5:30am • Timezone Headaches (Part 1)
I need Python's help to figure out the time of my talk • Dealing with timezones and daylight saving with Python's `zoneinfo` and `datetime` modules • The first article in a two-part mini-series
I've been wanting to use Textual for a while. Textual is a framework to build TUIs—Text User Interfaces. So, when I saw that my friend and colleague Rodrigo* was delivering a Textual workshop, I signed up.
The talk was in New Zealand. But this is 2023, so Rodrigo gave the talk from Portugal, and I could attend from the UK.
The talk was at 6:30pm NZST, which stands for New Zealand Standard Time. NZST is UTC+12. UTC is Coordinated Universal Time—yes, I know, the acronym doesn't match. So UTC+12 is 12 hours ahead of UTC. UTC is the same time as GMT. GMT stands for Greenwich Mean Time. I hope you're paying attention. Stay sharp! And GMT is the UK's timezone, right? Wrong. The UK is observing daylight saving. So the UK's timezone is BST, that's British Summer Time, which is GMT+1. And Rodrigo was in Portugal, so he was in the WEST timezone, which is Western European Summer Time, which is also GMT+1. That's the same as BST.
Is that clear?
So, it was 6:30pm for the hosts in New Zealand, which is 7:30am for Rodrigo and also 7:30am for me. And that's perfect for me. It's the time I normally start my working day.
*I hope you're already subscribed to Rodrigo's weekly newsletter…
And then, I was asked if I wanted to give a talk to the same group on another day. So I said yes. 7:30am is a good time for me. But wait a second, the clocks were about to move in New Zealand, which is not the same time as the clock change in the UK. Would that be an hour later or earlier? I never know. But my talk was scheduled for mid-November, and by that time, the UK would have changed clocks, too, so all is back on track then, right? My talk is at 7:30am.
But there's one minor issue I overlooked. It isn't. New Zealand and the UK are in different hemispheres, duh! So my talk is at 5:30am. Oops! I'd better make sure I don't run out of coffee that day.
I find timezones confusing. Especially when planning for a future date with different switch-over dates for daylight saving. Luckily, it only takes a few lines of Python code to resolve the problem.
Using zoneinfo
And datetime
to Deal With Timezones
Before Python version 3.9, you had to use a third-party module, pytz
, to deal with timezones. However, Python 3.9 introduced zoneinfo
to the standard library. Using zoneinfo
is now the preferred way to deal with timezones.
Let's see how it works. You can start seeing what timezones are available:
You will likely see the timezones shown in a different order. The zoneinfo
module doesn't contain the timezone information within it. Instead, it uses the timezones available on your system. Not all systems include the timezone database. Apparently, most Windows systems don't. However, I cannot verify this because I'm using a Mac! If the code above doesn't return timezones, you can install the tzdata
package using the following pip
command in your terminal:
$ pip install tzdata
Or you can use your package manager of choice instead of pip
!
Most timezone identifiers include the continent or region and the city separated by a forward slash. For example, my timezone is "Europe/London"
.
Next, you can create an instance of ZoneInfo
, which is a class within the zoneinfo
module:
You'll see where you'll need this object soon.
The timezone identifier, such as "Europe/London"
, denotes a location. For many locations, the actual timezone can change depending on the time of the year because of daylight saving. So, "Europe/London"
represents either GMT or BST, depending on the time of year. And the database takes into account historical changes to timezones, too.
Let's create a date and time using the datetime
module, and then we'll make it aware of the timezone. First, let's create a naïve datetime.datetime
object. This means the object is not aware of the timezone:
This object shows the year, month, day, hour, minutes, seconds, and even the microseconds when I ran this code. But this could be just before 10pm anywhere in the world. This datetime.datetime
object is not aware of the timezone.
Let's fix this:
I'm showing the output of the datetime.datetime
object as a multi-line output for display purposes.
Currently, the UK is observing daylight saving and is one hour ahead of GMT. Recall that GMT is the same time as UTC. You can confirm this by finding the offset from UTC:
The datetime.datetime
object representing the time now is 3,600 seconds from UTC. That's one hour!
Let's look at a date when there's no daylight saving, such as New Year's Day:
If you're not familiar with the datetime
module, the strptime()
function, and the format codes, such as "%d/%m/%Y"
, you can read Chapter 9 | Dealing With Dates and Times in Python in The Python Coding Book.
The datetime.datetime
object returned by strptime()
is naïve. However, we can replace the tzinfo
argument to change it to the London timezone you created earlier. In this case, there's no offset from UTC since London is in the GMT/UTC timezone on New Year's Day.
Working Out The Time of My New Zealand Talk
Let me start with Rodrigo's workshop, which was on the 20th of September at 6:30pm New Zealand time. Let's convert this to the time in the UK on that day. I know London's timezone identifier, but I'm not sure what's the one for Auckland. But that's fine. You can fetch them from the set of all timezones:
The workshop was scheduled at 6:30pm on the 20th of September in Auckland:
And now you can convert to London time:
This confirms that the workshop is at 7:30am in the UK. I'm using the datetime format codes in an f-string in the last expression.
I did attend that workshop. It was brilliant!
But Rodrigo is returning to Auckland (virtually) for a follow-up workshop to discuss more advanced uses of Textual. It's scheduled for the 4th of October, also at 6:30pm Auckland time:
I combined the calls to strptime()
and replace()
in one expression this time. The workshop is at the same time in Auckland but an hour earlier in the UK. This is because the clocks change in New Zealand but not in the UK between the two workshops. You can check the UTC offset for the two dates in both timezones:
There's an extra offset of one hour in New Zealand for the second workshop, but not in London.
But my talk is on the 15th of November at the same New Zealand time of 6:30pm:
And there you have it. I'll have to wake up very early on the 15th of November.
End of Part 1 • Preview of Part 2?
This works perfectly fine for working out what the time will be in some other part of the world, any time in the past, present, or future. And the code is short enough for me to write it out from scratch each time I need to avoid a timezone headache.
But how about making something that's easily reusable and more user-friendly? I'll explore this idea in the second and final article in this two-part 5:30am Series.
Coming soon… 5:30am • Timezone Headaches (Part 2)
Code in this article uses Python 3.11
Stop Stack
#31
If you want to attend my 5:30am talk on the 15th of November, you can sign up here: https://www.meetup.com/nzpug-auckland/. And you have no excuses to figure out what the time will be in whichever part of the world you live in!
Recently published articles on The Python Coding Stack:
A Slicing Story Are you sure you know everything there is to know about Python's
slice
object?Coding, Fast and Slow, Just Like Chess An essay: How adapting from fast to slow chess got me thinking about coding in Python
Butter Berries, An Elusive Delicacy (Paid article) How my quest to find butter berries at the supermarket led to musings about Python lists and dictionaries and more
Pay As You Go • Generate Data Using Generators (Data Structure Categories #7) Generators • Part 7 of the Data Structure Categories Series
Clearing The Deque—Tidying My Daughter's Soft Toys • A Python Picture Story Exploring Python's
deque
data structure through a picture story. [It's pronounced "deck"]
Recently published articles on Breaking the Rules, my other substack about narrative technical writing:
The Unexpected Audience (Ep. 11). What I'm learning from listening to Feynman's physics lectures
The Story So Far (Mid-Season* Review). Are you back from your holidays? Catch up with what you've missed
Broken Rules (Ep. 10). Let's not lose sight of why it's good to break the rules—sometimes
Frame It • Part 2 (Ep. 9). Why and when to use story-framing
The Rhythm of Your Words (Ep. 8). Can you control your audience's pace and rhythm when they read your article?
Stats on the Stack
Age: 5 months, 2 weeks, and 4 days old
Number of articles: 32
Subscribers: 1,079
Each article is the result of years of experience and many hours of work. Hope you enjoy each one and find them useful. If you're in a position to do so, you can support this Substack further with a paid subscription. In addition to supporting this work, you'll get access to the full archive of articles and some paid-only articles.
I imagine you get to this in part 2 but I just wanted to state that datetime values should always be generated for storage in UTC, and then converted to local times only for ephemeral display purposes, if and when necessary. For browser based systems the UTC value should be delivered intact to the browser and then converted to the locale of the OS they are displayed on. For server apps, log files and everything else, UTC should be the only timezone. I can't tell you how many times I have had to guess what timezone different log files were generated in.
I would argue that naive datetimes shouldn't even exist since they are essentially indeterminate values in any global system.