Badge photo

EXTRA SAVINGS – ACHIEVE YOUR GOALS IN 2025 + 60% OFF PREMIUM

Sale Ends in
00
days
00
hours
00
mins
00
secs
May 11, 2022

Python Datetime: A Simple Guide with 39 Code Examples (2023)

You may already be familiar with various data types in Python like integers, floats, strings, etc. However, sometimes when you're developing a script or a machine learning algorithm, you should use dates and times. In everyday life, we can represent dates and times in many different formats, for example, July 4, March 8, 2022, 22:00, or 31 December 2022, 23:59:59. They use a combination of integers and strings, or you can also use floats to represent a fraction of a day, minute, etc.

But this isn't the only complication! Add to this listing different timezones, daylight saving time, different time format conventions (in the USA, it's 07/23/2022 and in Europe, it's 23/07/2023), etc.

Computers require unambiguous precision when we tell them what to do, but dates and times represent a challenge. Thankfully, Python has the datetime module that allows us to easily manipulate objects that represent dates and times.

In this Python datetime tutorial, we'll learn the following:

  1. Uses of the datetime module in Python
  2. Transforming a string to a datetime object and vice versa using Python datetime functions
  3. Extracting date and time from a datetime object
  4. Working with timestamps
  5. Performing arithmetic operations on dates and times
  6. Working with timezones
  7. Finally, creating a countdown timer to determine how long remains until New Year 2023 (in New York City!)

Let's begin working with dates and times in Python.

Note: If a piece of code doesn't have the import statement, assume that the class/function used in the code has already been imported.

How do I use datetime in Python?

As we said earlier, representing dates and times in programming is a challenge. First of all, we have to represent them in a standard, universally accepted format. Fortunately, the International Organization for Standardization (ISO) developed a worldwide standard ISO 8601, which represents date- and time-related objects as YYYY-MM-DD HH:MM:SS with information ranging from the most important (years,YYYY) to the least important (seconds, SS). Each piece of this format is represented as a four- or two-digit number.

The datetime module in Python has 5 main classes (parts of the module):

  • date to manipulate date objects
  • time to manipulate time objects
  • datetime is a combination of date and time
  • timedelta allows us to work with time duration
  • tzinfo allows us to work with timezones

Additionally, we'll use the zoneinfo module, which provides us a modern way of working with time zones, and the dateutil package, which contains plenty of useful functions to work with dates and times.

Let's import the datetime module and create our first date and time objects:

# From the datetime module import date
from datetime import date

# Create a date object of 2000-02-03
date(2000, 2, 3)
datetime.date(2000, 2, 3)

In the code above, we imported the date class from the module and then created a datetime.date object of February 3, 2000. We can note that the order of numbers that we use to create this object is exactly the same as in ISO 8061 (but we omit 0s and write only one-digit numbers for the month and the day).

Coding is all about experimentation, so let's say we want to create an object of 2000-26-03:

# Create a date object of 2000-26-03
date(2000, 26, 3)
---------------------------------------------------------------------------

ValueError                                Traceback (most recent call last)

Input In [2], in 
      1 # Create a date object of 2000-26-03
----> 2 date(2000, 26, 3)

ValueError: month must be in 1..12

We get ValueError: month must be in 1..12, which makes sense because there is no month 26 in the calendar. This function strictly follows the ISO 8601 format. For practice, add more arguments to the date function and note what happens.

Let's see how we can create a datetime.time object:

# From the datetime module import time
from datetime import time

# Create a time object of 05:35:02
time(5, 35, 2)
datetime.time(5, 35, 2)

We've succeeded, and you can guess that this function also follows the ISO 8061 format. Can you break it as we did before with the date function?

Now, what if we want both a date and a time in one object? We should use the datetime class:

# From the datetime module import datetime
from datetime import datetime

# Create a datetime object of 2000-02-03 05:35:02
datetime(2000, 2, 3, 5, 35, 2)
datetime.datetime(2000, 2, 3, 5, 35, 2)

Great, we have our datetime object. We can also be more explicit and pass keyword arguments to the datetime constructor:

datetime(year=2000, month=2, day=3, hour=5, minute=35, second=2)
datetime.datetime(2000, 2, 3, 5, 35, 2)

What if we pass in only three arguments (year, month, and day)? Would it cause an error?

# Create a datetime object of 2000-02-03
datetime(2000, 2, 3)
datetime.datetime(2000, 2, 3, 0, 0)

We can see that now there are two zeros in the object that represent (respectively) the hour and the minute. Seconds, in this case, are omitted.

In many situations, we want to know the exact time at the moment. We can use the now() method of the datetime class:

# Time at the moment
now = datetime.now()
now
datetime.datetime(2022, 2, 14, 11, 33, 25, 516707)

We get a datetime object. Here the last number is microseconds.

If we only need today's date, we can use the today() method from the date class:

today = date.today()
today
datetime.date(2022, 2, 14)

If we only need the time, we may write time.now(), but it won't work. Instead, we have to access the hour, minute, and second attributes of the datetime.now() object and pass them to the time constructor:

time(now.hour, now.minute, now.second)
datetime.time(11, 33, 25)

For practice, get the date from the datetime object in Python (assigned to the now variable).

We can also extract the week number and the number of the day from a datetime object using the isocalendar() function. It will return a three-item tuple containing an ISO year, week number, and weekday number:

# isocalendar() returns a 3-item tuple with ISO year, week number, and weekday number
now.isocalendar()
datetime.IsoCalendarDate(year=2022, week=7, weekday=1)

In the ISO format, a week starts on Monday and ends on Sunday. The days of the week are encoded by the numbers from 1 (Monday) to 7 (Sunday). If we want to access one of these tuple elements, we use bracket notation:

# Access week number
week_number = now.isocalendar()[1]
week_number
7

Extracting dates from strings

In data science, and in programming generally, we mostly work with dates and times that are stored as strings in dozens of different formats, depending on the region, company, or the granularity of the information we need. Sometimes, we need the date and the exact time, but in other cases, we only need the year and the month. How can we extract the data we need from a string to easily manipulate it as a datetime (date, time) object?

fromisoformat() and isoformat()

The first function we'll learn to convert a date string into a date object is fromisoformat. We call it this way because it uses the ISO 8601 format (i.e., YYYY-MM-DD). Let's look at an example:

# Convert a date string into a date object
date.fromisoformat("2022-12-31")
datetime.date(2022, 12, 31)

The ISO format also contains the time, but if we pass it to the function, it won't work:

date.fromisoformat("2022-12-31 00:00:00")
---------------------------------------------------------------------------

ValueError                                Traceback (most recent call last)

Input In [13], in 
----> 1 date.fromisoformat("2022-12-31 00:00:00")

ValueError: Invalid isoformat string: '2022-12-31 00:00:00'

Of course, we can also perform an inverse operation and convert a datetime object into a date string in the ISO format. For this purpose, we should use the isoformat():

# Convert a datetime object into a string in the ISO format
date(2022, 12, 31).isoformat()
'2022-12-31'

strptime()

To solve the above ValueError problem, we can use the strptime() function that can convert an arbitrary date/time string into a datetime object. Our string doesn't necessarily need to follow the ISO format, but we should specify which part of the string represents which date or time unit (year, hour, etc.). Let's look at an example to clarify. First of all, we'll use the strict ISO format to convert a string into a datetime object:

# Date as a string
iso_date = "2022-12-31 23:59:58"

# ISO format
iso_format = "

# Convert the string into a datetime object
datetime.strptime(iso_date, iso_format)
datetime.datetime(2022, 12, 31, 23, 59, 58)

In the first line, we create a date/time string. In the second line, we specify the format of the string using a special code that contains a percent sign followed by a character that encodes a date or time unit. Finally, in the third line, we use the strptime() function to convert the string into a datetime object. This function takes two arguments: the string and the format of the string.

The code we used above can also encode other date and time units like a weekday, month name, week number, etc.

Code Example Description
Monday Full weekday name
December Full month name
2 Week number (Monday as the first day of the week)

You can refer to this website for other codes used in Python.

Let's look at a few more examples using other formats:

# European date as a string
european_date = "31-12-2022"

# European format
european_format = "

# Convert the string into a datetime object
datetime.strptime(european_date, european_format)
datetime.datetime(2022, 12, 31, 0, 0)

As we can see above, the string was successfully converted, but we have additional zeros that represent the time. Let's look at an example using other codes:

# Full month name date
full_month_date = "12 September 2022"

# Full month format
full_month_format = "

# Convert the string into a datetime object
datetime.strptime(full_month_date, full_month_format)
datetime.datetime(2022, 9, 12, 0, 0)

Again success! Note that the format we define should match the format of the date string. Thus, if we have spaces, colons, hyphens, or other characters to separate time units, they should be in the code string. Otherwise, Python will throw a ValueError:

# Full month name date
full_month_date = "12 September 2022"

# Wrong format (missing space)
full_month_format = "

# Convert the string into a datetime object
datetime.strptime(full_month_date, full_month_format)
---------------------------------------------------------------------------

ValueError                                Traceback (most recent call last)

Input In [18], in 
      5 full_month_format = 
      7 # Convert the string into a datetime object
----> 8 datetime.strptime(full_month_date, full_month_format)

File ~/coding/dataquest/articles/using-the-datetime-package/env/lib/python3.10/_strptime.py:568, in _strptime_datetime(cls, data_string, format)
    565 def _strptime_datetime(cls, data_string, format=
    566     """Return a class cls instance based on the input string and the
    567     format string."""
--> 568     tt, fraction, gmtoff_fraction = _strptime(data_string, format)
    569     tzname, gmtoff = tt[-2:]
    570     args = tt[:6] + (fraction,)

File ~/coding/dataquest/articles/using-the-datetime-package/env/lib/python3.10/_strptime.py:349, in _strptime(data_string, format)
    347 found = format_regex.match(data_string)
    348 if not found:
--> 349     raise ValueError("time data
    350                      (data_string, format))
    351 if len(data_string) != found.end():
    352     raise ValueError("unconverted data remains:
    353                       data_string[found.end():])

ValueError: time data '12 September 2022' does not match format 

Even one missing space can cause the error!

Converting datetime objects into strings

strftime()

In Python, we can also convert a datetime object into a string using the strftime() function. It takes two arguments: a datetime object and the format of the output string.

# Create a datetime object
datetime_obj = datetime(2022, 12, 31)

# American date format
american_format = "

# European format
european_format = "

# American date string
print(f"American date string: {datetime.strftime(datetime_obj, american_format)}.")

# European date string
print(f"European date string: {datetime.strftime(datetime_obj, european_format)}.")
American date string: 12-31-2022.
European date string: 31-12-2022.

We took the same datetime object and converted it into two different formats. We can also specify other formats, like the full month name followed by the day and the year.

full_month = "
datetime.strftime(datetime_obj, full_month)
'December 31, 2022'

Another way to use strftime is to place it after a datetime object:

datetime_obj = datetime(2022, 12, 31, 23, 59, 59)
full_datetime = "
datetime_obj.strftime(full_datetime)
'December 31, 2022 23:59:59'

In practice, strftime() may be useful if we want to extract the weekday name that falls on December 31 in different years:

# Extract the weekday name of December 31
weekday_format = "

for year in range(2022, 2026):
    print(f"Weekday of December 31, {year} is {date(year, 12, 31).strftime(weekday_format)}.")
Weekday of December 31, 2022 is Saturday.
Weekday of December 31, 2023 is Sunday.
Weekday of December 31, 2024 is Tuesday.
Weekday of December 31, 2025 is Wednesday.

Timestamps

In programming, it's common to see dates and times stored in the Unix timestamp format. This format represents whatever date as digits. Basically, a timestamp is a number of seconds that passed from the Unix epoch that started at 00:00:00 UTC (Coordinated Universal Time) on 1 January 1970. We can compute this number using the timestamp() function:

new_year_2023 = datetime(2022, 12, 31)
datetime.timestamp(new_year_2023)
1672441200.0

1672441200 is the number of seconds between the start of the Unix epoch and December 31, 2022.

We can perform the inverse operation by using the fromtimestamp() function:

datetime.fromtimestamp(1672441200)
datetime.datetime(2022, 12, 31, 0, 0)

Arithmetic operations with dates

Sometimes, we may want to compute the difference between two dates or perform other arithmetic operations on dates and times. Fortunately, Python has many instruments in its toolkit to perform such calculations.

Basic arithmetic operations

The first operation we can perform is to calculate the difference between two dates. To do this, we use the minus sign:

# Instatiate two dates
first_date = date(2022, 1, 1)
second_date = date(2022, 12, 31)

# Difference between two dates
date_diff = second_date - first_date

# Function to convert datetime to string
def dt_string(date, date_format="
    return date.strftime(date_format)

print(f"The number of days and hours between {dt_string(first_date)} and {dt_string(second_date)} is {date_diff}.")
The number of days and hours between January 01, 2022 and December 31, 2022 is 364 days, 0:00:00.

Let's look at what type of object first_date - second_date returns:

type(date_diff)
datetime.timedelta

The type of this object is datetime.timedelta. It has delta in its name, which refers to a Green letter delta, $\Delta$, which in science and engineering, describes a change. Indeed, here it represents a change (difference) in time.

What if we're interested only in the number of days between two dates? We can access different attributes of the timedelta objects, and one of them is called .days.

print(f"The number of days between {dt_string(first_date)} and {dt_string(second_date)} is {(date_diff).days}.")
The number of days between January 01, 2022 and December 31, 2022 is 364.

timedelta()

Now that we know about the timedelta object, it's time to introduce the timedelta() function. It allows us to perform many arithmetic operations on time objects by adding and subtracting time units like days, years, weeks, seconds, etc. For example, we may be interested in knowing what day of the week is 30 days from now. To do so, we have to create an object that represents the current time and a timedelta object that defines the amount of time we add to it:

# Import timedelta
from datetime import timedelta

# Current time
now = datetime.now()

# timedelta of 30 days
one_month = timedelta(days=30)

# Day in one month/using dt_string function defined above
print(f"The day in 30 days is {dt_string(now + one_month)}.")
The day in 30 days is March 16, 2022.

If we check the help page of the timedelta function (help(timedelta)), we'll see that it has the following arguments: days=0, seconds=0, microseconds=0, milliseconds=0, minutes=0, hours=0, weeks=0. So, you can practice adding or subtracting other time units to a date. For example, we can compute the time 12 hours before New Year 2030:

# New year 2030
new_year_2030 = datetime(2030, 1, 1, 0, 0)

# timedelta of 12 hours
twelve_hours = timedelta(hours=12)

# Time string of 12 hours before New Year 2023
twelve_hours_before = (new_year_2030 - twelve_hours).strftime("

# Print the time 12 hours before New Year 2023
print(f"The time twelve hours before New Year 2030 will be {twelve_hours_before}.")
The time twelve hours before New Year 2030 will be December 31, 2029, 12:00:00.

We can also combine multiple arguments of the timedelta() function to figure out a more specific time. For example, what will the time be in 27 days, 3 hours, and 45 minutes from now?

# Current time
now = datetime.now()

# Timedelta of 27 days, 3 hours, and 45 minutes
specific_timedelta = timedelta(days=27, hours=3, minutes=45)

# Time in 27 days, 3 hours, and 45 minutes
twenty_seven_days = (now + specific_timedelta).strftime("

print(f"The time in 27 days, 3 hours, and 45 minutes will be {twenty_seven_days}.")
The time in 27 days, 3 hours, and 45 minutes will be March 13, 2022, 15:18:39.

relativedelta()

Unfortunately, we can see from the help page that the function doesn't allow us to use months or years. To overcome this limitation, we can use the relativedelta function from the dateutil package. This function is very similar to timedelta(), but it extends its functionality.

For example, we want to subtract 2 years, 3 months, 4 days, and 5 hours from the current time:

# Import relativedelta
from dateutil.relativedelta import relativedelta

# Current time
now = datetime.now()

# relativedelta object
relative_delta = relativedelta(years=2, months=3, days=4, hours=5)

two_years = (now - relative_delta).strftime("

print(f"The time 2 years, 3 months, 4 days, and 5 hours ago was {two_years}.")
The time 2 years, 3 months, 4 days, and 5 hours ago was November 10, 2019, 06:33:40.

We can also use relativedelta() to compute the difference between two datetime objects:

relativedelta(datetime(2030, 12, 31), now)
relativedelta(years=+8, months=+10, days=+16, hours=+12, minutes=+26, seconds=+19, microseconds=+728345)

These arithmetic operations may seem very abstract and impractical, but actually, they're useful in many applications. One such scenario might look like the following.

Say, a certain operation in our script should be performed only 30 days before a specific date. We can define a variable that holds the current time and adds a timedelta object of 30 days to it. If today is the day, the operation will be evoked!

Otherwise, imagine we're working with datasets using pandas, and one of the columns contains some dates. Imagine that we have a dataset that holds our company profits on each day of the year. We want to create another dataset that will hold the dates exactly one year from the current date and predicted profits on each of those days. We're bound to use arithmetic calculations on dates!

Sooner or later, you'll encounter dates and times in one of your data science projects. When you do, head back to this tutorial.

Working with Time Zones

As we mentioned in the introduction, one of the issues of working with time in programming is handling time zones. They can have different shapes. We should also be aware that some regions implement daylight saving time (DST) while others don't.

Python distinguishes two types of date and time objects: naive and aware. A naive object doesn't hold any information about time zones, and an aware object does hold it.

First of all, let's look at a naive time object:

# Import tzinfo
from datetime import tzinfo

# Naive datetime
naive_datetime = datetime.now()

# Naive datetime doesn't hold any timezone information
type(naive_datetime.tzinfo)
NoneType

From Python 3.9, we have a concrete implementation of time zones using the Internet Assigned Numbers Authority database. The module that implements this functionality is called zoneinfo. Note that if you're using previous versions of Python, you may use the pytz or dateutil packages.

Let's create an aware datetime object using zoneinfo and, in particular, the ZoneInfo class, which is an implementation of the datetime.tzinfo abstract class:

# Import ZoneInfo 
from zoneinfo import ZoneInfo

utc_tz = ZoneInfo("UTC")

# Aware datetime object with UTC timezone
dt_utc = datetime.now(tz=utc_tz)

# The type of an aware object implemented with ZoneInfo is zoneinfo.ZoneInfo
type(dt_utc.tzinfo)
zoneinfo.ZoneInfo

We can see that an aware datetime object has the information about time zones (implemented as a zoneinfo.ZoneInfo object).

Let's look at a few examples. We want to determine the current time in Central Europe and California.

First of all, we can list all the available time zones in zoneinfo:

import zoneinfo

# Will return a long list of timezones (opens many files!)
zoneinfo.available_timezones() 
{'Africa/Abidjan',
 'Africa/Accra',
 'Africa/Addis_Ababa',
 'Africa/Algiers',
 'Africa/Asmara',
 'Africa/Asmera',
 'Africa/Bamako',
 'Africa/Bangui',
 'Africa/Banjul',
 'Africa/Bissau',
 'Africa/Blantyre',
 'Africa/Brazzaville',
 'Africa/Bujumbura',
 'Africa/Cairo',
 'Africa/Casablanca',
 'Africa/Ceuta',
 'Africa/Conakry',
 'Africa/Dakar',
 'Africa/Dar_es_Salaam',
 'Africa/Djibouti',
 'Africa/Douala',
 'Africa/El_Aaiun',
 'Africa/Freetown',
 'Africa/Gaborone',
 'Africa/Harare',
 'Africa/Johannesburg',
 'Africa/Juba',
 'Africa/Kampala',
 'Africa/Khartoum',
 'Africa/Kigali',
 'Africa/Kinshasa',
 'Africa/Lagos',
 'Africa/Libreville',
 'Africa/Lome',
 'Africa/Luanda',
 'Africa/Lubumbashi',
 'Africa/Lusaka',
 'Africa/Malabo',
 'Africa/Maputo',
 'Africa/Maseru',
 'Africa/Mbabane',
 'Africa/Mogadishu',
 'Africa/Monrovia',
 'Africa/Nairobi',
 'Africa/Ndjamena',
 'Africa/Niamey',
 'Africa/Nouakchott',
 'Africa/Ouagadougou',
 'Africa/Porto-Novo',
 'Africa/Sao_Tome',
 'Africa/Timbuktu',
 'Africa/Tripoli',
 'Africa/Tunis',
 'Africa/Windhoek',
 'America/Adak',
 'America/Anchorage',
 'America/Anguilla',
 'America/Antigua',
 'America/Araguaina',
 'America/Argentina/Buenos_Aires',
 'America/Argentina/Catamarca',
 'America/Argentina/ComodRivadavia',
 'America/Argentina/Cordoba',
 'America/Argentina/Jujuy',
 'America/Argentina/La_Rioja',
 'America/Argentina/Mendoza',
 'America/Argentina/Rio_Gallegos',
 'America/Argentina/Salta',
 'America/Argentina/San_Juan',
 'America/Argentina/San_Luis',
 'America/Argentina/Tucuman',
 'America/Argentina/Ushuaia',
 'America/Aruba',
 'America/Asuncion',
 'America/Atikokan',
 'America/Atka',
 'America/Bahia',
 'America/Bahia_Banderas',
 'America/Barbados',
 'America/Belem',
 'America/Belize',
 'America/Blanc-Sablon',
 'America/Boa_Vista',
 'America/Bogota',
 'America/Boise',
 'America/Buenos_Aires',
 'America/Cambridge_Bay',
 'America/Campo_Grande',
 'America/Cancun',
 'America/Caracas',
 'America/Catamarca',
 'America/Cayenne',
 'America/Cayman',
 'America/Chicago',
 'America/Chihuahua',
 'America/Coral_Harbour',
 'America/Cordoba',
 'America/Costa_Rica',
 'America/Creston',
 'America/Cuiaba',
 'America/Curacao',
 'America/Danmarkshavn',
 'America/Dawson',
 'America/Dawson_Creek',
 'America/Denver',
 'America/Detroit',
 'America/Dominica',
 'America/Edmonton',
 'America/Eirunepe',
 'America/El_Salvador',
 'America/Ensenada',
 'America/Fort_Nelson',
 'America/Fort_Wayne',
 'America/Fortaleza',
 'America/Glace_Bay',
 'America/Godthab',
 'America/Goose_Bay',
 'America/Grand_Turk',
 'America/Grenada',
 'America/Guadeloupe',
 'America/Guatemala',
 'America/Guayaquil',
 'America/Guyana',
 'America/Halifax',
 'America/Havana',
 'America/Hermosillo',
 'America/Indiana/Indianapolis',
 'America/Indiana/Knox',
 'America/Indiana/Marengo',
 'America/Indiana/Petersburg',
 'America/Indiana/Tell_City',
 'America/Indiana/Vevay',
 'America/Indiana/Vincennes',
 'America/Indiana/Winamac',
 'America/Indianapolis',
 'America/Inuvik',
 'America/Iqaluit',
 'America/Jamaica',
 'America/Jujuy',
 'America/Juneau',
 'America/Kentucky/Louisville',
 'America/Kentucky/Monticello',
 'America/Knox_IN',
 'America/Kralendijk',
 'America/La_Paz',
 'America/Lima',
 'America/Los_Angeles',
 'America/Louisville',
 'America/Lower_Princes',
 'America/Maceio',
 'America/Managua',
 'America/Manaus',
 'America/Marigot',
 'America/Martinique',
 'America/Matamoros',
 'America/Mazatlan',
 'America/Mendoza',
 'America/Menominee',
 'America/Merida',
 'America/Metlakatla',
 'America/Mexico_City',
 'America/Miquelon',
 'America/Moncton',
 'America/Monterrey',
 'America/Montevideo',
 'America/Montreal',
 'America/Montserrat',
 'America/Nassau',
 'America/New_York',
 'America/Nipigon',
 'America/Nome',
 'America/Noronha',
 'America/North_Dakota/Beulah',
 'America/North_Dakota/Center',
 'America/North_Dakota/New_Salem',
 'America/Nuuk',
 'America/Ojinaga',
 'America/Panama',
 'America/Pangnirtung',
 'America/Paramaribo',
 'America/Phoenix',
 'America/Port-au-Prince',
 'America/Port_of_Spain',
 'America/Porto_Acre',
 'America/Porto_Velho',
 'America/Puerto_Rico',
 'America/Punta_Arenas',
 'America/Rainy_River',
 'America/Rankin_Inlet',
 'America/Recife',
 'America/Regina',
 'America/Resolute',
 'America/Rio_Branco',
 'America/Rosario',
 'America/Santa_Isabel',
 'America/Santarem',
 'America/Santiago',
 'America/Santo_Domingo',
 'America/Sao_Paulo',
 'America/Scoresbysund',
 'America/Shiprock',
 'America/Sitka',
 'America/St_Barthelemy',
 'America/St_Johns',
 'America/St_Kitts',
 'America/St_Lucia',
 'America/St_Thomas',
 'America/St_Vincent',
 'America/Swift_Current',
 'America/Tegucigalpa',
 'America/Thule',
 'America/Thunder_Bay',
 'America/Tijuana',
 'America/Toronto',
 'America/Tortola',
 'America/Vancouver',
 'America/Virgin',
 'America/Whitehorse',
 'America/Winnipeg',
 'America/Yakutat',
 'America/Yellowknife',
 'Antarctica/Casey',
 'Antarctica/Davis',
 'Antarctica/DumontDUrville',
 'Antarctica/Macquarie',
 'Antarctica/Mawson',
 'Antarctica/McMurdo',
 'Antarctica/Palmer',
 'Antarctica/Rothera',
 'Antarctica/South_Pole',
 'Antarctica/Syowa',
 'Antarctica/Troll',
 'Antarctica/Vostok',
 'Arctic/Longyearbyen',
 'Asia/Aden',
 'Asia/Almaty',
 'Asia/Amman',
 'Asia/Anadyr',
 'Asia/Aqtau',
 'Asia/Aqtobe',
 'Asia/Ashgabat',
 'Asia/Ashkhabad',
 'Asia/Atyrau',
 'Asia/Baghdad',
 'Asia/Bahrain',
 'Asia/Baku',
 'Asia/Bangkok',
 'Asia/Barnaul',
 'Asia/Beirut',
 'Asia/Bishkek',
 'Asia/Brunei',
 'Asia/Calcutta',
 'Asia/Chita',
 'Asia/Choibalsan',
 'Asia/Chongqing',
 'Asia/Chungking',
 'Asia/Colombo',
 'Asia/Dacca',
 'Asia/Damascus',
 'Asia/Dhaka',
 'Asia/Dili',
 'Asia/Dubai',
 'Asia/Dushanbe',
 'Asia/Famagusta',
 'Asia/Gaza',
 'Asia/Harbin',
 'Asia/Hebron',
 'Asia/Ho_Chi_Minh',
 'Asia/Hong_Kong',
 'Asia/Hovd',
 'Asia/Irkutsk',
 'Asia/Istanbul',
 'Asia/Jakarta',
 'Asia/Jayapura',
 'Asia/Jerusalem',
 'Asia/Kabul',
 'Asia/Kamchatka',
 'Asia/Karachi',
 'Asia/Kashgar',
 'Asia/Kathmandu',
 'Asia/Katmandu',
 'Asia/Khandyga',
 'Asia/Kolkata',
 'Asia/Krasnoyarsk',
 'Asia/Kuala_Lumpur',
 'Asia/Kuching',
 'Asia/Kuwait',
 'Asia/Macao',
 'Asia/Macau',
 'Asia/Magadan',
 'Asia/Makassar',
 'Asia/Manila',
 'Asia/Muscat',
 'Asia/Nicosia',
 'Asia/Novokuznetsk',
 'Asia/Novosibirsk',
 'Asia/Omsk',
 'Asia/Oral',
 'Asia/Phnom_Penh',
 'Asia/Pontianak',
 'Asia/Pyongyang',
 'Asia/Qatar',
 'Asia/Qostanay',
 'Asia/Qyzylorda',
 'Asia/Rangoon',
 'Asia/Riyadh',
 'Asia/Saigon',
 'Asia/Sakhalin',
 'Asia/Samarkand',
 'Asia/Seoul',
 'Asia/Shanghai',
 'Asia/Singapore',
 'Asia/Srednekolymsk',
 'Asia/Taipei',
 'Asia/Tashkent',
 'Asia/Tbilisi',
 'Asia/Tehran',
 'Asia/Tel_Aviv',
 'Asia/Thimbu',
 'Asia/Thimphu',
 'Asia/Tokyo',
 'Asia/Tomsk',
 'Asia/Ujung_Pandang',
 'Asia/Ulaanbaatar',
 'Asia/Ulan_Bator',
 'Asia/Urumqi',
 'Asia/Ust-Nera',
 'Asia/Vientiane',
 'Asia/Vladivostok',
 'Asia/Yakutsk',
 'Asia/Yangon',
 'Asia/Yekaterinburg',
 'Asia/Yerevan',
 'Atlantic/Azores',
 'Atlantic/Bermuda',
 'Atlantic/Canary',
 'Atlantic/Cape_Verde',
 'Atlantic/Faeroe',
 'Atlantic/Faroe',
 'Atlantic/Jan_Mayen',
 'Atlantic/Madeira',
 'Atlantic/Reykjavik',
 'Atlantic/South_Georgia',
 'Atlantic/St_Helena',
 'Atlantic/Stanley',
 'Australia/ACT',
 'Australia/Adelaide',
 'Australia/Brisbane',
 'Australia/Broken_Hill',
 'Australia/Canberra',
 'Australia/Currie',
 'Australia/Darwin',
 'Australia/Eucla',
 'Australia/Hobart',
 'Australia/LHI',
 'Australia/Lindeman',
 'Australia/Lord_Howe',
 'Australia/Melbourne',
 'Australia/NSW',
 'Australia/North',
 'Australia/Perth',
 'Australia/Queensland',
 'Australia/South',
 'Australia/Sydney',
 'Australia/Tasmania',
 'Australia/Victoria',
 'Australia/West',
 'Australia/Yancowinna',
 'Brazil/Acre',
 'Brazil/DeNoronha',
 'Brazil/East',
 'Brazil/West',
 'CET',
 'CST6CDT',
 'Canada/Atlantic',
 'Canada/Central',
 'Canada/Eastern',
 'Canada/Mountain',
 'Canada/Newfoundland',
 'Canada/Pacific',
 'Canada/Saskatchewan',
 'Canada/Yukon',
 'Chile/Continental',
 'Chile/EasterIsland',
 'Cuba',
 'EET',
 'EST',
 'EST5EDT',
 'Egypt',
 'Eire',
 'Etc/GMT',
 'Etc/GMT+0',
 'Etc/GMT+1',
 'Etc/GMT+10',
 'Etc/GMT+11',
 'Etc/GMT+12',
 'Etc/GMT+2',
 'Etc/GMT+3',
 'Etc/GMT+4',
 'Etc/GMT+5',
 'Etc/GMT+6',
 'Etc/GMT+7',
 'Etc/GMT+8',
 'Etc/GMT+9',
 'Etc/GMT-0',
 'Etc/GMT-1',
 'Etc/GMT-10',
 'Etc/GMT-11',
 'Etc/GMT-12',
 'Etc/GMT-13',
 'Etc/GMT-14',
 'Etc/GMT-2',
 'Etc/GMT-3',
 'Etc/GMT-4',
 'Etc/GMT-5',
 'Etc/GMT-6',
 'Etc/GMT-7',
 'Etc/GMT-8',
 'Etc/GMT-9',
 'Etc/GMT0',
 'Etc/Greenwich',
 'Etc/UCT',
 'Etc/UTC',
 'Etc/Universal',
 'Etc/Zulu',
 'Europe/Amsterdam',
 'Europe/Andorra',
 'Europe/Astrakhan',
 'Europe/Athens',
 'Europe/Belfast',
 'Europe/Belgrade',
 'Europe/Berlin',
 'Europe/Bratislava',
 'Europe/Brussels',
 'Europe/Bucharest',
 'Europe/Budapest',
 'Europe/Busingen',
 'Europe/Chisinau',
 'Europe/Copenhagen',
 'Europe/Dublin',
 'Europe/Gibraltar',
 'Europe/Guernsey',
 'Europe/Helsinki',
 'Europe/Isle_of_Man',
 'Europe/Istanbul',
 'Europe/Jersey',
 'Europe/Kaliningrad',
 'Europe/Kiev',
 'Europe/Kirov',
 'Europe/Lisbon',
 'Europe/Ljubljana',
 'Europe/London',
 'Europe/Luxembourg',
 'Europe/Madrid',
 'Europe/Malta',
 'Europe/Mariehamn',
 'Europe/Minsk',
 'Europe/Monaco',
 'Europe/Moscow',
 'Europe/Nicosia',
 'Europe/Oslo',
 'Europe/Paris',
 'Europe/Podgorica',
 'Europe/Prague',
 'Europe/Riga',
 'Europe/Rome',
 'Europe/Samara',
 'Europe/San_Marino',
 'Europe/Sarajevo',
 'Europe/Saratov',
 'Europe/Simferopol',
 'Europe/Skopje',
 'Europe/Sofia',
 'Europe/Stockholm',
 'Europe/Tallinn',
 'Europe/Tirane',
 'Europe/Tiraspol',
 'Europe/Ulyanovsk',
 'Europe/Uzhgorod',
 'Europe/Vaduz',
 'Europe/Vatican',
 'Europe/Vienna',
 'Europe/Vilnius',
 'Europe/Volgograd',
 'Europe/Warsaw',
 'Europe/Zagreb',
 'Europe/Zaporozhye',
 'Europe/Zurich',
 'Factory',
 'GB',
 'GB-Eire',
 'GMT',
 'GMT+0',
 'GMT-0',
 'GMT0',
 'Greenwich',
 'HST',
 'Hongkong',
 'Iceland',
 'Indian/Antananarivo',
 'Indian/Chagos',
 'Indian/Christmas',
 'Indian/Cocos',
 'Indian/Comoro',
 'Indian/Kerguelen',
 'Indian/Mahe',
 'Indian/Maldives',
 'Indian/Mauritius',
 'Indian/Mayotte',
 'Indian/Reunion',
 'Iran',
 'Israel',
 'Jamaica',
 'Japan',
 'Kwajalein',
 'Libya',
 'MET',
 'MST',
 'MST7MDT',
 'Mexico/BajaNorte',
 'Mexico/BajaSur',
 'Mexico/General',
 'NZ',
 'NZ-CHAT',
 'Navajo',
 'PRC',
 'PST8PDT',
 'Pacific/Apia',
 'Pacific/Auckland',
 'Pacific/Bougainville',
 'Pacific/Chatham',
 'Pacific/Chuuk',
 'Pacific/Easter',
 'Pacific/Efate',
 'Pacific/Enderbury',
 'Pacific/Fakaofo',
 'Pacific/Fiji',
 'Pacific/Funafuti',
 'Pacific/Galapagos',
 'Pacific/Gambier',
 'Pacific/Guadalcanal',
 'Pacific/Guam',
 'Pacific/Honolulu',
 'Pacific/Johnston',
 'Pacific/Kanton',
 'Pacific/Kiritimati',
 'Pacific/Kosrae',
 'Pacific/Kwajalein',
 'Pacific/Majuro',
 'Pacific/Marquesas',
 'Pacific/Midway',
 'Pacific/Nauru',
 'Pacific/Niue',
 'Pacific/Norfolk',
 'Pacific/Noumea',
 'Pacific/Pago_Pago',
 'Pacific/Palau',
 'Pacific/Pitcairn',
 'Pacific/Pohnpei',
 'Pacific/Ponape',
 'Pacific/Port_Moresby',
 'Pacific/Rarotonga',
 'Pacific/Saipan',
 'Pacific/Samoa',
 'Pacific/Tahiti',
 'Pacific/Tarawa',
 'Pacific/Tongatapu',
 'Pacific/Truk',
 'Pacific/Wake',
 'Pacific/Wallis',
 'Pacific/Yap',
 'Poland',
 'Portugal',
 'ROC',
 'ROK',
 'Singapore',
 'Turkey',
 'UCT',
 'US/Alaska',
 'US/Aleutian',
 'US/Arizona',
 'US/Central',
 'US/East-Indiana',
 'US/Eastern',
 'US/Hawaii',
 'US/Indiana-Starke',
 'US/Michigan',
 'US/Mountain',
 'US/Pacific',
 'US/Samoa',
 'UTC',
 'Universal',
 'W-SU',
 'WET',
 'Zulu',
 'build/etc/localtime'}

Now we can use ZoneInfo to determine the current times in different regions:

# Function to convert datetime into ISO formatted time
def iso(time, time_format="
    return time.strftime(time_format)

# CET time zone
cet_tz = ZoneInfo("Europe/Paris")

# PST time zone
pst_tz = ZoneInfo("America/Los_Angeles")

# Current time in Central Europe
dt_cet = datetime.now(tz=cet_tz)

# Current time in California
dt_pst = datetime.now(tz=pst_tz)

print(f"Current time in Central Europe is {iso(dt_cet)}.")
print(f"Current time in California is {iso(dt_pst)}.")
Current time in Central Europe is 2022-02-14 11:33:42.
Current time in California is 2022-02-14 02:33:42.

Let's print datetime.now(tz=cet_tz):

print(datetime.now(tz=cet_tz))
2022-02-14 11:33:43.048967+01:00

We see that there is +01:00, which indicates the UTC offset. Indeed, the CET time zone is one hour ahead of UTC.

Moreover, the ZoneInfo class handles daylight saving time. For example, we can add one day (24 hours) to a day when DST change occurs. In the USA, in 2022, it'll happen on November 5 (clock backward).

# Define timedelta
time_delta = timedelta(days=1)

# November 5, 2022, 3 PM
november_5_2022 = datetime(2022, 11, 5, 15, tzinfo=pst_tz) # Note that we should use tzinfo in the datetime construct
print(november_5_2022)

# Add 1 day to November 5, 2022
print(november_5_2022 + time_delta)
2022-11-05 15:00:00-07:00
2022-11-06 15:00:00-08:00

As we can see, the offset changed from -07:00 to -08:00, but the time remained the same (15:00).

Countdown Timer to New Year 2023 in New York City

Times Square in New Your City attracts thousands of people on New Year's Eve. Let's apply everything we've learned so far to create a countdown timer to Times Square New Year's Eve!

Here we'll use tz from the dateutil package, which allows us to set our local time zone to demonstrate the utility of the dateutil package. However, we can also use the build/etc/localtime time zone from zoneinfo to do the same thing.

from zoneinfo import ZoneInfo
from dateutil import tz
from datetime import datetime

def main():
    """Main function."""
    # Set current time in our local time zone
    now = datetime.now(tz=tz.tzlocal())

    # New York City time zone
    nyc_tz = ZoneInfo("America/New_York")

    # New Year 2023 in NYC
    new_year_2023 = datetime(2023, 1, 1, 0, 0, tzinfo=nyc_tz)

    # Compute the time left to New Year in NYC
    countdown = relativedelta(new_year_2023, now)

    # Print time left to New Year 2023
    print(f"New Year in New Your City will come on: {new_year_2023.strftime('
    print(f"Time left to New Year 2023 in NYC is: {countdown.months} months, {countdown.days} days, {countdown.hours} hours, {countdown.minutes} minutes, {countdown.seconds} seconds.")

if __name__ == "__main__":
    main()
New Year in New Your City will come on: January 1, 2023 00:00:00.
Time left to New Year 2023 in NYC is: 10 months, 17 days, 18 hours, 26 minutes, 16 seconds.

We wrapped the code in the main() function, and now we can use it in a .py file. In this script, we worked with time zones, created a datetime object, converted it into a string using strftime(), and even accessed time attributes of a relativedelta object!

For practice, you can improve the script. I'm writing this tutorial in 2022, so for me, the New Year hasn't come yet. If you're reading it in future years, you can improve it and account for the fact that the New Year 2023 has already come. You can also include times left until future New Years. Or you may count the time that passed from the previous New Years. You can also update the script using the pytz package to make it compatible with older versions of Python (before 3.9). It's up to you now.

Conclusions

Here's what we've covered in this tutorial:

  • Issues with representing dates and times on computers
  • Creating date, time, and datetime objects
  • Standard date format, ISO 8061
  • Extracting dates from strings using fromisoformat() and strptime()
  • Converting datetime objects into strings using strftime()
  • Timestamps
  • Arithmetic operations with dates and timedelta objects
  • Dealing with time zones in Python with
  • Creating a countdown timer to New Year 2023 in New York City

Now you have plenty of tools in your toolkit, and you can start working with dates and times in Python.

Feel free to connect with me on LinkedIn and GitHub. Happy coding!

Artur Sannikov

About the author

Artur Sannikov

I am a Molecular Biology student at the University of Padua, Italy interested in bioinformatics and data analysis.