The datetime module is a built-in module in Python that allows us to work with dates and times easily. Inside the module we can find a few classes, but the most used ones are datetime and timedelta.

This usually means that we work with the datetime class inside the datetime module—somewhat confusing, but that's why you'll normally see things like datetime.datetime when looking at Python code that uses this.

What is a datetime object?

Put simply, a datetime object is one that stores information about a specific point in time. Information such as year, month, day, hour, minute, and second. With all that information, we can use a datetime object to refer to one particular moment in time. For example:

import datetime

today = datetime.datetime(year=2019, month=12, day=23, hour=11, minute=49, second=30)
print(today)  # 2019-12-23 11:49:30

In addition to storing data about the specific point in time, it also has methods that help us interact with or process that data in a way that makes sense.

For example, given two datetime objects you can compare them to see which one is further into the future.

import datetime

today = datetime.datetime(year=2019, month=12, day=23, hour=11, minute=49, second=30)
tomorrow = datetime.datetime(year=2019, month=12, day=24, hour=11, minute=49, second=30)
print(today > tomorrow)  # False

Here we would print False because today is in the past, relative to tomorrow. This is how we can compare two dates, for example to tell whether something happened in the past.

How to get today's date

Because getting today's date is so common, the datetime class comes with a method you can use to get a new datetime object for today's date:

import datetime

print(datetime.datetime.now())
# 2019-12-23 11:54:13.151509

You can also use datetime.datetime.today() to get the current time and date, but it can sometimes be less precise. Also, it does now allow us to give it timezone information (more on that later!)

Notice that when we're doing this, we get microseconds as well as the other time measures. This can be unnecessary, but when dealing with computers it can sometimes be useful.

How to modify dates

You can't add two dates together, as that seldom makes sense. For example, what should happen if you add "today" to "today"?

import datetime

print(datetime.datetime.now() + datetime.datetime.now())
# Error

Instead, when you want to change a date—for example by adding a few days to it—we use the timedelta class. A "delta" in mathematics means a "change", so that's where the name comes from.

import datetime

today = datetime.datetime.now()
one_week = datetime.timedelta(days=7)

print(today + one_week) # 2019-12-30 11:58:52.073407

You can use timedelta with these arguments:

  • days
  • seconds
  • microseconds
  • milliseconds
  • minutes
  • hours
  • weeks

But the object itself will only store days, seconds, and microseconds. All other arguments will be converted to those (e.g. minutes * 60 will be added to seconds).

How to display dates

You can print dates or convert them to strings by using the built-in str() function, so that they'll be shown in this format:

import datetime

print(datetime.datetime.now())
# 2019-12-23 11:54:13.151509

Sometimes you may want more flexibility regarding how you print it out. Maybe you only want to print out the date portion. Or maybe, just "hours and minutes".

You can do this by using .strftime(), which stands for "string format time".

import datetime

today = datetime.datetime.now()

print(today.strftime("%Y-%m-%d")
# 2019-12-23

print(today.strftime("%H:%M")
# 11:54

Doing this does not modify the today object at all, it just creates a string representing the date or time as dictated by the "format string" passed.

This is a good reference for all different things you can pass to strftime: [https://strftime.org/](https://strftime.org/).

How to parse dates

Very similarly to printing dates with a specific format, we can read in dates with a specific format.

For example, let's say your user gives you a string describing today's date: 23-12-2019. Clearly this string is in the format "day-month-year". In Python datetime format: "%d-%m-%Y".

We can use .strptime to parse a date string into a datetime object:

import datetime

user_date = input("Enter today's date: ")
# Assume they entered 23-12-2019

today = datetime.datetime.strptime(user_date, "%d-%m-%Y")
print(today)  # 2019-12-23 00:00:00

The same format strings as we used for strftime can be used in strptime

What is a timestamp?

A timestamp is the number of seconds that have passed since 1st January, 1970, at midnight.

We use timestamps because it is easier to work with a single number (albeit large) than with many numbers each describing a different measurement.

The timestamp for "2nd January, 1970, at midnight" would be 86400: the number of seconds in one day.

To get a timestamp you can call the timestamp() method of any datetime object:

import datetime

today = datetime.datetime.now()
print(today.timestamp())  # 1577104357.558527

Because it is so common to get the timestamp of right now, we can use the time module to get it more easily:

import time

print(time.time())  # 1577104492.7515678

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 -06:00.

We call that the "offset", and it is usually placed after a date and time. For example:

2019-12-23 11:54:13+01:00

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 datetime.timezone.utc to .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 is 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 +00:00 offset.

Naive vs. aware datetimes

If your 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 datetime objects.

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 or using pip install pytz.

Getting a timezone from pytz

The pytz module comes with a whole database of timezones, accessible by their official names:

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 datetime object.

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 pytz:

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 eastern_time and 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

The 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.

An example of this shown below

Working with dates and times in software applications

Usually I would recommend to always work in UTC in your applications. Ask the users for local time and timezone, and convert it to UTC. 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.

The pytz module comes with a pytz.utc timezone that you can use when constructing datetime objects in order to simplify this:

import datetime
import pytz

eastern = pytz.timezone("US/Eastern")

user_date = datetime.datetime(2020, 4, 15, 6, 0, 0, tzinfo=eastern)
utc_date = user_date.astimezone(pytz.utc)
print(utc_date)  # 2020-04-15 10:56: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!

Wrapping Up

Thank you for reading this blog post on working with dates and times in Python using datetime and pytz. I hope it's been useful!

If you want to learn more about Python as a whole, check out our Complete Python Course. It's a 30-hour video-course that takes you from beginner to expert in Python. We'd love to see you there!