Making sense of timezones in Python
Avoiding common pitfalls when dealing with timezones
Running into timezone trouble in your Python project? Here’s a quick primer on how timezones work and how to convert between them (mostly because I usually forget how to do this myself).
We’ll be using the popular pytz
package, so make sure it’s installed.
🙃 Naive and aware datetimes
A naive datetime
doesn’t have a timezone associated with it. An aware one does.
from datetime import datetime
from pytz import timezone# Set the time to noon on 2019-08-19
naive = datetime(2019, 08, 19, 12, 00)# Let's treat this time as being in the UTC timezone
aware = timezone('UTC').localize(naive)
pytz.localize()
is useful for making a naive timezone aware. I find that it’s useful when a front-end client sends a datetime to the backend to be treated as a particular timezone (usually UTC).
🎛 Converting between timezones
💡 It’s best practice to store all datetimes in your project as UTC. Most ORMs, including SQLAlchemy and Django will store aware datetimes in UTC. This ensures you almost never have to do conversions between timezones. It’s best to leave the rendering of timezones to the front-end…
To render an aware datetime (perhaps from a record in a database) in another timezone we can use the pytz.astimezone()
method:
from datetime import datetime
from pytz import timezone# Set the time to noon on 2019-08-19
naive = datetime(2019, 08, 19, 12, 00)UTC = timezone('UTC')
NYC = timezone('America/New_York')aware = UTC.localize(naive)# UTC is 5 hours ahead of New York, let's verify
time_in_new_york = aware.astimezone(NYC)print(time_in_new_york)
>>> 2019-08-19 08:00:00-04:00
🐍 Common Pitfalls
Avoid using datetime.replace(tzinfo=…) to make timezones aware
It will only work with UTC and the behavior is inconsistent with other timezones. Rather use localize()
as above.
Getting the correct UTC time
from datetime import datetime
from pytz import UTC# Naive UTC
naive = datetime.utcnow()# Aware UTC (if you're about to render it in another timezone)
aware = UTC.localize(datetime.utcnow())
Using datetime.now()
just returns your system’s current wall-clock time.