It's About Time
Getting to grips with timezones in Django
David Seddon
david@seddonym.me
http://seddonym.me
Do you prefer to have timezones enabled on your sites?
1. Yes
2. No
3. It depends on the site
4. I don't really think about it
Have you ever seen
this warning?
DateTimeField MyModel.my_datetime received a
naive datetime (2016-04-09 16:19:52.557550)
while time zone support is active.
In Django, is timezone support enabled by default?
1. Yes
2. No
3. I have no idea
4. None of the above
Timezones are something we would rather not
think about
This talk
-
Timezone settings
-
Naive versus aware datetimes
- Datetimes in models, forms
and templates
Part 1
Timezone settings
Two settings you should care about
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.
Part 2
Naive vs. aware datetimes
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.
Getting a naive datetime
>>> 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
Getting a aware datetime
>>> 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.
Making datetimes aware
>>> 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!
Current vs Default timezone
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
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.
Part 3
Datetimes in models, forms and templates
Timezone-aware Django
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
All you need to remember
- User inputted datetimes are assumed to be in the current timezone.
- Datetimes are stored in the database in UTC.
- Datetimes are rendered in templates in the current timezone.
A quiz
Assuming we are in British Summertime,
and TIME_ZONE = 'Europe/London':
Question One
- With timezones disabled, save to a model field a datetime of 7.30pm today.
- Now, render the model field to a template. What do you see?
Answer: 7.30pm today.
Assuming we are in British Summertime,
and TIME_ZONE = 'Europe/London':
Question Two
- Now, enable timezone support.
- Render the datetime again. What do
you see?
Answer: 8.30pm today.
Assuming we are in British Summertime,
and TIME_ZONE = 'Europe/London':
Question Three
- Resave the model with 7.30pm today, London time.
- Render the datetime again. What do
you see?
Answer: 7.30pm today.
Assuming we are in British Summertime,
and TIME_ZONE = 'Europe/London':
Question Four
- Resave the model with 7.30pm today, Paris time.
- Render the datetime again. What do
you see?
Answer: 6.30pm today.
Assuming we are in British Summertime,
and TIME_ZONE = 'Europe/London':
Question Five
- Save the model with 7.30pm today, London time.
- Turn off time zone support, and reload the model.
- Render the datetime again. What do
you see?
Answer: 6.30pm today.
Migrating to a timezone aware site
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.
Putting it all together
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 |
Some closing remarks
- Use pytz
- Always enable timezone handling
- Don't enable/disable timezones without migrating your datetimes
- Use timezone.now(), never datetime.now()
- Use django.timezone.utils for helpful utilities.
https://slides.com/davidseddon/about-time
David Seddon
david@seddonym.me
http://seddonym.me
It's About Time
By David Seddon
It's About Time
Getting to Grips with Timezones in Django
- 2,657