Using timezone-aware DateTime in Django
When working with models in Django with DateTimeField such as created_at
, updated_at
, and so on, we often come across the issue of supporting different timezones, especially when your intended audience is spread out in multiple timezones.
Quite often, during the database/backend design process, you will find yourself deciding on this topic by asking questions such as: "do we save timestamps in our own current local timezone, and then translate it to the user's timezone on the frontend?"
This is where Django's i18n (shorthand for "internationalization") support for timezone comes in handy.
Django stores datetime information in UTC in the database, uses timezone-aware datetime objects internally, and translates them to the end user’s time zone in templates and forms.
(Reference: https://docs.djangoproject.com/en/dev/topics/i18n/timezones/)
Setting timezone support in settings.py
In settings.py, we can enable support for timezones by setting USE_TZ
and providing which TIME_ZONE
this Django instance should use, for instance:
# settings.py
USE_TZ = True
TIME_ZONE = "America/New_York"
(Note: USE_TZ
is set to True and TIME_ZONE
"UTC" by default when you run django-admin startproject
.)
Django's time zone support uses pytz by default and a list of TIME_ZONE
values can be found by running:
import pytz
print(pytz.all_timezones)
# ["Africa/Abidjan", "Africa/Accra", ..., "Asia/Seoul", "UTC", ...]
"Naïve" DateTime objects
To create a datetime
object, we simply use the datetime.datetime.now()
, i.e.
from datetime import datetime
start_time = datetime.now()
While this is sufficient for most cases, the start_time
object here is considered as "naïve". When you attempt to store this naive timestamp to a model instance, Django will complain about timezone support with the following message:
RuntimeWarning: DateTimeField start_time received a naive datetime (2021-01-23 12:34:56) while time zone support is active.
Python's datetime
object has a property named tzinfo
(stands for timezone information), that provides an offset for the timezone from UTC (Coordinated Universal Time). For example, US Eastern Standard Time is UTC -05:00, and US Eastern Daylight Savings Time is UTC -04:00. (Note how the UTC "zero" is absolute and thus the local time change affects the offset instead).
"Aware" DateTime objects
A timezone-aware DateTime
object simply means that it has a tzinfo
attribute.
Django conveniently provides timezone support via its utils
module; i.e. to convert a DateTime
object from above snippet:
from datetime import datetime
from django.utils import timezone
start_time = datetime.now() # naive
aware_start_time = timezone.make_aware(datetime.now()) # aware