Article Overview: In a previous post, we discussed the
datetime module in Python and used it to iterate through a range of dates. Today, let’s learn how to manage timezones in Python using the
datetime module and a third-party package called
dateutil. I aim to choose a range of world cities and print the local time in each of those cities. I will also print my local time and UTC, or Coordinated Universal Time. If you’ve not used the
datetime module before, I suggest a quick review of this post before continuing.
The Python datetime module and dateutil package
The Python datetime module dramatically eases the coding complexities when dealing with timezones and leap years. The three most common classes of the datetime module are:
This class uses the Gregorian calendar, which started in 1582 following a slight adjustment to the Julian calendar. However,
datetime.date() assumes the Gregorian calendar extends infinitely into the past and the future, and it takes the attributes, year, month, and day.
This class is independent of any particular day and assumes that a day consists of 24 x 60 x 60 seconds and ignores leap seconds. It accepts attributes, hour, minute, second, microsecond, and time zone information, abbreviated to
This class is a combination of the two classes above and utilises the same attributes of both.
Today we’ll be using only the
datetime() class from the
However, we will introduce some greater functionality with a package called
dateutil, a third-party package that adds power and utility to the standard
datetime module in Python. If you don’t have
dateutil installed, you can do so with the following command:
pip install python-dateutil
Dateutil has several modules; however, today, we’ll use the
tz module, allowing timezone implementation by using information from the
datetime.tzinfo() class. If you wish to know more about the
dateutil package, you can find it here.
Are your datetimes aware or naive?
When we speak of someone being naive in English, we usually mean they lack sophistication, are simple, or innocent. In Python, we distinguish between dates and times as naive or aware, meaning that a naive date or time instance does not contain timezone information, whilst an aware one does.
The Python documentation states that an aware date or time instance is a specific moment in time not open to interpretation, and it can locate itself in relation to other aware date or time objects. If I did arithmetic with aware dates or times from different timezones, Python would deliver the correct time interval. If I tried it with a naive object, it would return the wrong information.
For this reason, we use
datetime objects which contain an attribute called
tzinfo, or timezone information, that creates awareness. Tzinfo captures information about the offset from UTC, the timezone names, and whether daylight saving time is in force.
Let’s write some code!
We’ll begin with importing the
datetime module, and then the
tz module from the
dateutil package. As we code, I’ll introduce, use, and explain a few classes found within the
tz module. Then we’ll find the current UTC, or time at zero degrees longitude.
from datetime import datetime as dt from dateutil.tz import * print(dt.now(tzlocal())) # Result # 2021-05-13 16:51:04.250732+01:00
In this code, we imported the
datetime class from the
datetime module and we gave it an alias of
dt just to save me writing the class out longhand each time. We then used the asterisk notation to import all classes of the
tz module which forms part of the
I called the
now() method which forms part of the
datetime module. The
now() method returns the current date and time of the computer system during the execution of the now statement. However, we passed to the
now method the method
tzlocal() which is in the
tz module and it returns the timezone information, or
tzinfo. This gives access to the timezone and daylight saving information needed to make the
datetime object aware. You can see the returned value is 16:51 on the afternoon of 13 May 2021, and the timezone is +1 hour from UTC. Now we’ll see what the time is at zero degrees longitude by calling for the current UTC.
from datetime import datetime as dt from dateutil.tz import * print(dt.now(tzlocal()), '\n') print(dt.now(tzutc())) # Result # 2021-05-13 17:01:16.626317+01:00 # 2021-05-13 16:01:16.627316+00:00
Here we’ve introduced another method from the dateutil
tz module, which is called
tzutc(). As you’d expect, it returns an aware
datetime instance in UTC of
datetime.now(). So you see my time in London, which is 17:01 GMT Summer Time, and the time in UTC, which is 16:01 UTC.
Success – but not that readable
We’ve achieved a result, but it’s not that user friendly. I’m going to format the output from these two lines of code to give the timezone, and we’ll then format the output to be more readable.
from datetime import datetime as dt from dateutil.tz import * local = dt.now(tzlocal()).tzname() print('London: ', dt.now(tzlocal()), local, '\n') utc = dt.now(tzutc()).tzname() print('UTC: ', '\t', dt.now(tzutc()), utc) # Result # London: 2021-05-13 17:22:29.135941+01:00 GMT Summer Time # UTC: 2021-05-13 16:22:29.135941+00:00 UTC
There are a few things happening in this last bit of code. Let’s unpick it. I’ve introduced a new method from the dateutils
tz module, called
tzname(). This method returns the timezone name from the
datetime instances for my time and UTC time, and I’ve passed it to two variables, local and utc respectively. Then in the print line, I’ve simply called the variable after the
datetime instance to print out the timezone info. Of course, I’ve put in the name of the city or zone upfront as a string to enhance readability.
So readability is improving, but we can do more. Let’s format the output of the
datetime instance to be more human friendly.
from datetime import datetime as dt from dateutil.tz import * local = dt.now(tzlocal()).tzname() lon = dt.now(tzlocal()) print('London: ', lon.strftime('%A %d %b %Y %H:%M hrs'), local, '\n') utc = dt.now(tzutc()).tzname() base = dt.now(tzutc()) print('UTC: ', '\t', base.strftime('%A %d %b %Y %H:%M hrs'), utc) # Result # London: Thursday 13 May 2021 17:38 hrs GMT Summer Time # UTC: Thursday 13 May 2021 16:38 hrs UTC
Now that’s a vast improvement. In this code, I introduced the
strftime() method from the
datetime module. This method does a string format on the
datetime instance, hence the name.
I created a
datetime instance for each location and passed it to variables
base, respectively. I then called the
datetime instance using
strftime() and used specific codes in a string to format the data to be returned. For the complete list of codes, go to this site. To explain the ones we’ve used, here’s a list:
|%A||Weekday as locale’s full name.||Thursday|
|%d||Day of the month as a zero-padded decimal number.||13|
|%B||Month as locale’s full name.||May|
|%Y||Year with century as a decimal number.||2021|
|%H||Hour (24-hour clock) as a zero-padded decimal number.||16|
|%-M||Minute as a decimal number. (Platform specific)||38|
Now we have readable format giving all the information we need, we can add in those other cities in which we wish to know the time. Here’s the final bit of code with some reconfiguration and some new methods.
from datetime import datetime as dt from dateutil.tz import * utc = dt.now(tzutc()).tzname() base = dt.now(tzutc()) print('UTC: ', '\t\t\t\t', base.strftime('%A %d %b %Y %H:%M hrs'), utc, '\n') print('East of UTC', '\n') local = dt.now(tzlocal()).tzname() lon = dt.now(tzlocal()) print('London: ', '\t\t\t', lon.strftime('%A %d %b %Y %H:%M hrs'), local) jburg = dt.now(tzoffset('SAST', 7200)) sa = dt.tzname(jburg) print('Johannesburg: ', '\t\t', jburg.strftime('%A %d %b %Y %H:%M hrs'), sa) tokyo = dt.now(tzoffset('JST', 32400)) jp = dt.tzname(tokyo) print('Tokyo: ', '\t\t\t', tokyo.strftime('%A %d %b %Y %H:%M hrs'), jp) kiri = dt.now(tzoffset('LINT', 50400)) ci = dt.tzname(kiri) print('Kiribati: ', '\t\t\t', kiri.strftime('%A %d %b %Y %H:%M hrs'), ci, '\n') print('West of UTC', '\n') wash_dc = dt.now(tzoffset('EDT', -14400)) us = dt.tzname(wash_dc) print('Panama: ', '\t\t\t', wash_dc.strftime('%A %d %b %Y %H:%M hrs'), us) pana = dt.now(tzoffset('PAB', -18000)) pan = dt.tzname(pana) print('Washington DC: ', '\t', pana.strftime('%A %d %b %Y %H:%M hrs'), pan) ckt = dt.now(tzoffset('CKT', -36000)) rar = dt.tzname(ckt) print('Rarotonga: ', '\t\t', ckt.strftime('%A %d %b %Y %H:%M hrs'), rar) alo = dt.now(tzoffset('NUT', -39600)) nut = dt.tzname(alo) print('Niue: ', '\t\t\t\t', alo.strftime('%A %d %b %Y %H:%M hrs'), nut)
# Result UTC: Friday 14 May 2021 10:34 hrs UTC East of UTC London: Friday 14 May 2021 11:34 hrs GMT Summer Time Johannesburg: Friday 14 May 2021 12:34 hrs SAST Tokyo: Friday 14 May 2021 19:34 hrs JST Kiribati: Saturday 15 May 2021 00:34 hrs LINT West of UTC Panama: Friday 14 May 2021 06:34 hrs EDT Washington DC: Friday 14 May 2021 05:34 hrs PAB Rarotonga: Friday 14 May 2021 00:34 hrs CKT Niue: Thursday 13 May 2021 23:34 hrs NUT
In this code, I’ve introduced another method of the
tz class, which is
tzoffset() you pass two attributes, the name of the timezone and the offset in seconds from UTC. Note that for timezones west of UTC, the offset number is positive; it’s negative for those East of UTC.
I find the world clock website to be useful for time, date, and abbreviation information.
In this article, we introduced the
datetime module in Python and the third-party package
dateutils. We aimed to output a list of times in chosen world cities compared to UTC and our local time.
We learned about the
datetime.datetime() class in Python, and the
tz class in
dateutils. We also looked at naive and aware
Classes we utilised are:
|Returns the current date and time of the computer system during the execution of the now statement.|
|Returns the timezone information or tzinfo.|
|Returns an aware datetime instance in UTC|
|Returns the timezone name from a datetime instance|
|Accepts two attributes; timezone name and its offset in seconds from UTC|
|Does a string format on the datetime instance from entered codes|
Finally, we produced a list of chosen cities worldwide and printed formatted dates and times, with their timezone name appended.
I trust this article has been helpful to you. Thanks for reading.
David is a Python programmer and a technical writer creating in-depth articles for readers wanting uncomplicated explanations for topics made difficult by industry jargon. Also a woodworker, metalworker, landscape photographer, and pilot, he is freelance after 42 years in the corporate world. He has an MBA in Technology.