Converting String with Utc Offset to a Datetime Object

Converting string with UTC offset to a datetime object

It looks as if strptime doesn't always support %z. Python appears to just call the C function, and strptime doesn't support %z on your platform.

Note: from Python 3.2 onwards it will always work.

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 convert string with UTC offset

Your format string is correct and works fine in Python 3.3:

>>> a = "Sun 10 May 2015 13:34:36 -0700"
>>> datetime.strptime(a, "%a %d %b %Y %H:%M:%S %z")
datetime.datetime(2015, 5, 10, 13, 34, 36, tzinfo=datetime.timezone(datetime.timedelta(-1, 61200)))

It gives the error in Python 2.7 indeed.

Unlike strftime(), which is implemented by calling the libc function, strptime() is implemented in the Python library. Here you can see that the version used in Python 2.7 doesn’t support the z format. On the other hand here is the version from Python 3.3, which supports that (I think this was added around 3.2).

So, basically, you have two options:

  1. Using some external library that is able to handle z.
  2. Implementing it yourself (e.g. by stripping the timezone from the string, feeding the first part to strptime() and parsing the second one manually). Looking at how this is done in the Python library might be helpful.

I tried to parse this to return an “aware” object, but it is somewhat complicated.

>>> a = "Sun 10 May 2015 13:34:36 -0700"
>>> time, tz = a.rsplit(' ', 1)
>>> d = datetime.strptime(time, '%a %d %b %Y %H:%M:%S')
datetime.datetime(2015, 5, 10, 13, 34, 36)

Now I have to call d.replace(tzinfo=…tz…) to replace the timezone, but the problem is that I can’t get an instance of tzinfo because just knowing the offset from UTC is not enough to identify a timezone.

In Python 3.2 there is a special timezone class that is a subclass of tzinfo representing a “fake” timezone defined by just its offset. So there are two ways to proceed:

  1. Backport (basically, copy and paste) the timezone class from Python 3 and use it in your parser.
  2. Return a “naive” object:

    >>> d + timedelta(hours=int(tz[1:]) * (1 if tz.startswith('-') else -1))
    datetime.datetime(2015, 6, 8, 17, 34, 36)

Changing string to UTC to dateTime object

#1 Since your string is ISO 8601 compatible, use fromisoformat() on Python 3.7+:

from datetime import datetime, timezone

s = '2020-06-24T13:30:00-04:00'

dtobj = datetime.fromisoformat(s)
# dtobj
# datetime.datetime(2020, 6, 24, 13, 30, tzinfo=datetime.timezone(datetime.timedelta(days=-1, seconds=72000)))

Note that this will give you a timezone-aware datetime object; the tzinfo property is a UTC offset. You can easily convert that to UTC using astimezone():

dtobj_utc = dtobj.astimezone(timezone.utc)
# dtobj_utc
# datetime.datetime(2020, 6, 24, 17, 30, tzinfo=datetime.timezone.utc)

#2 You can achieve the same with strptime (also Python3.7+ according to this):

dtobj = datetime.strptime(s, '%Y-%m-%dT%H:%M:%S%z')
dtobj_utc = dtobj.astimezone(timezone.utc)
# dtobj_utc
# datetime.datetime(2020, 6, 24, 17, 30, tzinfo=datetime.timezone.utc)

#3 If you want to turn the result into a naive datetime object, i.e. remove the tzinfo property, replace with None:

dtobj_utc_naive = dtobj_utc.replace(tzinfo=None)
# dtobj_utc_naive
# datetime.datetime(2020, 6, 24, 17, 30)

#4 For older Python versions, you should be able to use dateutil's parser:

from dateutil import parser
dtobj = parser.parse(s)
dtobj_utc = dtobj.astimezone(timezone.utc)
dtobj_utc_naive = dtobj_utc.replace(tzinfo=None)
# dtobj_utc_naive
# datetime.datetime(2020, 6, 24, 17, 30)

Convert string time with offset from UTC to UTC unix

Code:

from datetime import datetime, timezone, timedelta

def strtime_to_unix(str_time: str, utc_offset: int, format: str = '%d.%m.%Y %H:%M') -> int:
return int(datetime.strptime(str_time, format).replace(
tzinfo=timezone(timedelta(seconds=utc_offset * 60 * 60))).timestamp())

str_time = '27.06.2022 12:35'

print(strtime_to_unix(str_time, 0)) # 12:35 UTC -> 1656333300
print(strtime_to_unix(str_time, -4)) # 16:35 UTC -> 1656347700
print(strtime_to_unix(str_time, 3)) # 09:35 UTC -> 1656322500

Output:

1656333300
1656347700
1656322500

Convert a date with UTC timezone offset to corresponding local timezone format

since your input already contains a UTC offset, you can parse the iso-format string to aware datetime (tzinfo set) and use astimezone to set the correct time zone. Using pytz:

from datetime import datetime
from pytz import timezone # Python 3.9: zoneinfo (standard lib)

str_date = '2020-01-01T00:00:00-08:00'

# to datetime
dt = datetime.fromisoformat(str_date)

# to tz
dt_tz = dt.astimezone(timezone("America/Los_Angeles"))

print(dt_tz)
# 2020-01-01 00:00:00-08:00
print(repr(dt_tz))
# datetime.datetime(2020, 1, 1, 0, 0, tzinfo=<DstTzInfo 'America/Los_Angeles' PST-1 day, 16:00:00 STD>)
print(dt_tz.strftime("%a, %d %b %Y %H:%M:%S %Z"))
# Wed, 01 Jan 2020 00:00:00 PST

See also: Display the time in a different time zone - e.g. this answer for a Python 3.9/zoneinfo example.

Convert UTC datetime string to local datetime

If you don't want to provide your own tzinfo objects, check out the python-dateutil library. It provides tzinfo implementations on top of a zoneinfo (Olson) database such that you can refer to time zone rules by a somewhat canonical name.

from datetime import datetime
from dateutil import tz

# METHOD 1: Hardcode zones:
from_zone = tz.gettz('UTC')
to_zone = tz.gettz('America/New_York')

# METHOD 2: Auto-detect zones:
from_zone = tz.tzutc()
to_zone = tz.tzlocal()

# utc = datetime.utcnow()
utc = datetime.strptime('2011-01-21 02:37:21', '%Y-%m-%d %H:%M:%S')

# Tell the datetime object that it's in UTC time zone since
# datetime objects are 'naive' by default
utc = utc.replace(tzinfo=from_zone)

# Convert time zone
central = utc.astimezone(to_zone)

Edit Expanded example to show strptime usage

Edit 2 Fixed API usage to show better entry point method

Edit 3 Included auto-detect methods for timezones (Yarin)

How to convert local time string to UTC?

Thanks @rofly, the full conversion from string to string is as follows:

import time
time.strftime("%Y-%m-%d %H:%M:%S",
time.gmtime(time.mktime(time.strptime("2008-09-17 14:04:00",
"%Y-%m-%d %H:%M:%S"))))

My summary of the time/calendar functions:

time.strptime

string --> tuple (no timezone applied, so matches string)

time.mktime

local time tuple --> seconds since epoch (always local time)

time.gmtime

seconds since epoch --> tuple in UTC

and

calendar.timegm

tuple in UTC --> seconds since epoch

time.localtime

seconds since epoch --> tuple in local timezone

Convert timestamps with offset to datetime obj using strptime

The Python 2 strptime() function indeed does not support the %z format for timezones (because the underlying time.strptime() function doesn't support it). You have two options:

  • Ignore the timezone when parsing with strptime:

    time_obj = datetime.datetime.strptime(time_str[:19], '%Y-%m-%dT%H:%M:%S')
  • use the dateutil module, it's parse function does deal with timezones:

    from dateutil.parser import parse
    time_obj = parse(time_str)

Quick demo on the command prompt:

>>> from dateutil.parser import parse
>>> parse("2012-07-24T23:14:29-07:00")
datetime.datetime(2012, 7, 24, 23, 14, 29, tzinfo=tzoffset(None, -25200))

You could also upgrade to Python 3.2 or newer, where timezone support has been improved to the point that %z would work, provided you remove the last : from the input, and the - from before the %z:

>>> import datetime
>>> time_str = "2012-07-24T23:14:29-07:00"
>>> datetime.datetime.strptime(time_str, '%Y-%m-%dT%H:%M:%S%z')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/mj/Development/Library/buildout.python/parts/opt/lib/python3.4/_strptime.py", line 500, in _strptime_datetime
tt, fraction = _strptime(data_string, format)
File "/Users/mj/Development/Library/buildout.python/parts/opt/lib/python3.4/_strptime.py", line 337, in _strptime
(data_string, format))
ValueError: time data '2012-07-24T23:14:29-07:00' does not match format '%Y-%m-%dT%H:%M:%S%z'
>>> ''.join(time_str.rsplit(':', 1))
'2012-07-24T23:14:29-0700'
>>> datetime.datetime.strptime(''.join(time_str.rsplit(':', 1)), '%Y-%m-%dT%H:%M:%S%z')
datetime.datetime(2012, 7, 24, 23, 14, 29, tzinfo=datetime.timezone(datetime.timedelta(-1, 61200)))

How do I convert 2015-11-06T18:34:07+05:30 string to UTC DateTime object in C#

DateTime dt = DateTime.Parse("2015-11-06T18:34:07+05:30",
CultureInfo.InvariantCulture,
DateTimeStyles.AdjustToUniversal);

Or:

DateTime dt = DateTime.ParseExact("2015-11-06T18:34:07+05:30",
"yyyy-MM-dd'T'HH:mm:ssK",
CultureInfo.InvariantCulture,
DateTimeStyles.AdjustToUniversal);

Or:

DateTimeOffset dto = DateTimeOffset.Parse("2015-11-06T18:34:07+05:30",
CultureInfo.InvariantCulture);
DateTime dt = dto.UtcDateTime;

Or:

DateTimeOffset dto = DateTimeOffset.ParseExact("2015-11-06T18:34:07+05:30",
"yyyy-MM-dd'T'HH:mm:sszzz",
CultureInfo.InvariantCulture);
DateTime dt = dto.UtcDateTime;

Of course, there are also the TryParse and TryParseExact variants, if you need validation.

Personally, I'd recommend keeping it as a DateTimeOffset rather than going back to DateTime.

There's also Noda Time:

OffsetDateTimePattern pattern = OffsetDateTimePattern.ExtendedIsoPattern;
OffsetDateTime odt = pattern.Parse("2015-11-06T18:34:07+05:30").Value;
DateTimeOffset dto = odt.ToDateTimeOffset();
DateTime dt = dto.UtcDateTime;


Related Topics



Leave a reply



Submit