Converting Strings using datetime

Converting Strings using datetime

The datetime module consists of three different object types: date, time and datetime. As you may have guessed, date holds the date, time holds the time, and datetime holds both date and time.

For example, the following example will print the current date and time:

import datetime

print ('Current date/time: {}'.format(datetime.datetime.now()))

Running this code will print an output similar to below:

$ python3 datetime-print-1.py
Current date/time: 2018-06-29 08:15:27.243860

When no custom formatting is given, the default string format is used, i.e. the format for “2018-06-29 08:15:27.243860” is in ISO 8601 format (YYYY-MM-DDTHH:MM:SS.mmmmmm). If our input string to create a datetime object is in the same ISO 8601 format, we can easily parse it to a datetime object.

Let’s take a look at the code below:

import datetime

date_time_str = '2018-06-29 08:15:27.243860'
date_time_obj = datetime.datetime.strptime(date_time_str, '%Y-%m-%d %H:%M:%S.%f')

print('Date:', date_time_obj.date())
print('Time:', date_time_obj.time())
print('Date-time:', date_time_obj)

Running it will print the below output:

$ python3 datetime-print-2.py
Date: 2018-06-29
Time: 08:15:27.243860
Date-time: 2018-06-29 08:15:27.243860

In this example, we are using a new method called strptime. This method takes two arguments: the first one is the string representation of the date-time and the second one is the format of the input string. The return value is of the type datetime. In our example, "2018-06-29 08:15:27.243860" is the input string and "%Y-%m-%d %H:%M:%S.%f" is the format of this date string. The returned datetime value is stored in date_time_obj variable. Since this is a datetime variable, we can call date() and time() methods directly on it. As you can see from the output, it prints the ‘date’ and ‘time’ part of the input string.

You might be wondering what is the meaning of the format "%Y-%m-%d %H:%M:%S.%f". These are known as format tokens with different meaning for each token. Check out the strptime documentation for the list of all different types of format code supported in Python.

So, if the format of a string is known, it can be easily parsed to a datetime object using strptime. Let me show you one more non-trivial example:

import datetime

date_time_str = 'Jun 28 2018  7:40AM'
date_time_obj = datetime.datetime.strptime(date_time_str, '%b %d %Y %I:%M%p')

print('Date:', date_time_obj.date())
print('Time:', date_time_obj.time())
print('Date-time:', date_time_obj)

From the following output you can see that the string was successfully parsed since it is being properly printed by the datetime object here.

$ python3 datetime-print-3.py
Date: 2018-06-28
Time: 07:40:00
Date-time: 2018-06-28 07:40:00

Here are a few more examples of commonly used time formats and the tokens used for parsing:

"Jun 28 2018 at 7:40AM" -> "%b %d %Y at %I:%M%p"
"September 18, 2017, 22:19:55" -> "%B %d, %Y, %H:%M:%S"
"Sun,05/12/99,12:30PM" -> "%a,%d/%m/%y,%I:%M%p"
"Mon, 21 March, 2015" -> "%a, %d %B, %Y"
"2018-03-12T10:12:45Z" -> "%Y-%m-%dT%H:%M:%SZ"

You can parse a date-time string of any format using the table mentioned in the strptime documentation.

Dealing with Timezones and datetime

Handling date-times becomes more complex while dealing with timezones. All above examples we have discussed are naive datetime objects, i.e. these objects don’t contain any timezone-related data. The datetime object has one variable tzinfo, that holds the timezone information.

import datetime as dt

dtime = dt.datetime.now()
print(dtime)
print(dtime.tzinfo)

This code will print:

$ python3 datetime-tzinfo-1.py
2018-06-29 22:16:36.132767
None

The output of tzinfo is None since it is a naive datetime object. For timezone conversion, one library called pytz is available for Python. You can install it as described in these instructions. Now, let’s use the pytz library to convert the above timestamp to UTC.

import datetime as dt
import pytz

dtime = dt.datetime.now(pytz.utc)
print(dtime)
print(dtime.tzinfo)

Output:

$ python3 datetime-tzinfo-2.py
2018-06-29 17:08:00.586525+00:00
UTC

+00:00 is the difference between the displayed time and the UTC time. In this example the value of tzinfo happens to be UTC as well, hence the 00:00 offset. In this case, the datetime object is a timezone-aware object.

Similarly, we can convert date-time strings to any other timezone. For example, we can convert the string “2018-06-29 17:08:00.586525+00:00” to “America/New_York” timezone, as shown below:

import datetime as dt
import pytz

date_time_str = '2018-06-29 17:08:00'
date_time_obj = dt.datetime.strptime(date_time_str, '%Y-%m-%d %H:%M:%S')

timezone = pytz.timezone('America/New_York')
timezone_date_time_obj = timezone.localize(date_time_obj)

print(timezone_date_time_obj)
print(timezone_date_time_obj.tzinfo)

Output:

$ python3 datetime-tzinfo-3.py
2018-06-29 17:08:00-04:00
America/New_York

First, we have converted the string to a datetime object, date_time_obj. Then we converted it to a timezone-enabled datetime object, timezone_date_time_obj. Since we have mentioned the timezone as “America/New_York”, the output time shows that it is 4 hours behind than UTC time. You can check this Wikipedia page to find the full list of available time zones.

Converting Timezones

We can convert timezone of a datetime object from one region to another, as shown in the example below:

import datetime as dt
import pytz

timezone_nw = pytz.timezone('America/New_York')
nw_datetime_obj = dt.datetime.now(timezone_nw)

timezone_london = pytz.timezone('Europe/London')
london_datetime_obj = nw_datetime_obj.astimezone(timezone_london)


print('America/New_York:', nw_datetime_obj)
print('Europe/London:', london_datetime_obj)

First, it created one datetime object with the current time on “America/New_York” timezone. Then using astimezone() method, we have converted this datetime to “Europe/London” timezone. Both datetimes will print different values like:

$ python3 datetime-tzinfo-4.py
America/New_York: 2018-06-29 22:21:41.349491-04:00
Europe/London: 2018-06-30 03:21:41.349491+01:00

Using Third Party Libraries

Python’s datetime module can convert all different types of strings to a datetime object. But the main problem is that in order to do this you need to create the appropriate formatting code string that strptime can understand. Creating this string takes time and it makes the code harder to read. Instead, we can use other third-party libraries to make it easier.

In some cases these third party libraries also have better built-in support for manipulating and comparing date-times, and some even have timezones built-in, so you don’t need to include an extra package.

Let’s take a look at few of these libraries in the following sections.

dateutil

The dateutil module is an extension to the datetime module. We don’t need to pass any parsing code to parse a string. For example:

from dateutil.parser import parse

datetime = parse('2018-06-29 22:21:41')

print(datetime)

This parse function will parse the string automatically and store it in the datetime variable. Parsing is done automatically. You don’t have to mention any format string. Let’s try to parse different types of strings using dateutil:

from dateutil.parser import parse

date_array = [
    '2018-06-29 08:15:27.243860',
    'Jun 28 2018  7:40AM',
    'Jun 28 2018 at 7:40AM',
    'September 18, 2017, 22:19:55',
    'Sun, 05/12/1999, 12:30PM',
    'Mon, 21 March, 2015',
    '2018-03-12T10:12:45Z',
    '2018-06-29 17:08:00.586525+00:00',
    '2018-06-29 17:08:00.586525+05:00',
    'Tuesday , 6th September, 2017 at 4:30pm'
]

for date in date_array:
    print('Parsing: ' + date)
    dt = parse(date)
    print(dt.date())
    print(dt.time())
    print(dt.tzinfo)
    print('\n')

Output:

$ python3 dateutil-1.py
Parsing: 2018-06-29 08:15:27.243860
2018-06-29
08:15:27.243860
None

Parsing: Jun 28 2018  7:40AM
2018-06-28
07:40:00
None

Parsing: Jun 28 2018 at 7:40AM
2018-06-28
07:40:00
None

Parsing: September 18, 2017, 22:19:55
2017-09-18
22:19:55
None

Parsing: Sun, 05/12/1999, 12:30PM
1999-05-12
12:30:00
None

Parsing: Mon, 21 March, 2015
2015-03-21
00:00:00
None

Parsing: 2018-03-12T10:12:45Z
2018-03-12
10:12:45
tzutc()

Parsing: 2018-06-29 17:08:00.586525+00:00
2018-06-29
17:08:00.586525
tzutc()

Parsing: 2018-06-29 17:08:00.586525+05:00
2018-06-29
17:08:00.586525
tzoffset(None, 18000)

Parsing: Tuesday , 6th September, 2017 at 4:30pm
2017-09-06
16:30:00
None

You can see that almost any type of string can be parsed easily using the dateutil module.

Maya

Maya also makes it very easy to parse a string and for changing timezones. Some simple examples are shown below:

import maya

dt = maya.parse('2018-04-29T17:45:25Z').datetime()
print(dt.date())
print(dt.time())
print(dt.tzinfo)

Output:

$ python3 maya-1.py
2018-04-29
17:45:25
UTC

For converting the time to a different timezone:

import maya

dt = maya.parse('2018-04-29T17:45:25Z').datetime(to_timezone='America/New_York', naive=False)
print(dt.date())
print(dt.time())
print(dt.tzinfo)

Output:

$ python3 maya-2.py
2018-04-29
13:45:25
America/New_York

Now isn’t that easy to use? Let’s try out maya with the same set of strings we have used with dateutil:

import maya

date_array = [
    '2018-06-29 08:15:27.243860',
    'Jun 28 2018  7:40AM',
    'Jun 28 2018 at 7:40AM',
    'September 18, 2017, 22:19:55',
    'Sun, 05/12/1999, 12:30PM',
    'Mon, 21 March, 2015',
    '2018-03-12T10:12:45Z',
    '2018-06-29 17:08:00.586525+00:00',
    '2018-06-29 17:08:00.586525+05:00',
    'Tuesday , 6th September, 2017 at 4:30pm'
]

for date in date_array:
    print('Parsing: ' + date)
    dt = maya.parse(date).datetime()
    print(dt)
    print(dt.date())
    print(dt.time())
    print(dt.tzinfo)

Output:

$ python3 maya-3.py
Parsing: 2018-06-29 08:15:27.243860
2018-06-29 08:15:27.243860+00:00
2018-06-29
08:15:27.243860
UTC

Parsing: Jun 28 2018  7:40AM
2018-06-28 07:40:00+00:00
2018-06-28
07:40:00
UTC

Parsing: Jun 28 2018 at 7:40AM
2018-06-28 07:40:00+00:00
2018-06-28
07:40:00
UTC

Parsing: September 18, 2017, 22:19:55
2017-09-18 22:19:55+00:00
2017-09-18
22:19:55
UTC

Parsing: Sun, 05/12/1999, 12:30PM
1999-05-12 12:30:00+00:00
1999-05-12
12:30:00
UTC

Parsing: Mon, 21 March, 2015
2015-03-21 00:00:00+00:00
2015-03-21
00:00:00
UTC

Parsing: 2018-03-12T10:12:45Z
2018-03-12 10:12:45+00:00
2018-03-12
10:12:45
UTC

Parsing: 2018-06-29 17:08:00.586525+00:00
2018-06-29 17:08:00.586525+00:00
2018-06-29
17:08:00.586525
UTC

Parsing: 2018-06-29 17:08:00.586525+05:00
2018-06-29 12:08:00.586525+00:00
2018-06-29
12:08:00.586525
UTC

Parsing: Tuesday , 6th September, 2017 at 4:30pm
2017-09-06 16:30:00+00:00
2017-09-06
16:30:00
UTC

As you can see, all date formats were parsed, but did you notice the difference? If we are not providing the timezone info it automatically converts it to UTC. So, it is important to note that we need to provide to_timezone and naive parameters if the time is not in UTC.

Arrow

Arrow is another library for dealing with datetime in Python. We can get the Python datetime object from an arrow object. Let’s try this with the same example string we have used for maya:

import arrow

dt = arrow.get('2018-04-29T17:45:25Z')
print(dt.date())
print(dt.time())
print(dt.tzinfo)

Output:

$ python3 arrow-1.py
2018-04-29
17:45:25
tzutc()

Timezone conversion:

import arrow

dt = arrow.get('2018-04-29T17:45:25Z').to('America/New_York')
print(dt)
print(dt.date())
print(dt.time())

Output:

$ python3 arrow-2.py
2018-04-29T13:45:25-04:00
2018-04-29
13:45:25

As you can see the date-time string is converted to the “America/New_York” region.

Now, let’s again use the same set of strings we have used above:

import arrow

date_array = [
    '2018-06-29 08:15:27.243860',
    #'Jun 28 2018  7:40AM',
    #'Jun 28 2018 at 7:40AM',
    #'September 18, 2017, 22:19:55',
    #'Sun, 05/12/1999, 12:30PM',
    #'Mon, 21 March, 2015',
    '2018-03-12T10:12:45Z',
    '2018-06-29 17:08:00.586525+00:00',
    '2018-06-29 17:08:00.586525+05:00',
    #'Tuesday , 6th September, 2017 at 4:30pm'
]

for date in date_array:
    dt = arrow.get(date)
    print('Parsing: ' + date)
    print(dt)
    print(dt.date())
    print(dt.time())
    print(dt.tzinfo)

This code will fail for the date-time strings that have been commented out. The output for other strings will be:

$ python3 arrow-3.py
Parsing: 2018-06-29 08:15:27.243860
2018-06-29T08:15:27.243860+00:00
2018-06-29
08:15:27.243860
tzutc()

Parsing: 2018-03-12T10:12:45Z
2018-03-12T10:12:45+00:00
2018-03-12
10:12:45
tzutc()

Parsing: 2018-06-29 17:08:00.586525+00:00
2018-06-29T17:08:00.586525+00:00
2018-06-29
17:08:00.586525
tzoffset(None, 0)

Parsing: 2018-06-29 17:08:00.586525+05:00
2018-06-29T17:08:00.586525+05:00
2018-06-29
17:08:00.586525
tzoffset(None, 18000)

In order to correctly parse the date-time strings that I have commented out, you’ll need to pass the corresponding format tokens. For example, “MMM” for months name, like “Jan, Feb, Mar” etc. You can check this guide for all available tokens.

Conclusion

We have checked different ways to parse a string to a datetime object in Python. You can either opt for the default Python datetime library or any of the third party library mentioned in this article, among many others. The main problem with default datetime package is that we need to specify the parsing code manually for almost all date-time string formats. So, if your string format changes in the future, you will likely have to change your code as well. But many third-party libraries, like the ones mentioned here, handle it automatically.

One more problem we face is dealing with timezones. The best way to handle them is always to store the time in UTC format on the server and convert it to the user’s local timezone while parsing. Not only for parsing string, these libraries can be used for a lot of different types of date-time related operations. I’d encourage you to go through the documents to learn the functionalities in detail.

Source stackabuse.com.