python-3.xtimezonepython-datetimedate-parsing

Why datetime.datetime.strptime %Z code doesn’t produce an aware object?


A little example:

$ python
Python 3.13.2 (main, Feb  5 2025, 08:05:21) [GCC 14.2.1 20250128] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import datetime
>>> datetime.datetime.strptime("2025-UTC", "%Y-%Z")
datetime.datetime(2025, 1, 1, 0, 0)
>>> datetime.datetime.strptime("2025-+0000", "%Y-%z")
datetime.datetime(2025, 1, 1, 0, 0, tzinfo=datetime.timezone.utc)

However reading the documentation, I would expect %Z passed to strptime to return an aware object for the values it supports:

%Z

...

strptime() only accepts certain values for %Z:

  • any value in time.tzname for your machine’s locale
  • the hard-coded values UTC and GMT

Even stranger, the internal function called by datetime.datetime.strptime: _strptime parses the UTC as expected:

>>> from _strptime import _strptime
>>> _strptime("2025-UTC", "%Y-%Z")
((2025, 1, 1, 0, 0, 0, 2, 1, 0, 'UTC', None), 0, 0)

So why doesn’t the high-level function return an aware object if the low-level function parsed the data?

I witnessed this behavior with python 3.5, 3.7, 3.10, 3.12 and 3.13 so I assume it isn’t a bug, but a feature? And so, what is the expected use/behavior of %Z in datetime.datetime.strptime? Did I miss something in the documentation or is it a bit unclear as to strptime is returning a naive or an aware object?


originally posted on Python Discuss Forum (with images there), sadly without success


Solution

  • I understand your question this way:

    When using %Z (timezone name like "UTC"), strptime() parses the timezone but returns a naive datetime (no tzinfo).

    When using %z (UTC offset like "+0000"), strptime() returns an aware datetime with tzinfo set

    This is actually intentional behavior, And not a bug.

    The reason behind this behavior is: strptime() function was originally designed to match C-language strptime behavior, which doesn't handle timezone conversion. _strptime function is shared with the time module, which doesn't have timezone awareness. Though %Z can recognize timezone names, it doesn't have enough information to find the actual offset (especially for non-UTC/GMT zones)

    _strptime shows different output because: The low-level _strptime function returns parsed components (including the timezone string). but doesn't do the final datetime construction. Higher-level datetime.strptime() ignores the timezone name when creating the datetime object.

    They should have clearly mentioned this behavior in the official document. They've mentioned what values %Z accepts. but it is not said that these won't make the datetime timezone-aware.

    So, youy can either Use %z with explicit offsets when possible. Or manually add the timezone after parsing:

    dt = datetime.strptime("2025-UTC", "%Y-%Z").replace(tzinfo=timezone.utc)