When we develop applications, the problem we often face is a string to datetime conversion. We have to deal with different formats and time zones. Sometimes we need to convert dates and times to human-readable format, and vice versa.
Converting a string to datetime
Python comes with the datetime module that consists of a date, time, and datetime. This module comes with various functions for manipulating dates and time.
The following code is going to print the current date and time. The custom formatting is not applied, therefore it uses the default one.
from datetime import datetime print('Current date/time:', str(datetime.now())) print('Current date:', str(datetime.now().date())) print('Current time:', str(datetime.now().time()))
This code will display the current date + time, date, and time using the default formatting. Date and time can be accessed using the date and time functions.
Current date/time: 2020-04-29 11:16:03.562353 Current date: 2020-04-29 Current time: 11:16:03.562353
This module uses ISO 8601. This is the international standard for exchanging dates and time-related data.
The strptime function
Now, instead of displaying the current date, we are going to use a date from a string and try to convert it to a datetime object. To do it, we will use the strptime function.
This function is going to convert string to a datetime object. In the second parameter, you have to apply the datetime formatting, because the module can’t figure it out on its own.
from datetime import datetime datetime_string = '2019-03-29 10:26:06.562353' datetime_object = datetime.strptime(datetime_string, '%Y-%m-%d %H:%M:%S.%f') print('Date and time:', datetime_object) print('Date:', datetime_object.date()) print('Time:', datetime_object.time())
Let’s take a look at the result:
Date and time: 2019-03-29 10:26:06.562353 Date: 2019-03-29 Time: 10:26:06.562353
The formatting has to be exactly the same as the values in the string. If you don’t add, for example, the fraction of the second, the program is going to return an error.
ValueError: unconverted data remains: .562353
If you want to get rid of the fractional part of a second, you can use the format function.
print('Time:', format(datetime_object.time(), '%H:%M:%S'))
It will print the same result, but this time without microseconds.
Let’s try something more complicated.
from datetime import datetime datetime_string = 'Jun 4 2020 2:34PM' datetime_object = datetime.strptime(datetime_string, '%b %d %Y %I:%M%p') print(datetime_object)
This time the date is written in a more complicated fashion. We don’t have only numbers, but also an abbreviated name of the month, and time that is written using the 12-hour clock.
If you run this code the date and time will be complicated in the standard fashion:
The formats we use to convert string to datetime consists of format tokens. Each token represents a different part of datetime, such a month, day, hour, etc.
The following table contains most of the commonly used tokens. These examples are for en_US.
|%a||Weekday as a word in an abbreviated form.||Sun, Mon, …, Sat|
|%A||Weekdays as a full word.||Sunday, Monday, …, Saturday|
|%w||Weekdays as a number. 0 is Sunday, 6 is Saturday.||0, 1, 2, 3, 4, 5, 6|
|%d||Day of the month as a zero-padded decimal number.||01, 02, 03, …, 29, 30, 31|
|%b||Month as a word in an abbreviated form.||Jan, Feb, …, Dec|
|%B||Month as a full word.||January, February, …, December|
|%m||Month as a zero-padded decimal number.||01, 02 …, 11, 12|
|%y||Year as a zero-padded two-digit number.||01, 02, …, 98, 99, 00|
|%Y||Year as a zero-padded four-digit number.||0001, …, 2020, …., 9999|
|%H||A zero-padded, 24-hour clock||01, 02, … , 23, 00|
|%I||A zero-padded, 12-hour clock||01, 02, … , 12|
|%p||AM and PM||AM, PM|
|%M||Minute as a zero-padded decimal number.||01, 02, … , 59|
|%S||Second as a zero-padded decimal number.||01, 02, … , 59|
|%f||Microsecond as a zero-padded decimal number.||000000, 000001, …, 999999|
|%z||UTC offset in the form ±HHMM[SS].||(empty), +0000, -0300, +1020|
|%Z||Time zone name.||(empty), UTC, IST, CST|
|%j||Day of the year as a zero-padded three-digit number.||001, 002, …, 366|
|%U||Week of the year as a zero-padded two-digit number.|
(Sunday as the first day of the week).
|00, 01, …, 53|
|%W||Week of the year as a zero-padded two-digit number|
(Monday as the first day of the week)
|00, 01, …, 53|
|%c||Locale’s appropriate date and time representation.||Wed Apr 29 22:30:00 2020|
|%x||Locale’s appropriate date representation.||04/29/20|
|%X||Locale’s appropriate time representation.||22:30|
|%%||A literal ‘%’ character.||%|
So far, we were dealing with naive datetime objects. Naive datetime objects don’t contain any timezone-related data.
If you want to check whether your object holds information about timezone, you can use the tzinfo parameter.
Because we are using the naive datetime object, the result of tzinfo is None.
The pytz module
The easy way to apply timezone to naive objects is by using the pytz module. It’s not present in the standard library, you have to install it.
After installation you can run this code:
from datetime import datetime from pytz import timezone datetime_string = '2019-03-29 10:26:06.562353' datetime_object = datetime.strptime(datetime_string, '%Y-%m-%d %H:%M:%S.%f') eastern = timezone('US/Eastern') timezone_datetime_object = eastern.localize(datetime_object) print(timezone_datetime_object) print(timezone_datetime_object.tzinfo)
The result shows date and timezone.
2019-03-29 10:26:06.562353-04:00 US/Eastern
Notice, that there is -04:00 at the end. This means, that the Eastern/US time is 4 hours earlier than the GMT timezone (Greenwich Mean Time).
You can change the timezone to a different one:
eastern = timezone('Europe/Warsaw') timezone_datetime_object = eastern.localize(datetime_object) print(timezone_datetime_object) print(timezone_datetime_object.tzinfo)
Now if you run the code, the timezone will change to +01:00.
2019-03-29 10:26:06.562353+01:00 Europe/Warsaw
You can use the datetime module for converting strings to datetime objects. The problem with this module is that you have to apply the formatting, which can be time-consuming.
Instead, you can use third-party libraries that make the process of string to datetime conversion much easier.
In Python, there is a huge amount of modules. I created a list of the most popular ones:
This module provides an extension to the datetime module.
You can install it using PIP.
pip install python-dateutil
With this module, you don’t have to apply formatting to the string, it will recognize tokens and automatically convert string to the datetime object.
from dateutil.parser import parse datetime_string = '2019-03-29 10:26:06.562353' datetime_object = parse(datetime_string) print('Date and time:', datetime_object) print('Date:', datetime_object.date()) print('Time:', datetime_object.time()) print('Timezone:', datetime_object.tzinfo)
Now, the code is cleaner and the result is the same as before.
Date and time: 2019-03-29 10:26:06.562353 Date: 2019-03-29 Time: 10:26:06.562353 Timezone: None
The string was recognized as a naive object. Which is correct because you didn’t add a zone number.
Write code like this with two hours added to GMT:
datetime_string = '2019-03-29 10:26:06.562353+02:00'
It will be recognized by dateutil, but the module will display it in a form that is not very attractive to the user:
Timezone: tzoffset(None, 7200)
Maya is another module to help you with parsing dates and time.
To install it with PIP use this command:
pip install maya
The code with maya looks like this:
import maya datetime_object = maya.parse('2019-03-29 10:26:06.562353+02:00') print('Date and time:', datetime_object) print('Date:', datetime_object.date)
After you run the code, you will see the following result:
Date and time: Fri, 29 Mar 2019 08:26:06 GMT Date: 2019-03-29
The date is returned as an attribute and not as a function as we saw with the previous module. There is no attribute time. If you want to display time, you have to display an hour, minute, and second separately.
Notice, that time is already converted to GMT, so instead of 10:26:06, you see 08:26:06.
With maya, you can easily add date and time to the object:
datetime_object = maya.parse('2019-03-29 10:26:06.562353').add(hours=3, months=1)
This code adds 1 month and 3 hours to the date.
Date and time: Mon, 29 Apr 2019 13:26:06 GMT
The arrow library aims to help you work with datetime objects by using fewer imports and less code. It offers a human-friendly approach to creating, manipulating, formatting, and converting dates.
You can install it using this command:
pip install arrow
Here is an example of how a program looks like:
import arrow datetime_object = arrow.get('2019-03-29 10:26:06.562353') print('Date and time:', datetime_object.shift(hours=-2)) print('Date:', datetime_object.date()) print('Time:', datetime_object.time())
It returns a date that is two hours earlier than that in a string and date and time with functions.
Date and time: 2019-03-29T08:26:06.562353+00:00 Date: 2019-03-29 Time: 10:26:06.562353
You can use humanize to make the result more … human.
import arrow datetime_object = arrow.get('2019-03-29 10:26:06.562353') print(datetime_object.humanize())
After you run the code, you are going to get a result that is written as a string.
a year ago
Another interesting module used to deal with dates is moment. You can import the date from as a string in a similar way as you did with other modules.
import moment datetime_object = moment.date('2019-03-29 10:26:06.562353') tomorrow_date = moment.date('tomorrow') print(datetime_object) print(tomorrow_date)
By default, the module treats the date using a local timezone. With the moment module, the date function can get a string parameter, such as ‘tomorrow’ to return the tomorrow date.
In this tutorial, I presented a standard Python library and a few third-party libraries.
The main advantage of these libraries over the standard module is that you don’t have to specify the format and they can figure it out on their own.
Each of these libraries has its pros and cons. To learn more about them, you should read their documentation and decide which one is the most suitable for your needs.