Getting to grips with timezones in Django
David Seddon
david@seddonym.me
http://seddonym.me
1. Yes
2. No
3. It depends on the site
4. I don't really think about it
DateTimeField MyModel.my_datetime received a
naive datetime (2016-04-09 16:19:52.557550)
while time zone support is active.
1. Yes
2. No
3. I have no idea
4. None of the above
USE_TZ
Whether or not timezones
are enabled.
TIME_ZONE
The default time zone.
USE_TZ = True
TIME_ZONE = 'Europe/London'
USE_TZ is False by default, but True in projects
created using django-admin.py startproject.
12 April 2016, 7.30pm
A naive datetime
Naive datetimes do not represent moments in time (even if it looks like they do).
Instead, they are calendar events.
>>> from datetime import datetime
>>> naive_datetime = datetime.now()
>>> naive_datetime
datetime.datetime(2016, 4, 12, 19, 30, 24, 247648)
>>> from django.utils import timezone
>>> timezone.is_naive(naive_datetime)
True
12 April 2016, 7.30pm, London time
Some aware datetimes
Aware datetimes represent moments in time.
12 April 2016, 6.30pm, UTC
12 April 2016, 8.30pm, Paris time
>>> from django.utils import timezone
>>>
>>> aware_datetime = timezone.now()
>>> aware_datetime
datetime.datetime(2016, 4, 12, 19, 30, 24, 247648,
tzinfo=<UTC>)
>>> timezone.is_aware(aware_datetime)
True
# settings.py
USE_TZ = True
If USE_TZ is False, timezone.now() will return a naive datetime.
>>> from django.utils import timezone
>>> from datetime import datetime
>>> naive_datetime = datetime.now()
>>> naive_datetime
datetime.datetime(2016, 4, 12, 19, 30, 24, 247648)
>>> aware_datetime = timezone.make_aware(naive_datetime)
>>> aware_datetime
datetime.datetime(2016, 4, 12, 19, 30, 24, 247648,
tzinfo=<DstTzInfo 'Europe/London' BST+1:00:00 DST>)
make_aware() will use the current timezone by default,
make sure it's the correct one!
The default time zone is the time zone defined by the TIME_ZONE setting.
The current time zone is the time zone that’s used for rendering. This can be something that your application can set.
# my_app/views.py
import pytz
from django.utils import timezone
def my_view(request):
timezone.activate(pytz.timezone('Europe/London'))
# etc.
Dates and times are not datetimes.
They are calendar events, and are always naive in Django.
Don't combine DateFields and TimeFields, to depict datetimes.
April 12th 2016, 7.30pm
April 12th 2016, 7.30pm Europe/London
User submits datetime in form
Django assumes the datetime is in the current timezone
April 12th 2016, 6.30pm UTC
Django converts the datetime to UTC before storing in database
April 12th 2016, 6.30pm UTC
Django fetches the datetime from the database, as UTC
April 12th 2016, 7.30pm
Django renders the datetime in
the current timezone
Assuming we are in British Summertime,
and TIME_ZONE = 'Europe/London':
Answer: 7.30pm today.
Assuming we are in British Summertime,
and TIME_ZONE = 'Europe/London':
Answer: 8.30pm today.
Assuming we are in British Summertime,
and TIME_ZONE = 'Europe/London':
Answer: 7.30pm today.
Assuming we are in British Summertime,
and TIME_ZONE = 'Europe/London':
Answer: 6.30pm today.
Assuming we are in British Summertime,
and TIME_ZONE = 'Europe/London':
Answer: 6.30pm today.
You can't just change the USE_TZ setting. It will probably mess things up, because Django will assume that all preexisting datetimes are in UTC.
Be sure to write a migration that corrects the datetimes so they have the correct timezone.
USE_TZ = False | USE_TZ = True | |
---|---|---|
Naive datetimes stored in the database | As is | Assumed to be UTC, with a warning |
Aware datetimes stored in the database | Raises ValueError | Converted to UTC |
Datetimes retrieved from database | Naive datetime | Aware datetime in UTC |
Datetime form fields will assume | Naive datetime | Aware datetime in current timezone |
Naive datetime in template | As is | As is |
Aware datetime in template | As is | Converted to current timezone |
datetime.now() | Naive datetime | Naive datetime |
timezone.now() | Naive datetime | Aware datetime in UTC |
https://slides.com/davidseddon/about-time
David Seddon
david@seddonym.me
http://seddonym.me