Dealing With Time Zones
Python 3.9 introduced major changes with respect to time zones by adding the zoneinfo
database. In this lesson, you’ll learn how to use the ZoneInfo
class to add time zone information to a datetime
object. You’ll also explore the complexities caused by inconsistent naming standards.
If you’d like to read more about the Christmas Island/Kiritimati example, you can check out Python 3.9: Cool New Features for You to Try.
00:00
In the previous lesson, I introduced you to the standard library’s datetime
module. In this lesson, I’ll be talking about time zones and how to deal with them. Python datetime
objects come in two varieties.
00:13
The first, which you’ve been using so far, is called naive. These are datetime
objects without any time zone data associated with them. Without specific arguments, most calls to datetime
constructors result in naive objects. By contrast, aware objects are those that have time zone information associated with them.
00:34 You need to be careful when doing work with dates and times to know which of these two objects you’re dealing with, and they often don’t mix and match nicely.
00:42
Most of the functions that create datetime
objects can be used to create both naive and aware objects. Until recently, most time zone work was done outside of the standard library. As of Python 3.9, the zoneinfo
module was introduced.
00:58
It was considered important enough for this feature to be in the language that backport packages were created. Even if you’re using something before Python 3.9, you can install one of those backport packages in order to use zoneinfo
.
01:12
Speaking of, let’s go into the REPL and play with some time zones. I’ll start out by demonstrating the difference between naive and aware datetime
objects. Let me import datetime
… and I’ll create an object using the .now()
constructor.
01:35
The .astimezone()
method converts a naive datetime
into an aware one. It does this using your local time zone. local_now
shows an additional argument from what you’ve seen before. That argument, tzinfo
, is a timezone
object, attached.
01:54 My local time zone is Eastern Standard Time, which is shortened to EST. There are some troublesome edge cases here. The IANA standard listing of time zones contains some short forms, but not all of the short forms. According to the IANA database, my time zone is America/Toronto, but my computer reports it as EST.
02:19 Depending on your operating system and your configuration, you may or may not get proper IANA time zones. EST is a short form recognized by the database, but I’ve seen claims that some locally understood short forms aren’t official and may even overlap with official names.
02:37
This is one of those standards that isn’t quite fully adopted but works well enough to lull you into complacency. The short version of all this: converting back and forth with the different names can be problematic. Back to local_now
.
02:55
I can get the .tzinfo
attribute and then use that …
03:04
with the .tzname()
method to get the string identifier of the time zone. Why would I want to do this? Well, this is another one of those edge case problems.
03:15
The library you should be using for time zone stuff is the relatively new zoneinfo
library, but as computers don’t always report their local time zone as an IANA standard, the folks at Python decided to not provide a way of determining the local time zone in the zoneinfo
module.
03:33
If you absolutely have to determine the zone without asking the user, you can cross your fingers that getting the label like I just did is enough to construct a zoneinfo
object. If you’re North American-based, you can probably get away with this.
03:47 If not, there may be some risk. The right way to do these things is to let the user specify their own time zones. In fact, when traveling, I may or may not change my local time zone, depending on how long I’m there and whether I want all my appointments to show in my home or not. Giving your user control is your best choice.
04:07 All right, enough of that. Let’s actually use a time zone or two.
04:15
The zoneinfo
module contains the ZoneInfo
class. You can construct new classes passing in IANA time zone names.
04:30
Here, I’ve constructed the time zone for jolly old London, England. Let me just grab a naive datetime
… and now I’ll create an aware datetime
by specifying the tz
argument to now
.
04:51
Examining that, and you can see two key differences. One, the aware object has a timezone
object embedded in it. And two, 11:00 a.m. has become 4:00 p.m.
05:02 as there is currently a five-hour difference between my location and London. You’ll also see the fact that it took me twenty-seven seconds between these two calls, which may or may not look like twenty-seven seconds by the time I’ve edited the video.
05:15
Looking at the underlying time zone name for the london_now
object … gives 'GMT'
, yet another short form. This is all rather annoying and just begs for the creation of a bug.
05:30
There is an older method for constructing UTC zones built into datetime
called utc_now
. Note that this is a naive datetime
object.
05:43
It shows the same time as GMT—Europe/London
is in UTC when not in daylight savings—but the object has no .timezone
attribute associated with it.
05:55
It is generally considered bad practice to use utc_now
. It is there for historic reasons. The better way would be
06:08
to create an aware datetime
object explicitly stating it is in UTC. This time around, I’ve got the UTC time along with the metadata about what zone it is in.
06:21
I believe I’ve mentioned that all this datetime
stuff can be messy. Let me show you two cases where time zones can be confusing. The timedelta
class allows you to construct differences in time to do some math with them.
06:39
The next lesson will dig into this further, but for now, I’m going to use it to make things just a tiny bit more readable. I’m going to create a timedelta
of one hour.
06:51
Now I’ll create an instance of the America/New_York
time zone.
07:00
The .utcoffset()
method on a zoneinfo
object will tell you what the time difference is between a given time zone and a datetime
object.
07:09
The result is returned in seconds, so I’ll do a bit of math using the timedelta
to make it more readable.
07:23
On Halloween 2006, the difference between New York and UTC was -5.0
hours. Remember when I mentioned that the candy companies got all meddlesome? Well, that was a 2005 law that took effect in 2007.
07:45
This is the same time the following year. The offset is now -4.0
. The good news is the zoneinfo
database knows when things change and allows you to properly reflect that in your code. The bad news is these things change, which can make dealing with something as simple as your trick or treating appointment potentially confusing.
08:07 For those who read the article or took the course a couple of years back on What’s New in Python 3.9, the next example is some repetition, but it’s a perfect example of messiness, and this course wouldn’t be complete without it.
08:20 There is a place in the Pacific called Christmas Island. Why is it named that? Because the second explorer to see it was the first explorer to name it and happened to be sailing by on Christmas Day. You got that right.
08:34 He didn’t even land and ask the locals, what did they call it? Let’s grab Christmas Island’s time zone …
08:46 and check its offset on December 30, 1994 …
08:57 and then two days later.
09:02 That’s quite the swing. You see, Christmas Island is near the international date line and a territory of Australia. They decided it would make everything much easier if there wasn’t a full day’s offset between them and the Australian mainland. At the tail end of 1994, they switched to the other side of the international date line, losing an entire day. I hope, for their sake, they had a New Year’s Eve Eve party that year instead. I’ll say it one more time.
09:31
This datetime
stuff is messy. Let’s update our BAKTUN
script with some time zone information. On line 5, I import the time zone that Chichen Itza, a key Mayan city, would be in if it weren’t just ruins today.
09:47
Lines 6 and 7 have been updated to include the time zone information in the BAKTUN
constant. Lines 9 and 10 now use a local instance of now
, and so our script is now time zone aware.
10:00 Let’s run this on the console.
10:06
And there it is. To see the difference, let me run the old one as well. There’s an hour’s difference between the naive implementation and the aware implementation. Chichen Itza is in Central Standard Time, which is one zone to the left of Eastern Standard Time. See, even I just did it there. All my life, they’ve been CST and EST. IANA standard or not, in conversation, that’s what they’re going to get called. Next up, I’ll dig some more into timedelta
and similar datetime
arithmetic.
Become a Member to join the conversation.