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.
Related
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.
Using Jinja2, how do I format a date field? I know in Python I can simply do this:
print(car.date_of_manufacture.strftime('%Y-%m-%d'))
But how do I format the date in Jinja2?
There are two ways to do it. The direct approach would be to simply call (and print) the strftime() method in your template, for example
{{ car.date_of_manufacture.strftime('%Y-%m-%d') }}
Another, sightly better approach would be to define your own filter, e.g.:
from flask import Flask
import babel
app = Flask(__name__)
#app.template_filter()
def format_datetime(value, format='medium'):
if format == 'full':
format="EEEE, d. MMMM y 'at' HH:mm"
elif format == 'medium':
format="EE dd.MM.y HH:mm"
return babel.dates.format_datetime(value, format)
(This filter is based on babel for reasons regarding i18n, but you can use strftime too). The advantage of the filter is, that you can write
{{ car.date_of_manufacture|format_datetime }}
{{ car.date_of_manufacture|format_datetime('full') }}
which looks nicer and is more maintainable. Another common filter is also the "timedelta" filter, which evaluates to something like "written 8 minutes ago". You can use babel.dates.format_timedelta for that, and register it as filter similar to the datetime example given here.
Here's the filter that I ended up using for strftime in Jinja2 and Flask
#app.template_filter('strftime')
def _jinja2_filter_datetime(date, fmt=None):
date = dateutil.parser.parse(date)
native = date.replace(tzinfo=None)
format='%b %d, %Y'
return native.strftime(format)
And then you use the filter like so:
{{car.date_of_manufacture|strftime}}
I think you have to write your own filter for that. It's actually the example for custom filters in the documentation.
If you are dealing with a lower level time object (I often just use integers), and don't want to write a custom filter for whatever reason, an approach I use is to pass the strftime function into the template as a variable, where it can be called where you need it.
For example:
import time
context={
'now':int(time.time()),
'strftime':time.strftime } # Note there are no brackets () after strftime
# This means we are passing in a function,
# not the result of a function.
self.response.write(jinja2.render_template('sometemplate.html', **context))
Which can then be used within sometemplate.html:
<html>
<body>
<p>The time is {{ strftime('%H:%M%:%S',now) }}, and 5 seconds ago it was {{ strftime('%H:%M%:%S',now-5) }}.
</body>
</html>
You can use it like this in template without any filters
{{ car.date_of_manufacture.strftime('%Y-%m-%d') }}
Google App Engine users : If you're moving from Django to Jinja2, and looking to replace the date filter, note that the % formatting codes are different.
The strftime % codes are here: http://docs.python.org/2/library/datetime.html#strftime-and-strptime-behavior
You can use it like this in jinja template
{{ row.session_start_date_time.strftime('%d-%m-%Y %H:%M:%S')}}
In this code the field name is row.session_start_date_time.
in flask, with babel, I like to do this :
#app.template_filter('dt')
def _jinja2_filter_datetime(date, fmt=None):
if fmt:
return date.strftime(fmt)
else:
return date.strftime(gettext('%%m/%%d/%%Y'))
used in the template with {{mydatetimeobject|dt}}
so no with babel you can specify your various format in messages.po like this for instance :
#: app/views.py:36
#, python-format
msgid "%%m/%%d/%%Y"
msgstr "%%d/%%m/%%Y"
I use this filter, it's in Spanish but you may change the names as you need.
#app.template_filter('datetime')
def date_format(value):
months = ('Enero','Febrero',"Marzo","Abril","Mayo","Junio","Julio","Agosto","Septiembre","Octubre","Noviembre","Diciembre")
month = months[value.month-1]
hora = str(value.hour).zfill(2)
minutos = str(value.minute).zfill(2)
return "{} de {} del {} a las {}:{}hs".format(value.day, month, value.year, hora, minutos)
There is a jinja2 extension you can use just need pip install (https://github.com/hackebrot/jinja2-time)
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?
I have a model that includes a TimeField object. I also have a django template that lists the time field in JSON Format
e.g.
...
"time":"{{ mymodel.mytime }}",
...
Without specifying any formatting/filters etc, this results in the following e.g.
....
"time":"5 p.m.",
....
However when I apply a time filter to my template i.e.
...
"time":"{{ mymodel.mytime|time:"H:i" }}",
...
I get the following result:
...
"time":"",
...
i.e. my time value is wiped out. I am not sure what's going on? I also have a datefield in the same model and the filter I am applying to that (in the same django template) which works as defined.
I am using Django 1.4.2
What am I missing or not doing correctly?
You have put time format in double quotes too.
try:
"time":"{{ mymodel.mytime|time:'H:i' }}",
or
"time":'{{ mymodel.mytime|time:"H:i" }}',
or
"time":"{{ mymodel.mytime|time:\"H:i\" }}",
time
Formats a time according to the given format.
Given format can be the predefined one TIME_FORMAT, or a custom format, same as the date filter. Note that the predefined format is locale-dependent.
For example:
{{ value|time:"H:i" }}
For some bizzare reason, not sure what.. It has started working! Must have been a typo or something in the filter format?! Really weird.
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)