pytz for timezones
A timezone is a region where the same standard time is used.
For example, a lot of Western Europe uses the same standard time—Spain, France, Italy, Germany, etc. They are all in the "Central European Time" timezone. In Summer, when daylight savings are in effect, they are all in the "Central European Summer Time" timezone.
If you are running Python in a computer that is using CET, then doing
datetime.datetime.now() would give you the local time of the computer.
However if you grab a different computer that is using Pacific Standard Time, then
datetime.datetime.now() would give you a different time.
That is why it is important to tell the
datetime objects what timezone the time they represent is in, so that no matter what computer is running the code, the time represented will always be the same.
The "central" time is called Universal Time Coordinated (UTC). All other timezones can be described by using UTC as a reference. For example, CET is UTC + 1 hour. PST is UTC - 6 hours.
We would thus write CET as
+01:00, and PST as
We call that the "offset", and it is usually placed after a date and time. For example:
The above time is 11:54 with an offset of 1 hour.
That means that that timezone might be CET (or any other timezone with a +1 hour offset from UTC).
We can calculate the UTC time by subtracting 1 hour from the time, which would put us at 10:54.
# Getting the current date and time in UTC
We've learned that you can get the local time like so:
import datetime today = datetime.datetime.now() print(today) # 2019-12-23 15:35:56.133138
When doing this you can ask Python to get you the current time, but translated to a different timezone. Below we pass
.now() so that Python will give us the current time in the UTC timezone.
import datetime today = datetime.datetime.now(datetime.timezone.utc) print(today) # 2019-12-23 15:35:56.133138+00:00
You can see that in my case, the time is the same. That's because, at the moment, my timezone also has an offset of +00:00, so it matches UTC.
If your timezone does not match UTC, you'll see those two times are different (indeed, the difference will be your timezone's offset from UTC).
Note that when we did
.now() without passing a timezone, our printed string did not contain an offset. But when we did
.now(datetime.timezone.utc), the printed string contained the
# Naive vs. aware datetimes
datetime object knows what timezone the date and time it represents is in, then we call that an aware
datetime object. If it doesn't have timezone information, we call that a naive object.
Aware objects represent specific points in time in specific places in the world. Naive objects only represent specific points in time.
Note that working with naive objects can be dangerous because different pieces of code may interpret the naive object to be using a different timezone. For example, some of your code might use a naive object as if it were in UTC. Some other code might use a naive object as if it were in the current, local timezone.
Therefore it's almost always a good idea to store timezone information in your
# Converting from one timezone to another using pytz
You can convert from one timezone to another using the built-in
datetime module, but you'll need to tell Python what timezones exist and can be used by writing a class for each timezone that subclasses the
datetime.tzinfo class. You'll also have to do a lot of manual work so that your timezone classes keep track of daylight savings.
Alternatively you can use (and probably should use) the
pytz module for working with timezones in your Python code.
First of all, install
pytz by following their installation instructions (opens new window) or using
pip install pytz.
# Getting a timezone from pytz
pytz module comes with a whole database of timezones, accessible by their official names (opens new window):
import pytz eastern = pytz.timezone("US/Eastern") amsterdam = pytz.timezone("Europe/Amsterdam")
# Adding timezone information to a native datetime
If you have a naive
datetime object (from the built-in module), you can use
pytz to add timezone information:
import datetime import pytz eastern = pytz.timezone("US/Eastern") local_time = datetime.datetime.now() print(local_time) # 2020-02-12 11:47:21.817708 eastern_time = eastern.localize(local_time) print(eastern_time) # 2020-02-12 11:47:21.817708-05:00
Note that doing this does not change the time information in the
# Converting from one timezone to another
If you have an aware
datetime object and you want to translate it to another timezone, then you can also do this with
import datetime import pytz eastern = pytz.timezone("US/Eastern") amsterdam = pytz.timezone("Europe/Amsterdam") local_time = datetime.datetime.now() eastern_time = eastern.localize(local_time) print(eastern_time) # 2020-02-12 11:47:21.817708-05:00 amsterdam_time = eastern_time.astimezone(amsterdam) print(amsterdam_time) # 2020-02-12 17:47:21.817708+01:00
Now the displayed time has changed because it's the same date and time, but in a different timezone. Both
amsterdam_time refer to the same exact point in time.
You can make sure of that by converting each to UTC yourself. Add 5 hours to
eastern_time and subtract 1 hour from
amsterdam_time to reach a +00:00 offset. You'll see the times are identical.
# Dealing with daylight savings
pytz module deals with daylight savings with you. For example the
US/Eastern timezone may sometimes be at
-05:00 and other times at
-04:00, depending on the time of year.
# Getting current local time from users
The way to construct a current local time is to:
import datetime import pytz local_time = datetime.datetime.now() utc_local_time = datetime.datetime.now(tz=pytz.utc) print(local_time) # datetime.datetime(2020, 4, 26, 11, 52, 56, 47126) print(utc_local_time) # datetime.datetime(2020, 4, 26, 10, 52, 56, 47126, tzinfo=<UTC>)
Note that when using
.now() together with
tz=pytz.utc, the system is able to determine that the time right now if we were in the UTC time zone would be 10:52, and not 11:52 (which is the local time with the current timezone).
# Working with dates and times in software applications
I would recommend to always work with UTC everywhere in your applications:
- Ask users for a local time and their timezone.
- Use their timezone to localize the local time.
- Convert to UTC before saving to the database.
Then, always store UTC in your database. Only change back to the user's local time when you are displaying a date and time to them (and only if your users want to see times in their local time).
That way you will not have to worry about timezones within your application logic; only when dealing with user interaction.
import datetime import pytz eastern = pytz.timezone("US/Eastern") user_date = eastern.localize(datetime.datetime(2020, 4, 15, 6, 0, 0)) utc_date = user_date.astimezone(pytz.utc) print(utc_date) # 2020-04-15 10:00:00+00:00
As long as you always work with UTC, and only convert to the user's local timezone when you are displaying information, it will be relatively simple!