It’s time to talk about dates and times in Python. This area of programming is used extensively in some applications. However, there are other applications in which you’ll never need to use dates and times. Data points often include a date and time component, and this is often an essential part of the data in many real-world applications.
There are several reasons why dealing with dates and times in Python is not as straightforward as you might think. Here are some of those reasons:
- The way we group days and times is not the most straightforward. Days of the week repeat every seven days, but the number of days in a month doesn’t follow any easily quantifiable rule. We don’t even get a whole number of weeks in a year as 365 is not divisible by 7. And then there are leap years.
- In the real world, we use many different formats to deal with dates and times. The first day of September of the year two thousand and twenty-one is the same as 01/09/2021 or indeed 09/01/2021, depending on which part of the world you’re in. Or you can use just 21 to represent the year. This date is the same as 1-Sep-21 or September 1, 2021. There are many more options, too.
- And if this isn’t enough to cause headaches, you can add time zones to the mix. 10:00 am on September 1, 2021 in Greenwich Mean Time/Coordinated Universal Time (GMT/UTC) is not the same as 10:00 am on September 1, 2021 in Eastern Standard Time (EST). And yes, there’s also the switch to daylight saving time in many time zones to consider.
Luckily, there are tools you can use to deal with dates and times in Python without having to work out all these quirks about dates and times from first principles.
datetime to Deal With Dates and Times in Python
There’s more than one module that allows you to deal with dates and times in Python. You’ll learn about the most standard of these modules in this Chapter. This module is the built-in
datetime module that you can import into your program using the
import keyword in the usual manner.
Let’s start using the
datetime module and introduce the two key data types that you’ll need to get started:
One of the data types is called
datetime which is part of the
datetime module! This naming can cause some confusion. Remember that in
datetime.datetime, the first
datetime is the module name while the second is the name of the data type.
The best way to introduce
datetime.datetime is through an example. Note that you’re running the examples below in a Python console (interactive environment). The output will look different when you run similar code in a script. You’ll learn why this happens later on in this Chapter:
>>> import datetime >>> time_now = datetime.datetime.now() >>> time_now datetime.datetime(2021, 9, 20, 21, 40, 3, 58773) >>> type(time_now) <class 'datetime.datetime'>
You’ve used the method
datetime.datetime to get the current date and time and assign it to the variable name
time_now. Of course, you can call this variable anything you wish. When you display the value of this variable, you can see numbers that represent the date and time when I ran this code. The numbers shown are the year, month, day, hour, minute, second, and microsecond, in this order.
The output shows that the time was 9:40:03 pm (21:40:03) on the 20th of September, 2021 (20/9/2021, or 9/20/2021 in the US time format). You can even see the microsecond listed. There may be times when you need this level of precision!
datetime.datetime data type is Python’s way of understanding and dealing with dates and times. You can explore this further by looking at some of the attributes of an object of type
datetime.datetime. This Console session carries on from the one you started earlier:
>>> time_now.year 2021 >>> time_now.month 9 >>> time_now.day 20 >>> time_now.weekday() 0 >>> time_now.isoweekday() 1 >>> time_now.isoformat() '2021-09-20T21:40:03.058773'
The first three attributes contain the integers representing the year, month, and day of the
datetime.datetime object. There are also attributes containing the hour, minute, second, and microsecond values. These are the same values included in the output when you displayed
The examples above also show some of the methods you can use with
weekday() method returns
0 as the 20th of September 2021 was a Monday. The days Monday to Sunday are represented by the numbers
6. There’s also an alternative version of this method which adheres to the date and time international standard ISO 8601. This method is
isoweekday() which returns
1 for Monday and
7 for Sunday.
You can also display the date and time in the ISO standard using
isoformat(). If you’re likely to use dates and times extensively or you want to learn about the standard formats, you can read more about the ISO 8601 international standard.
You can now create two variables containing two different
datetime.datetime objects and compare them:
>>> first_time = datetime.datetime.now() >>> # Wait a minute, literally... >>> second_time = datetime.datetime.now() >>> first_time datetime.datetime(2021, 9, 20, 22, 13, 13, 810414) >>> second_time datetime.datetime(2021, 9, 20, 22, 14, 14, 112847) >>> gap = second_time - first_time >>> gap datetime.timedelta(seconds=60, microseconds=302433) >>> type(gap) <class 'datetime.timedelta'> >>> gap.seconds 60 >>> gap.total_seconds() 60.302433 >>> second_time > first_time True
In the example above, I assigned the date and time to a variable called
first_time, then waited a minute before assigning the date and time to a second variable named
second_time. I couldn’t get the timing perfectly right, so the difference between the two times is not exactly 60 seconds!
When you subtract one
datetime.datetime object from another, the value returned is an object of type
datetime.timedelta. In this example, the program shows the gap between the two dates and times using the number of seconds and microseconds. You’ll see later than for larger gaps, the number of days will also be displayed.
There are attributes that allow you to extract just one of the relevant values. In the example above, you use
gap.seconds to show the whole number of seconds included in the
datetime.timedelta. However, this value is not the total number of seconds that have elapsed. The method
total_seconds() returns this value.
You can also compare two
datetime.datetime objects. This operation doesn’t give a
datetime.timedelta but instead returns a Boolean, as is usually the case when using comparison operators. In the context of comparing dates and times, the “greater than” operator represents “later than”, whereas the “less than” operator means “earlier than”.
Converting Dates and Times in Python To and From Strings
You’ve learned about the two main data types in the
datetime module that allow you to deal with dates and times in Python. However, so far, you’ve only created
datetime.datetime objects using the
now() method. This method gives you the date and time at the time of execution. This is useful sometimes. However, in many applications, you’ll need to deal with other dates.
To deal with dates and times in Python from the past, present, or future, you’ll need two other key methods associated with the
datetime.datetime class. These methods, quite possibly, win the award for the worst named methods in all of Python’s standard library:
You can use these methods to convert a string containing a date and time into a
datetime.datetime object, and the other way round. Let’s look at each of these methods and find a way of remembering which one is which!
strptime() method converts a string containing a date and time into a
datetime.datetime object. You can remember this using the p between str and time in the method name, which refers to parsing a string into a date and time. The method name only refers to time, but as you’re using the
datetime module, this method deals with both dates and times.
The problem is that there are many different formats used to refer to dates and times. Let’s start with the following date, which I’m showing in three different formats that are commonly used around the world:
- 20/02/1991 (using dd/mm/yyyy)
- 20 February, 1991
No, this is not my birthday. It’s Python’s birthday. This is the date the very first version of the language was released.
You can create three variables to represent this date as a string in its three different formats:
>>> date_v1 = "20/02/1991" >>> date_v2 = "1991-02-20" >>> date_v3 = "20 February, 1991"
Before you can convert these strings into
datetime.datetime objects, you’ll need a way to let the
datetime module know which date format you’re using. There are format codes you can use that represent all the possible ways of displaying years, months, and days. There are also codes to refer to time data. However, these examples don’t include a time. Therefore, you can focus just on dates in these examples.
Here’s a selection of the format codes you’ll need for these examples, including a couple of additional ones. You can find the complete list of format codes in the
datetime module documentation:
|Day of the month using two digits||03, 22, 31|
|Month represented as a number using two digits||01, 10, 12|
|Month represented as an abbreviated name (language will depend on your locale setting)||Jan, Oct, Dec|
|Month represented as a full name (language will depend on your locale setting)||January, October, December|
|Year represented using two digits, without the century||91, 21, 01|
|Year represented using four digits||1991, 2021, 2001|
Let’s see how you can use these codes to input the date format you’re using into
>>> date_v1 = "20/02/1991" >>> datetime.datetime.strptime(date_v1, "%d/%m/%Y") datetime.datetime(1991, 2, 20, 0, 0)
strptime() is a class method that’s bound to the class rather than to individual instances of the class. You haven’t read about class methods, but you don’t need to know more at this stage. The second line is quite a mouthful. Let’s break it down to review what you’ve covered so far:
- The first
datetime.datetimeis the name of the module which you import
- The second
datetimeis the name of the class that creates objects. The objects are of type
datetimethat’s in the module
datetime. It would have been clearer if the module and class names were different, but unfortunately, they’re not
strptime()is a method of the
strptime() method takes two arguments:
- a string containing the date (or date and time)
- a string containing the format which uses the format codes
The date you’re using has the day, month, and four-digit year separated by the forward-slash
/, and therefore the format code which represents this date is the string
"%d/%m/%Y". Note that a capital Y is used to represent the four-digit year as the lowercase version refers to a two-digit year representation. You can refer to the table above describing the format codes or the full table of format codes in the documentation.
strptime() returns a
datetime.datetime object showing the year, month, and day. The values for second and microsecond are
0, as the string you used did not include any time information.
You can now try and convert the remaining two strings into
datetime.datetime objects. Once you’ve tried this, you can read on…
Although the three strings shown above are different strings, they represent the same date. You can confirm this by checking for equality after you convert to
>>> date_v1 = "20/02/1991" >>> date_v1_as_dt = datetime.datetime.strptime(date_v1, "%d/%m/%Y") >>> date_v1_as_dt datetime.datetime(1991, 2, 20, 0, 0) >>> date_v2 = "1991-02-20" >>> date_v2_as_dt = datetime.datetime.strptime(date_v2, "%Y-%m-%d") >>> date_v2_as_dt datetime.datetime(1991, 2, 20, 0, 0) >>> date_v3 = "20 February, 1991" >>> date_v3_as_dt = datetime.datetime.strptime(date_v3, "%d %B, %Y") >>> date_v3_as_dt datetime.datetime(1991, 2, 20, 0, 0) >>> # Compare the three strings >>> date_v1 == date_v2 == date_v3 False >>> # Compare the three datetime.datetime objects >>> date_v1_as_dt == date_v2_as_dt == date_v3_as_dt True
In the first three blocks of code above, you’re converting the three strings to
datetime.datetime objects using
strptime() and the correct format codes for each string. In the third case, you include a space between
%B and a comma and a space between
The strings are different, and therefore, you get
False when you check the three strings for equality. Python doesn’t know yet that these strings represent dates. It’s just checking whether the characters in the strings are identical. They’re not.
However, the equality check for the three
datetime.datetime objects returns
True. These are indeed the same dates.
The second of the “two worst named methods in Python” is
strftime(). This method performs the reverse of the method you’ve just learned about. The f in the method name shows that you’re obtaining a string from a date and time object. This method is an instance method. Therefore, it’s bound to an instance of the class and has access to the data contained within that instance. These are the methods you learned about in Chapter 7 on object-oriented programming in Python.
You can create a date and time object directly by creating an instance of the
>>> new_date = datetime.datetime(year=2008, month=12, day=3) >>> new_date datetime.datetime(2008, 12, 3, 0, 0)
As you learned in the earlier Chapter about OOP, this is the direct way to create an instance of a class. Recall that the second
datetime.datetime is the name of a class, even though it doesn’t follow the naming convention of using upper camel case. When you create an instance of a class, you’re calling the
__init__() method for this class. In this case, you’re passing three named arguments to the
__init__() method, which creates an instance of the
The date used in this example is when Python 3.0 was released.
You can convert this
datetime.datetime object into a string using
strftime(). You can choose to create a string using any format you wish:
>>> new_date.strftime("%Y-%m-%d") '2008-12-03' >>> new_date.strftime("Python 3.0 was released in %B %Y") 'Python 3.0 was released in December 2008'
strftime() only requires one explicit argument, which is the string containing the format code. The method also has access to the object
new_date itself. The full signature of this method is
self is the implied first argument, as is the case for all instance methods.
If you wish, you can also use
strftime() using the same style as
>>> datetime.datetime.strftime(new_date, "%Y-%m-%d") '2008-12-03'
In this case, you need two arguments. The first is the
datetime.datetime object and the second the format code. However, the OOP form you used earlier is clearer and more concise, so it’s often the preferred option.
You can now easily find out the time elapsed between the release of the first version of Python and Python 3.0. You can also find how old Python is today:
>>> new_date - date_v1_as_dt datetime.timedelta(days=6496) >>> datetime.datetime.now() - date_v1_as_dt datetime.timedelta(days=11171, seconds=40363, microseconds=426860)
In both of these examples, you’re subtracting one
datetime.datetime object from another. As you’ve seen earlier, this operation returns a
datetime.timedelta which can include days, seconds, and microseconds.
What Day of The Week Were You Born?
You can now write a short program to find what day of the week you were born. The steps you’ll need are:
- Choose a format you’d like to use for the date
- Input the date in that format as a string
- Convert the string to a
- Either use the
isoweekday()methods to get the day of the week, or use
strftime()to get the name of the weekday straight away
I will use the dd/mm/yyyy format in this example which is represented by the format code string
"%d/%m/%Y". You can use
input() to ask the user to enter the date of birth:
# find_day_of_birth.py import datetime format = "%d/%m/%Y" dob = input("Enter your date of birth (dd/mm/yyyy): ") dob_as_dt = datetime.datetime.strptime(dob, format) print(dob_as_dt)
When you run this script, you’re asked to enter a date of birth. I’ll use Python’s date of birth again in the example below:
Enter your date of birth (dd/mm/yyyy): 20/02/1991 1991-02-20 00:00:00
You’ll have noticed that you’re using a script for this example rather than the Python Console. Therefore, you’ve had to use
print() to display the
datetime.datetime object. The output from
print() shows the date and time but it’s displayed differently from when you displayed
datetime.datetime objects in the Console. In the Snippets section at the end of this Chapter, you can read about the different ways to display an object. The built-in functions
repr() return the object’s string representation and object representation, respectively.
Displaying dates and times in Python using
For this section, a short explanation will be sufficient. You can look in the Snippets section for more details.
In this case,
print() returns the string representation of a
datetime.datetime object, which displays the date and time in a form that the user of the program easily understands. This is the same output returned by
str(). The date and time format displayed when using
str() is the ISO international standard format you have already come across.
When you display the value of an object in the Python Console or other interactive environments, Python displays the object representation. This output is the value returned by
repr(), and it’s a string showing information that’s specific to the Python object. It’s aimed at the programmer and not the user of a program.
Let’s compare the two representations in a Console session using the same
new_date variable you used earlier, which is Python 3.0’s date of birth:
>>> new_date = datetime.datetime(year=2008, month=12, day=3) >>> str(new_date) 2008-12-03 00:00:00 >>> repr(new_date) 'datetime.datetime(2008, 12, 3, 0, 0)'
When you convert
new_date into a string using
str(), the function returns the ISO standard format. Note that this string has no reference to Python data types. It’s a format that everyone can understand as long as they’re familiar with the standard format for dates and times. They don’t need to be familiar with Python.
repr() returns the object representation. This representation gives you information about the Python data type, which
str() didn’t do. When you use
repr(), you can see that you have a
datetime.datetime object. Indeed,
repr() usually returns a string that shows valid Python syntax, and you can copy and paste this to recreate the object. This information is intended for a programmer who understands Python and not for the user of a program.
print() returns the same output as
str() in this case. However, when you type the name of an object in the Console and hit Return/Enter, Python displays the value returned by
repr(). You can read more about these two types of representations,
repr(), and the special methods
__repr__() in the Snippets section at the end of this Chapter.
Completing the day-you-were-born program
Before adding the final steps to the program to find the day of the week you were born, try rerunning the script and input the date in the wrong format to see what happens:
Enter your date of birth (dd/mm/yyyy): 1991-2-20 Traceback (most recent call last): ... ValueError: time data '1991-2-20' does not match format '%d/%m/%Y'
The string you input doesn’t have the same format as the one defined in
strptime(). This input raises a
The final step in your program is to find the weekday that is associated with the
datetime.datetime object. The first option you have is to use one of two methods you’ve seen already:
isoweekday(). The only difference between the two methods is how the days are labelled. I’ll use
isoweekday() since it uses the integers
7 rather than
6 to represent the days of the week:
# find_day_of_birth.py import datetime format = "%d/%m/%Y" dob = input("Enter your date of birth (dd/mm/yyyy): ") dob_as_dt = datetime.datetime.strptime(dob, format) print(dob_as_dt.isoweekday())
This code gives the following output:
Enter your date of birth (dd/mm/yyyy): 20/02/1991 3
Python was born on a Wednesday since
1 represents Monday in the ISO format, and therefore,
3 represents Wednesday.
You can also use
strftime() to display the weekday directly, using the format codes shown in the documentation for
datetime. You’ll find the code
%A close to the top of the table presented in the documentation. This format code represents the full name of the weekday:
# find_day_of_birth.py import datetime format = "%d/%m/%Y" dob = input("Enter your date of birth (dd/mm/yyyy): ") dob_as_dt = datetime.datetime.strptime(dob, format) print(dob_as_dt.isoweekday()) print(dob_as_dt.strftime("%A"))
You’re now converting the
datetime.datetime object into a string, but you’re choosing to display only the weekday name. The output from this code is the following:
Enter your date of birth (dd/mm/yyyy): 20/02/1991 3 Wednesday
This output confirms that Python was indeed born on a Wednesday!
Dealing With Time Zones
Let’s go back to the very first example you used to create a
>>> import datetime >>> time_now = datetime.datetime.now() >>> time_now datetime.datetime(2021, 9, 21, 12, 36, 9, 58234)
As you can see, I was writing this part of the Chapter on the 21st of September, 2021, at 12:36 pm. But what time zone was I in? There are times when we don’t need to know information about time zones. In the
datetime module, dates and times that don’t include information about the time zone are called naive objects.
now() returns a naive object. Therefore, it’s not possible to know what time zone I was in when I created the
Date and time objects can also be aware. Aware date and time objects have information about the time zone. In the example below, you’ll first create an offset with respect to GMT/UTC. The offset is a time difference and therefore is of type
>>> offset = datetime.timedelta(hours=1) >>> offset datetime.timedelta(seconds=3600) >>> time_now = datetime.datetime.now(datetime.timezone(offset)) >>> time_now datetime.datetime(2021, 9, 21, 12, 48, 42, 220369, tzinfo=datetime.timezone(datetime.timedelta(seconds=3600)))
As I write, I’m in the British Summer Time (BST) time zone which is one hour later than GMT/UTC, or
+01:00 in ISO standard format. In this case, there is an argument passed to
now(). This is an object of type
datetime.timezone which is defined using the offset with respect to GMT/UTC.
When you display the
datetime.datetime object returned by the method
now(), you can see an additional term with the name
tzinfo. This term shows that the date and time object is aware as it has information about the time zone.
This is good news if you want to make sure your dates and times are aware of the time zone. However, defining time zones in this manner is not straightforward. You’ll still need to know what the offsets from GMT/UTC are, and consider whether daylight saving applies, for example.
There are many ways you can deal with time zones in Python. In this section, I won’t try to outline each and every option. Instead, I’ll introduce one of the newest additions to Python. The
zoneinfo module was introduced to the language in Python 3.9.
zoneinfo module does as its name implies. It provides information about specific time zones. The module provides a class called
ZoneInfo which you can use to create objects that can provide the time zone information required to make a date and time object aware.
Unlike the case with
datetime.datetime where the module name and class name are identical, the
zoneinfo module follows the Python naming convention: the class name uses upper camel case while the module name is in lowercase. This makes things a bit clearer when writing
Here’s an example. You’ll also need to import
zoneinfo in addition to
# using_time_zones.py import datetime import zoneinfo london_now = datetime.datetime.now(zoneinfo.ZoneInfo("Europe/London")) print("London time zone") print(london_now) print(repr(london_now))
You’ve used both
print(london_now) which shows the string representation of the date and time, and
print(repr(london_now)) which shows the Python-specific object representation. This code gives the following output, although you’ll get a different value when you run this:
London time zone 2021-09-21 13:07:34.746661+01:00 datetime.datetime(2021, 9, 21, 13, 7, 34, 746661, tzinfo=zoneinfo.ZoneInfo(key='Europe/London'))
The string representation shows the time in ISO format. You can see the
+01:00 at the end showing the offset with respect to GMT/UTC. This offset is consistent with London’s time zone on September 21 which is BST, one hour later than GMT/UTC.
The object representation shows the time zone info indicating that this date and time object is aware.
Let’s finish off by using
now() to find the time in two different time zones, and then finding the difference between those two times:
# using_time_zones.py import datetime import zoneinfo london_now = datetime.datetime.now(zoneinfo.ZoneInfo("Europe/London")) new_york_now = datetime.datetime.now(zoneinfo.ZoneInfo("America/New_York")) print("Print string and object representations of both datetimes") print("London time zone") print(london_now) print(repr(london_now)) print("\nNew York time zone") print(new_york_now) print(repr(new_york_now)) print("\nShow time difference between the two datetimes") print(new_york_now - london_now)
now() method returns the current time but in two different time zones. For clarity, you can print out both the string and object representations. Finally, you can find the difference between the two times. The output of this code when I ran it gives:
Print string and object representations of both datetimes London time zone 2021-09-21 13:16:26.738785+01:00 datetime.datetime(2021, 9, 21, 13, 16, 26, 738785, tzinfo=zoneinfo.ZoneInfo(key='Europe/London')) New York time zone 2021-09-21 08:16:26.740564-04:00 datetime.datetime(2021, 9, 21, 8, 16, 26, 740564, tzinfo=zoneinfo.ZoneInfo(key='America/New_York')) Show time difference between the two datetimes 0:00:00.001779
It was 1:16 pm (13:16) in London and 8:16 am (08:16) in New York when I ran this script. You can see the GMT/UTC offsets for both time zones in the ISO representation of the dates and times:
+01:00 for London and
-04:00 for New York.
Even though the times are different–it’s 1:16 pm in London, but 8:16 am in New York–when the two times are subtracted from each other, the time difference is virtually zero. That’s because the program executed the
now() method twice at roughly the same time. Even though the date and time is displayed differently because they’re showing different time zones, both
datetime.datetime objects represent the same date and time, except for a few microseconds!
Time’s up for this Chapter. As with all topics in programming and Python, there’s a lot more we can say about dealing with dates and times in Python. However, trying to squeeze everything in at the first time of asking won’t do anyone any good.
This Chapter aimed to give you a solid understanding of how to start using dates and times in Python using the
There are other modules in Python that you can use to deal with dates and times. Some can be used as a replacement for
datetime, while others are an extension. However,
datetime is the starting point for any work with dates and times, and it’s a module that’s part of the standard library. In most cases, you won’t need to look any further, but if you do, you’ll find that many of the concepts you learned about in
datetime reoccur in other modules, too.
If you use modules such as
Pandas extensively, you’ll also find functionality directly within those packages to deal with dates and times.
In this Chapter, you’ve learned about:
- Creating date and time objects using
datetime.timedeltafor time intervals
- Parsing strings into date and time objects using
- Creating strings from date and time objects using
- Using time zones when working with dates and times in Python
In many applications where you’ll need to use dates and times, you’ll find you’ll follow similar steps. Once you read in data from an external source which includes dates and times, you’ll convert these strings into
datetime.datetime. This conversion means that Python understands these data as dates and times and not just as strings of characters. The
datetime module gives Python the context to make sense of dates and times.
Once you perform all the operations you need, you can finally convert your dates and times back into strings using any format required. You can now export these strings back to the outside world where Python is no longer needed.
You’re now ready to start exploring dates and times in Python.
1 | Different Representations of Objects: Using
Sign-Up For Updates
This site was launched in May 2021. I’ll be adding chapters regularly over the coming weeks and months.
Sign-up for updates and you’ll also join the Codetoday Forum where you can ask me questions as you go through this journey to learn Python coding.