How to Convert UTC Time To User's Local Time in Django - django

I undrestand that this question is repeated, but unfortunately I cannot find any answer.
My impression is that Django already takes care of converting server time to local user as long as I have
TIME_ZONE = 'UTC'
USE_TZ = True
in my settings.
Even more if the db is postgresql that setting also doesn't matter and every thing will be still converted.
however I tried all the followings:
{% load tz %}
{{ obj.date }}
{{ obj.date|localtime }}
{{ obj.date | timezone:"Canada/Mountain" }}
and only last one works and the rest gives me UTC time. Last one also is not useful as the time would be only correct for users in that zone.
I was wondering if I am missing anything here.
I have a very simple test model:
class TimeObject(models.Model):
date = models.DateTimeField(auto_now=True)

My impression is that Django already takes care of converting server time to local
Unfortunately that is incorrect.
As discussed here, there's no automatic way of knowing what the user's timezone is. You need to figure that out in some other way (it could be saved as a user setting, for example). Then you need to activate() that timezone. Once you do, Django will perform the conversion.

Related

Getting the current date in a particular timezone django

I want to get the current date for a specific timezone in my Django app, irrespective of the server's timezone. I save the user's timezone in the database. I'll then use that in the following function:
def current_date(zone):
utc = timezone.now()
tz = pytz.timezone(zone)
return utc.astimezone(tz).date()
print(current_date('Pacific/Auckland')) #prints 2016-05-30
print(current_date('Africa/Accra')) #prints 2016-05-29
It seems to work, but working with timezones seems complex and I'm wondering if something can go wrong with this approach?
It looks fine as long as getting to the value in view itself is what you want. Since the date/time print is an aspect of presentation you probably may not want to do it in the view code rather in the template using something like below:
{% load tz %}
{{ object.datetime_field|timezone:request.user.timezone }}
assuming you are storing the user's timezone selection in the user model.

Django datetime localization

I want to output a datetime object (with timezone info set to UTC) in a Django template. I have I18N, L10N, and TZ support all on in the settings. I've also added the LocaleMiddleware and set the language settings. Settings also contains my local timezone as the default. If I simply do {{ mydatetime }} in the template than it properly gives the information in the local timezone. However {{ mydatetime|localize }} seems to always output the datetime in UTC.
Now, I know you're supposed to call django.utils.timezone.activate() to report the current user's timezone (which I'm not doing), but the docs seem to indicate that if you don't use that to report the user's timezone that it uses the default.
Also, does the localize filter get used on datetimes automatically? Is the only purpose of having an explicit call is for when you use {% localize off %} and you want a one-off localization made?

django 1.6 with django_tables2 cannot change DATETIME_FORMAT

python 2.7.5+. django_tables2 0.14.0
In settings.py:
USE_L10N = True
DATETIME_FORMAT = 'M j, Y H:i:s.u'
Didn't work. I tried {% load L10N %} at the top of my template. Didn't work.
I created a formats directory at the top of my project containing an en directory containing formats.py and added the following to my settings.py (yes, there are init.py files in the formats directory and in the en directory):
FORMAT_MODULE_PATH = '<project>.formats'
Didn't work.
I tried putting in {{created_at|date: 'M j, Y'}}{{created_at|time: 'H:i:s.u'}} before the {% render_table table %} tag from django_tables2. Got an error.
Tried the recommended django_tables2 method of, in my tables.py definition, adding to the Meta class:
localize('created_at')
Didn't work.
Tried putting 'localize=True' in the column definition for created_at in tables.py.
Didn't work.
The only thing that worked was modifying python2.7/site-packages/django/conf/locale/en/formats.py (in my virtual environment) but I won't be allowed to do that on my project because it's not considered portable.
I see stackoverflow entries that say this works but they are many years old and many django versions back. I see others here that say this doesn't work and they haven't/aren't going to fix it.
This functionality logs requests; during testing the other devs could really use seeing seconds and probably microseconds to help them debug but I can't seem to give it to them.
Any suggestions that I haven't already tried?
Thanks.
OK, blush - I got it. I made the following change in the model for the DateTimeField created_at:
#property
def nice_date(self):
# mm/dd/yyyy HH:mm:ss.uuuuuu
t = self.created_at.strftime("%m/%d/%Y %H:%M:%S.%f")
return t
Then, I displayed nice_date instead of created_at.
I hope this helps someone else.

Handling per-object timezone settings in Django

I have a Django application in which a user can create an Activity, and then for an Activity create a number of Log entries. An Activity has a created datetime, and a Log entry also has a created datetime.
The Activity should be created with the timezone of the creating user at the time at which they create it. Specifically, a given user may have two Activities in separate timezones; the timezone setting is not per-user, but per-activity. I can derive a user's current offset from UTC in JavaScript in the browser (and I'm happy to rely on that).
What I do not know is: how do I save these times to the database, and then display them correctly on rendering?
I have USE_TZ turned on. Here in the UK, everything works: when creating an Activity, I save the created datetime as django.utils.timezone.now(), and I save the user's current offset (from JavaScript) as timezoneHoursOffset in the Activity. Then, when I display the Activity and its Log entries on a page, I wrap this in {% timezone "Etc/GMT+(timezoneHoursOffset)" %} ... {% endtimezone %}.
However, this approach doesn't work if the server's not in the UK. The time is saved in the database as UTC (I've verified this by checking in the database itself, not using the ORM), that time is then "changed" by the ORM to be eight hours behind UTC (because the server's timezone is UTC-8) (and that gives the correct time!) and then the {% timezone %} tag subtracts another eight hours off the time, meaning that the displayed time is eight hours behind what it should be.
I'm not sure whether I should be saving the times differently; changing them on output; using the timezone template tag; or something else. This stuff is very confusing. How would you advise that I handle this situation?
Ref the answer, "Etc/GMT" timezones have their sign reversed. For example, I'm in +0800, it should be 'Etc/GMT-8' instead of 'Etc/GMT+8' (and I suspect that your UTC-8 actually should be "Etc/GMT+8" ):
>>> from django.template import Template, Context
>>> from django.utils.timezone import now
>>> print(Template("""{% load tz %}
localtime: {{ t }}
Etc/GMT-8: {{ t|timezone:"Etc/GMT-8" }}
Etc/GMT+8: {{ t|timezone:"Etc/GMT+8" }}
""").render(Context({'t':now()})))
localtime: Jan. 29, 2013, 11:48 p.m.
Etc/GMT-8: Jan. 29, 2013, 11:48 p.m.
Etc/GMT+8: Jan. 29, 2013, 7:48 a.m.
So, you have to reverse your timezoneHoursOffset...
For DB storing, according to the doc, it's better to store data in UTC.
Also, you need to make sure your javascript generates correct hour offset because of DST. If you can, it's better to store timezone name and use it accordingly.
from datetime import datetime
from dateutil import zoneinfo
from_zone = zoneinfo.gettz('UTC')
to_zone = zoneinfo.gettz('UK/UK')
utc = created # your datetime object from the db
# Tell the datetime object that it's in UTC time zone since
# datetime objects are 'naive' by default
utc = utc.replace(tzinfo=from_zone)
# Convert time zone
eastern_time = utc.aztimezone(to_zone)

Django - Pre render signal?

I have a 'live settings' app which I use to store certain global site settings. Certain pages of my site reference these settings, although generally they only use 2-3 at a time. I access these settings using a custom template tag like so:
{% settings site_name %}
Each time I use this tag, the model will retrieve the setting from the database, so if I had something like:
{% settings site_name %} {% settings num_articles %}
the database would get queried twice.
What I'd like to do is not hit the database more than once per page for the settings values, if I could get all the settings used on a page and then hit the database to retrieve them all, that would be much better.
I had a look through the documentation and it seems Django has no pre render signal which I could use to update any references to the settings, so does anyone have any suggestions how I could optimise this system? And yes obviously caching is the obvious answer but I'd still like to improve on this for when the page isn't cached.
Agreeing with Daniel that process_template_response is the hook you're looking for. I don't know how many settings you have, but it could also make sense to retrieve all settings once, and store them in some dictionary like structure, so you can access them anytime without hitting the database. You could retrieve them once either per request, or - even if your settings do barely change - when django initializes. You could additionally use django's signals to update your cached settings on delete or update.
If you have a look at django-dbsettings you will see it does a similar thing!
Django 1.3 includes the new TemplateResponse class, which allows you to modify the response later in the rendering process - eg in middleware. This looks like it might do what you want.
If you wish to stick to a scheme similar to what you already have, I'd implement a filter that takes in a list of keys and returns a dict with all the relevant values.
Example filter:
def get_settings(key_list):
# take values from conf.settings. In practice, can be from any source
return dict((k, getattr(settings, k, None)) for k in key_list.split(","))
register.filter("get_settings", get_settings)
Usage:
{# load multiple values with a single call #}
{% with "KEY1,KEY2"|get_settings as settings %}
setting values {{ settings.KEY1 }} and {{ settings.KEY2 }}
{% endwith %}
Note that I used a filter instead of a tag because the with builtin expects a single value before as.