6 Python datetime libraries

There are a host of libraries that make it simpler to test, convert, and read date and time information in Python.
232 readers like this.
Hands on a keyboard with a Python book

WOCinTech Chat. Modified by Opensource.com. CC BY-SA 4.0

Once upon a time, one of us (Lacey) had spent more than an hour staring at the table in the Python docs that describes date and time formatting strings. I was having a hard time understanding one specific piece of the puzzle as I was trying to write the code to translate a datetime string from an API into a Python datetime object, so I asked for help.

"Why don't you just use dateutil?" someone asked.

Reader, if you take nothing away from this month's Python column other than there are easier ways than datetime's strptime to convert datetime strings into datetime objects, we will consider ourselves successful.

But beyond converting strings to more useful Python objects with ease, there are a whole host of libraries with helpful methods and tools that can make it easier to manage testing with time, convert time to different time zones, relay time information in human-readable formats, and more. If this is your first foray into dates and times in Python, take a break and read How to work with dates and time with Python. To understand why dealing with dates and times in programming is hard, read Falsehoods programmers believe about time.

This article will introduce you to:

Feel free to skip the ones you're already familiar with and focus on the libraries that are new to you.

The built-in datetime module

Before jumping into other libraries, let's review how we might convert a date string to a Python datetime object using the datetime module.

Say we receive this date string from an API and need it to exist as a Python datetime object:

2018-04-29T17:45:25Z

This string includes:

  • The date in YYYY-MM-DD format
  • The letter "T" to indicate that a time is coming
  • The time in HH:II:SS format
  • A time zone designator "Z," which indicates this time is in UTC (read more about datetime string formatting)

To convert this string to a Python datetime object using the datetime module, you would start with strptime. datetime.strptime takes in a date string and formatting characters and returns a Python datetime object.

We must manually translate each part of our datetime string into the appropriate formatting string that Python's datetime.strptime can understand. The four-digit year is represented by %Y. The two-digit month is %m. The two-digit day is %d. Hours in a 24-hour clock are %H, and zero-padded minutes are %M. Zero-padded seconds are %S.

Much squinting at the table in the documentation is required to reach these conclusions.

Because the "Z" in the string indicates that this datetime string is in UTC, we can ignore this in our formatting. (Right now, we won't worry about time zones.)

The code for this conversion would look like this:

$ from datetime import datetime 
$ datetime.strptime('2018-04-29T17:45:25Z', '%Y-%m-%dT%H:%M:%SZ')
datetime.datetime(2018, 4, 29, 17, 45, 25)

The formatting string is hard to read and understand. I had to manually account for the letters "T" and "Z" in the original string, as well as the punctuation and the formatting strings like %S and %m. Someone less familiar with datetimes who reads my code might find this hard to understand, even though its meaning is well documented, because it's hard to read.

Let's look at how other libraries handle this kind of conversion.

Dateutil

The dateutil module provides extensions to the datetime module.

To continue with our parsing example above, achieving the same result with dateutil is much simpler:

$ from dateutil.parser import parse
$ parse('2018-04-29T17:45:25Z')
datetime.datetime(2018, 4, 29, 17, 45, 25, tzinfo=tzutc())

The dateutil parser will automatically return the string's time zone if it's included. Since ours was in UTC, you can see that the datetime object returned that. If you want parse to ignore time zone information entirely and return a naive datetime object, you can pass the parameter ignoretz=True to parse like so:

$ from dateutil.parser import parse
$ parse('2018-04-29T17:45:25Z', ignoretz=True)
datetime.datetime(2018, 4, 29, 17, 45, 25)

Dateutil can also parse more human-readable date strings:

$ parse('April 29th, 2018 at 5:45 pm')
datetime.datetime(2018, 4, 29, 17, 45)

dateutil also offers tools like relativedelta for calculating the time difference between two datetimes or adding/removing time to/from a datetime, rrule for creating recurring datetimes, and tz for dealing with time zones, among other tools.

Arrow

Arrow is another library with the goal of making manipulating, formatting, and otherwise dealing with dates and times friendlier to humans. It includes dateutil and, according to its docs, aims to "help you work with dates and times with fewer imports and a lot less code."

To return to our parsing example, here is how you would use Arrow to convert a date string to an instance of Arrow's datetime class:

$ import arrow 
$ arrow.get('2018-04-29T17:45:25Z')
<Arrow [2018-04-29T17:45:25+00:00]>

You can also specify the format in a second argument to get(), just like with strptime, but Arrow will do its best to parse the string you give it on its own. get() returns an instance of Arrow's datetime class. To use Arrow to get a Python datetime object, chain datetime as follows:

$ arrow.get('2018-04-29T17:45:25Z').datetime
datetime.datetime(2018, 4, 29, 17, 45, 25, tzinfo=tzutc())

With the instance of the Arrow datetime class, you have access to Arrow's other helpful methods. For example, its humanize() method translates datetimes into human-readable phrases, like so:

$ import arrow
$ utc = arrow.utcnow()
$ utc.humanize()
'seconds ago'

Read more about Arrow's useful methods in its documentation.

Moment

Moment's creator considers it "alpha quality," but even though it's in early stages, it is well-liked and we wanted to mention it.

Moment's method for converting a string to something more useful is simple, similar to the previous libraries we've mentioned:

$ import moment
$ moment.date('2018-04-29T17:45:25Z')
<Moment(2018-04-29T17:45:25)>

Like other libraries, it initially returns an instance of its own datetime class. To return a Python datetime object, add another date() call.

$ moment.date('2018-04-29T17:45:25Z').date
datetime.datetime(2018, 4, 29, 17, 45, 25, tzinfo=<StaticTzInfo 'Z'>)

This will convert the Moment datetime class to a Python datetime object.

Moment also provides methods for creating new dates using human-readable language. To create a date for tomorrow:

$ moment.date("tomorrow")
<Moment(2018-04-06T11:24:42)>

Its add and subtract commands take keyword arguments to make manipulating your dates simple, as well. To get the day after tomorrow, Moment would use this code:

$ moment.date("tomorrow").add(days=1)
<Moment(2018-04-07T11:26:48)>

Maya

Maya includes other popular libraries that deal with datetimes in Python, including Humanize, pytz, and pendulum, among others. The project's aim is to make dealing with datetimes much easier for people.

Maya's README includes several useful examples. Here is how to use Maya to reproduce the parsing example from before:

$ import maya
$ maya.parse('2018-04-29T17:45:25Z').datetime()
datetime.datetime(2018, 4, 29, 17, 45, 25, tzinfo=<UTC>)

Note that we have to call .datetime() after maya.parse(). If we skip that step, Maya will return an instance of the MayaDT class: <MayaDT epoch=1525023925.0>.

Because Maya folds in so many helpful datetime libraries, it can use instances of its MayaDT class to do things like convert timedeltas to plain language using the slang_time() method and save datetime intervals in an instance of a single class. Here is how to use Maya to represent a datetime as a human-readable phrase:

$ import maya
$ maya.parse('2018-04-29T17:45:25Z').slang_time()
'23 days from now`

Obviously, the output from slang_time() will change depending on how relatively close or far away you are from your datetime object.

Delorean

Delorean, named for the time-traveling car in the Back to the Future movies, is particularly helpful for manipulating datetimes: converting datetimes to other time zones and adding or subtracting time.

Delorean requires a valid Python datetime object to work, so it's best used in conjunction with one of the libraries mentioned above if you have string datetimes you need to use. To use Delorean with Maya, for example:

$ import maya 
$ d_t = maya.parse('2018-04-29T17:45:25Z').datetime()

Now, with the datetime object d_t at your disposal, you can do things with Delorean like convert the datetime to the U.S. Eastern time zone:

$ from delorean import Delorean
$ d = Delorean(d_t)
$ d
Delorean(datetime=datetime.datetime(2018, 4, 29, 17, 45, 25), timezone='UTC')
$ d.shift('US/Eastern')
Delorean(datetime=datetime.datetime(2018, 4, 29, 13, 45, 25), timezone='US/Eastern')

See how the hours changed from 17 to 13?

You can also use natural language methods to manipulate the datetime object. To get the next Friday following April 29, 2018 (the date we've been using):

$ d.next_friday()
Delorean(datetime=datetime.datetime(2018, 5, 4, 13, 45, 25), timezone='US/Eastern')

Read more about Delorean in its documentation.

Freezegun

Freezegun is a library that helps you test with specific datetimes in your Python code. Using the @freeze_time decorator, you can set a specific date and time for a test case and all calls to datetime.datetime.now(), datetime.datetime.utcnow(), etc. will return the date and time you specified. For example:

from freezegun import freeze_time
import datetime

@freeze_time("2017-04-14")
def test():
    assert datetime.datetime.now() == datetime.datetime(2017, 4, 14)

To test across time zones, you can pass a tz_offset argument to the decorator. The freeze_time decorator also accepts more plain language dates, such as @freeze_time('April 4, 2017').


Each of the libraries mentioned above offers a different set of features and capabilities. It might be difficult to decide which one best suits your needs. Maya's creator, Kenneth Reitz, says, "All these projects complement each other and are friends."

These libraries share some features, but not others. Some are good at time manipulation, others excel at parsing. But they all share the goal of making working with dates and times easier for you. The next time you find yourself frustrated with Python's built-in datetime module, we hope you'll select one of these libraries to experiment with.

User profile image.
Lacey Williams Henschel is a software engineer with REVSYS and part of the organizing team for DjangoCon US. In the past, she's chaired DjangoCon US, organized several Django Girls workshops, taught courses for Treehouse, and written about accessibility at tech events.
User profile image.
Jeff Triplett is an open source developer and works as a software engineer, consultant, and partner at REVSYS. He is a Board of Director at The Python Software Foundation. He occasionally blogs on his website.

3 Comments

Here's a little datetime method I used to get feedback on how long a script was running -- at the time I had a script going through some large text files, and creating Scribus documents. Some took longer then an hour to run.
Early in the script I put:
start = datetime.datetime.now().replace(microsecond=0)

then near the end:
finish = datetime.datetime.now().replace(microsecond=0)
interval = finish - start
scribus.messageBox("Interval",'This took ' + str(interval) + ' to finish',button1=scribus.BUTTON_OK)

It was very useful for finding modifications that speeded up the script.

Creative Commons LicenseThis work is licensed under a Creative Commons Attribution-Share Alike 4.0 International License.