Skip to content
Open
22 changes: 15 additions & 7 deletions Doc/library/datetime.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2659,9 +2659,11 @@ requires, and these work on all supported platforms.
| | number. | 2014, ..., 9998, 9999 | |
+-----------+--------------------------------+------------------------+-------+
| ``%z`` | UTC offset in the form | (empty), +0000, | \(6) |
| | ``±HHMM[SS[.ffffff]]`` (empty | -0400, +1030, | |
| | string if the object is | +063415, | |
| | naive). | -030712.345216 | |
| | ``±HHMM[SS[.ffffff]]``. | -0400, +1030, | |
| | For :meth:`!strptime`, | +063415, +04, | |
| | ``±HH[MM[SS[.ffffff]]]`` | -030712.345216 | |
| | (empty string if the object is | | |
| | naive). | | |
+-----------+--------------------------------+------------------------+-------+
| ``%Z`` | Time zone name (empty string | (empty), UTC, GMT | \(6) |
| | if the object is naive). | | |
Expand All @@ -2684,9 +2686,11 @@ convenience.
| | digits. | | |
+-----------+--------------------------------+------------------------+-------+
| ``%:z`` | UTC offset in the form | (empty), +00:00, | \(6) |
| | ``±HH:MM[:SS[.ffffff]]`` | -04:00, +10:30, | |
| | (empty string if the object is | +06:34:15, | |
| | naive). | -03:07:12.345216 | |
| | ``±HH:MM[:SS[.ffffff]]``. | -04:00, +10:30, | |
| | For :meth:`!strptime`, | +06:34:15, +04, | |
| | ``±HH[:MM[:SS[.ffffff]]]`` | -03:07:12.345216 | |
| | (empty string if the object is | | |
| | naive). | | |
+-----------+--------------------------------+------------------------+-------+

The full set of format codes supported varies across platforms, because Python
Expand Down Expand Up @@ -2822,7 +2826,7 @@ Notes:

``%z``
:meth:`~.datetime.utcoffset` is transformed into a string of the form
``±HHMM[SS[.ffffff]]``, where ``HH`` is a 2-digit string giving the number
``±HH[MM[SS[.ffffff]]]``, where ``HH`` is a 2-digit string giving the number
of UTC offset hours, ``MM`` is a 2-digit string giving the number of UTC
offset minutes, ``SS`` is a 2-digit string giving the number of UTC offset
seconds and ``ffffff`` is a 6-digit string giving the number of UTC
Expand Down Expand Up @@ -2871,6 +2875,10 @@ Notes:
aware :class:`.datetime` object will be produced. The ``tzinfo`` of the
result will be set to a :class:`timezone` instance.

.. versionchanged:: next
The ``%z`` and ``%:z`` directives in :meth:`~.datetime.strptime`
now accept time zone offsets in ``±HH`` format (for example, ``+03``).

(7)
When used with the :meth:`~.datetime.strptime` method, ``%U`` and ``%W`` are only used
in calculations when the day of the week and the calendar year (``%Y``)
Expand Down
8 changes: 4 additions & 4 deletions Lib/_strptime.py
Original file line number Diff line number Diff line change
Expand Up @@ -372,8 +372,8 @@ def __init__(self, locale_time=None):
'y': r"(?P<y>\d\d)",
'Y': r"(?P<Y>\d\d\d\d)",
# See gh-121237: "z" must support colons for backwards compatibility.
'z': r"(?P<z>([+-]\d\d:?[0-5]\d(:?[0-5]\d(\.\d{1,6})?)?)|(?-i:Z))?",
':z': r"(?P<colon_z>([+-]\d\d:[0-5]\d(:[0-5]\d(\.\d{1,6})?)?)|(?-i:Z))?",
'z': r"(?P<z>([+-]\d\d(:?[0-5]\d(:?[0-5]\d(\.\d{1,6})?)?)?)|(?-i:Z))?",
':z': r"(?P<colon_z>([+-]\d\d(:[0-5]\d(:[0-5]\d(\.\d{1,6})?)?)?)|(?-i:Z))?",
'A': self.__seqToRE(self.locale_time.f_weekday, 'A'),
'a': self.__seqToRE(self.locale_time.a_weekday, 'a'),
'B': self.__seqToRE(_fixmonths(self.locale_time.f_month[1:]), 'B'),
Expand Down Expand Up @@ -682,15 +682,15 @@ def parse_int(s):
if z == 'Z':
gmtoff = 0
else:
if z[3] == ':':
if len(z) != 3 and z[3] == ':':
z = z[:3] + z[4:]
if len(z) > 5:
if z[5] != ':':
msg = f"Inconsistent use of : in {found_dict[group_key]}"
raise ValueError(msg)
z = z[:5] + z[6:]
hours = int(z[1:3])
minutes = int(z[3:5])
minutes = int(z[3:5] or 0)
seconds = int(z[5:7] or 0)
gmtoff = (hours * 60 * 60) + (minutes * 60) + seconds
gmtoff_remainder = z[8:]
Expand Down
10 changes: 10 additions & 0 deletions Lib/test/datetimetester.py
Original file line number Diff line number Diff line change
Expand Up @@ -3016,6 +3016,10 @@ def test_strptime(self):

strptime = self.theclass.strptime

self.assertEqual(strptime("+01", "%z").utcoffset(), 1 * HOUR)
self.assertEqual(strptime("+01", "%:z").utcoffset(), 1 * HOUR)
self.assertEqual(strptime("-10", "%z").utcoffset(), -10 * HOUR)
self.assertEqual(strptime("-10", "%:z").utcoffset(), -10 * HOUR)
self.assertEqual(strptime("+0002", "%z").utcoffset(), 2 * MINUTE)
self.assertEqual(strptime("-0002", "%z").utcoffset(), -2 * MINUTE)
self.assertEqual(
Expand Down Expand Up @@ -3056,6 +3060,8 @@ def test_strptime(self):
with self.assertRaises(ValueError): strptime("-2400", "%z")
with self.assertRaises(ValueError): strptime("-000", "%z")
with self.assertRaises(ValueError): strptime("z", "%z")
with self.assertRaises(ValueError): strptime("+3", "%z")
with self.assertRaises(ValueError): strptime("-7", "%z")

def test_strptime_ampm(self):
dt = datetime(1999, 3, 17, 0, 44, 55, 2)
Expand Down Expand Up @@ -4185,6 +4191,10 @@ def test_strptime(self):

def test_strptime_tz(self):
strptime = self.theclass.strptime
self.assertEqual(strptime("+01", "%z").utcoffset(), 1 * HOUR)
self.assertEqual(strptime("+01", "%:z").utcoffset(), 1 * HOUR)
self.assertEqual(strptime("-10", "%z").utcoffset(), -10 * HOUR)
self.assertEqual(strptime("-10", "%:z").utcoffset(), -10 * HOUR)
self.assertEqual(strptime("+0002", "%z").utcoffset(), 2 * MINUTE)
self.assertEqual(strptime("-0002", "%z").utcoffset(), -2 * MINUTE)
self.assertEqual(
Expand Down
2 changes: 2 additions & 0 deletions Lib/test/test_strptime.py
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,8 @@ def test_offset(self):
self.assertEqual(offset_fraction, -1)

cases = [
("-01", -one_hour, 0),
("+09", 9 * one_hour, 0),
("+01:00", one_hour, 0),
("-01:30", -(one_hour + half_hour), 0),
("-01:30:30", -(one_hour + half_hour + half_minute), 0),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Update input time zone format from ``±HHMM[SS[.ffffff]]`` to
``±HH[MM[SS[.ffffff]]]`` for :meth:`datetime.date.strptime`,
:meth:`datetime.datetime.strptime`, :meth:`datetime.time.strptime` and
:func:`time.strptime`. Patch by Semyon Moroz.
Loading