How to convert a timezone aware string to datetime in Python without dateutil?

Question:

I have to convert a timezone-aware string like "2012-11-01T04:16:13-04:00" to a Python datetime object.

I saw the dateutil module which has a parse function, but I don’t really want to use it as it adds a dependency.

So how can I do it? I have tried something like the following, but with no luck.

datetime.datetime.strptime("2012-11-01T04:16:13-04:00", "%Y-%m-%dT%H:%M:%S%Z")
Asked By: lxyu

||

Answers:

As of Python 3.7, datetime.datetime.fromisoformat() can handle your format:

>>> import datetime
>>> datetime.datetime.fromisoformat('2012-11-01T04:16:13-04:00')
datetime.datetime(2012, 11, 1, 4, 16, 13, tzinfo=datetime.timezone(datetime.timedelta(days=-1, seconds=72000)))

In older Python versions you can’t, not without a whole lot of painstaking manual timezone defining.

Python does not include a timezone database, because it would be outdated too quickly. Instead, Python relies on external libraries, which can have a far faster release cycle, to provide properly configured timezones for you.

As a side-effect, this means that timezone parsing also needs to be an external library. If dateutil is too heavy-weight for you, use iso8601 instead, it’ll parse your specific format just fine:

>>> import iso8601
>>> iso8601.parse_date('2012-11-01T04:16:13-04:00')
datetime.datetime(2012, 11, 1, 4, 16, 13, tzinfo=<FixedOffset '-04:00'>)

iso8601 is a whopping 4KB small. Compare that tot python-dateutil‘s 148KB.

As of Python 3.2 Python can handle simple offset-based timezones, and %z will parse -hhmm and +hhmm timezone offsets in a timestamp. That means that for a ISO 8601 timestamp you’d have to remove the : in the timezone:

>>> from datetime import datetime
>>> iso_ts = '2012-11-01T04:16:13-04:00'
>>> datetime.strptime(''.join(iso_ts.rsplit(':', 1)), '%Y-%m-%dT%H:%M:%S%z')
datetime.datetime(2012, 11, 1, 4, 16, 13, tzinfo=datetime.timezone(datetime.timedelta(-1, 72000)))

The lack of proper ISO 8601 parsing is being tracked in Python issue 15873.

Answered By: Martijn Pieters

Here is the Python Doc for datetime object using dateutil package..

from dateutil.parser import parse

get_date_obj = parse("2012-11-01T04:16:13-04:00")
print get_date_obj

I’m new to Python, but found a way to convert

2017-05-27T07:20:18.000-04:00
to

2017-05-27T07:20:18 without downloading new utilities.

from datetime import datetime, timedelta

time_zone1 = int("2017-05-27T07:20:18.000-04:00"[-6:][:3])
>>returns -04

item_date = datetime.strptime("2017-05-27T07:20:18.000-04:00".replace(".000", "")[:-6], "%Y-%m-%dT%H:%M:%S") + timedelta(hours=-time_zone1)

I’m sure there are better ways to do this without slicing up the string so much, but this got the job done.

Answered By: wiseco68

There are two issues with the code in the original question: there should not be a : in the timezone and the format string for “timezone as an offset” is lower case %z not upper %Z.

This works for me in Python v3.6

>>> from datetime import datetime
>>> t = datetime.strptime("2012-11-01T04:16:13-0400", "%Y-%m-%dT%H:%M:%S%z")
>>> print(t)
2012-11-01 04:16:13-04:00
Answered By: Jamie Czuy

You can convert like this.

date = datetime.datetime.strptime('2019-3-16T5-49-52-595Z','%Y-%m-%dT%H-%M-%S-%f%z')
date_time = date.strftime('%Y-%m-%dT%H:%M:%S.%fZ')
Answered By: Muruganandam C

This suggestion for using dateutil by Mohideen bin Mohammed definitely is the best solution even if it does a require a small library. having used the other approaches there prone to various forms of failure. Here’s a nice function for this.

from dateutil.parser import parse


def parse_date_convert(date, fmt=None):
    if fmt is None:
        fmt = '%Y-%m-%d %H:%M:%S' # Defaults to : 2022-08-31 07:47:30
    get_date_obj = parse(str(date))
    return str(get_date_obj.strftime(fmt))

dates = ['2022-08-31T07:47:30Z','2022-08-31T07:47:29.098Z','2017-05-27T07:20:18.000-04:00','2012-11-01T04:16:13-04:00']

for date in dates:
    print(f'Before: {date}  After: {parse_date_convert(date)}')

Results:

Before: 2022-08-31T07:47:30Z  After: 2022-08-31 07:47:30
Before: 2022-08-31T07:47:29.098Z  After: 2022-08-31 07:47:29
Before: 2017-05-27T07:20:18.000-04:00  After: 2017-05-27 07:20:18
Before: 2012-11-01T04:16:13-04:00  After: 2012-11-01 04:16:13

Having tried various forms such as slicing split replacing the T Z like this:

dates = ['2022-08-31T07:47:30Z','2022-08-31T07:47:29.098Z','2017-05-27T07:20:18.000-04:00','2012-11-01T04:16:13-04:00']

for date in dates:
    print(f'Before: {date}  After: {date.replace("T", " ").replace("Z", "")}')

You still are left with subpar results. like the below

Before: 2022-08-31T07:47:30Z  After: 2022-08-31 07:47:30
Before: 2022-08-31T07:47:29.098Z  After: 2022-08-31 07:47:29.098
Before: 2017-05-27T07:20:18.000-04:00  After: 2017-05-27 07:20:18.000-04:00
Before: 2012-11-01T04:16:13-04:00  After: 2012-11-01 04:16:13-04:00
Answered By: Mik R

You can create a timezone unaware object and replace the tzinfo and make it a timezone aware DateTime object later.

from datetime import datetime
import pytz

unware_time = datetime.strptime("2012-11-01 04:16:13", "%Y-%m-%d %H:%M:%S")
aware_time = unaware_time.replace(tzinfo=pytz.UTC)
Answered By: Mowli sri
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.