How to Convert a Timezone Aware String to Datetime in Python Without Dateutil

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

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.

How to make a timezone aware datetime object

In general, to make a naive datetime timezone-aware, use the localize method:

import datetime
import pytz

unaware = datetime.datetime(2011, 8, 15, 8, 15, 12, 0)
aware = datetime.datetime(2011, 8, 15, 8, 15, 12, 0, pytz.UTC)

now_aware = pytz.utc.localize(unaware)
assert aware == now_aware

For the UTC timezone, it is not really necessary to use localize since there is no daylight savings time calculation to handle:

now_aware = unaware.replace(tzinfo=pytz.UTC)

works. (.replace returns a new datetime; it does not modify unaware.)

Convert non-ISO string containing date, time and timezone to UTC datetime

You can use datetime.datetime.strptime which, %z recognizes timezones without ":" or if using a 3rd party library is more appealing, python-dateutil dateutil.parse.isoparse works straight out of the box. Timezone conversion is afterwards easy with .astimezone method:

import datetime
import dateutil.parser

date_str = "2021-12-15T20:40:39.718-0500"
dt = datetime.datetime.strptime(date_str, "%Y-%m-%dT%H:%M:%S.%f%z")
print(dt)
# Alternative with dateutil
dt = dateutil.parser.isoparse(date_str)
print(dt)
dt = dt.astimezone(datetime.timezone.utc)
print(dt)

Python Datetime : use strftime() with a timezone-aware date

In addition to what @Slam has already answered:

If you want to output the UTC time without any offset, you can do

from datetime import timezone, datetime, timedelta
d = datetime(2009, 4, 19, 21, 12, tzinfo=timezone(timedelta(hours=-2)))
d.astimezone(timezone.utc).strftime('%Y-%m-%d %H:%M:%S.%f')

See datetime.astimezone in the Python docs.

convert string having timezone into to utc datetime object python

That looks like an ISO 8601 string (though with anomalously high precision on the sub-second component); if the fact that you have 7 digits instead of 6 after the decimal point is a typo, you can use dateutil.parser.isoparse on it:

from dateutil.parser import isoparse
from dateutil.tz import UTC

dt = isoparse('2018-05-04T05:22:52.327261-04:00')
dt.astimezone(UTC)

Otherwise you can use dateutil.parser.parse, which is slower but allows for more unusual formats:

from dateutil.parser import parse
from dateutil.tz import UTC

dt = parse('2018-05-04T05:22:52.3272611-04:00')
dt.astimezone(UTC)

You should also use parse if you are not certain what the format will be.

how to convert a string datetime with unknown timezone to timestamp in python

Short answer, without using tzinfos, replace CDT with its UTC equivalent:

In [15]: from dateutil import parser
...: timestamp = parser.parse("Thu Jun 02 11:56:53 UTC-5 2011")
...:
...:

In [16]: timestamp
Out[16]: datetime.datetime(2011, 6, 2, 11, 56, 53, tzinfo=tzoffset(None, 18000))

You can use tzinfos, it must be a dict where keys are the unknows timezones and values are either string UTC format (UTC-5 for example) or number of seconds to offset, here is the doc:

TZINFOS

Additional time zone names / aliases which may be present in the
string. This argument maps time zone names (and optionally offsets
from those time zones) to time zones. This parameter can be a
dictionary with timezone aliases mapping time zone names to time zones
or a function taking two parameters (tzname and tzoffset) and
returning a time zone. The timezones to which the names are mapped can
be an integer offset from UTC in seconds or a tzinfo object. This
parameter is ignored if ignoretz is set.

I tried with both methods and compared

timestamp = parser.parse("Thu Jun 02 11:56:53 CDT 2011", tzinfos={"CDT": -5*3600})
timestamp2 = parser.parse("Thu Jun 02 11:56:53 CDT 2011", tzinfos={"CDT": "UTC-5"})
timestamp3 = parser.parse("Thu Jun 02 11:56:53 UTC-0500 2011")

and it prints

2011-06-02 11:56:53-05:00
2011-06-02 11:56:53-05:00
2011-06-02 11:56:53+05:00

it seems like you have to inverse the sign when using tzinfos (correct me, it might be the inverse thing to do)

How can I parse a custom string as a timezone aware datetime?

Hmm How about maybe:

import re
import datetime
foo = "18 January 2022, 14:50 GMT-5"
bar = re.sub(r"[+-]\d+$", lambda m: "{:05d}".format(100 * int(m.group())), foo)
print(datetime.datetime.strptime(bar, "%d %B %Y, %H:%M %Z%z" ))

I think that gives you:

2022-01-18 14:50:00-05:00



Related Topics



Leave a reply



Submit