diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst index c0ae4d66b76a7b..7843ad2c750934 100644 --- a/Doc/library/datetime.rst +++ b/Doc/library/datetime.rst @@ -2547,8 +2547,8 @@ requires, and these work on all platforms with a standard C implementation. | | digits. | | | +-----------+--------------------------------+------------------------+-------+ | ``%z`` | UTC offset in the form | (empty), +0000, | \(6) | -| | ``±HHMM[SS[.ffffff]]`` (empty | -0400, +1030, | | -| | string if the object is | +063415, | | +| | ``±HH[MM[SS[.ffffff]]]`` | -0400, +1030, | | +| | (empty string if the object is | +063415, +04, | | | | naive). | -030712.345216 | | +-----------+--------------------------------+------------------------+-------+ | ``%Z`` | Time zone name (empty string | (empty), UTC, GMT | \(6) | @@ -2609,8 +2609,8 @@ convenience. These parameters all correspond to ISO 8601 date values. | | Jan 4. | | | +-----------+--------------------------------+------------------------+-------+ | ``%: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, | | +| | ``±HH[:MM[:SS[.ffffff]]]`` | -04:00, +10:30, | | +| | (empty string if the object is | +06:34:15, +04, | | | | naive). | -03:07:12.345216 | | +-----------+--------------------------------+------------------------+-------+ @@ -2710,7 +2710,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 diff --git a/Lib/_strptime.py b/Lib/_strptime.py index d011ddf8b181c3..9f536332085704 100644 --- a/Lib/_strptime.py +++ b/Lib/_strptime.py @@ -372,8 +372,8 @@ def __init__(self, locale_time=None): 'y': r"(?P\d\d)", 'Y': r"(?P\d\d\d\d)", # See gh-121237: "z" must support colons for backwards compatibility. - 'z': r"(?P([+-]\d\d:?[0-5]\d(:?[0-5]\d(\.\d{1,6})?)?)|(?-i:Z))?", - ':z': r"(?P([+-]\d\d:[0-5]\d(:[0-5]\d(\.\d{1,6})?)?)|(?-i:Z))?", + 'z': r"(?P([+-]\d\d(:?[0-5]\d(:?[0-5]\d(\.\d{1,6})?)?)?)|(?-i:Z))?", + ':z': r"(?P([+-]\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'), @@ -679,7 +679,7 @@ 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] != ':': @@ -687,7 +687,7 @@ def parse_int(s): 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:] diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index 43cea44bc3d6c0..284c1b9a1c976e 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -2901,6 +2901,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( @@ -4063,6 +4067,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( diff --git a/Lib/test/test_strptime.py b/Lib/test/test_strptime.py index d12816c90840ad..f0eceee0add62f 100644 --- a/Lib/test/test_strptime.py +++ b/Lib/test/test_strptime.py @@ -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), diff --git a/Misc/NEWS.d/next/Library/2025-02-21-07-34-37.gh-issue-128074.qeCRrp.rst b/Misc/NEWS.d/next/Library/2025-02-21-07-34-37.gh-issue-128074.qeCRrp.rst new file mode 100644 index 00000000000000..8016216949e6ff --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-02-21-07-34-37.gh-issue-128074.qeCRrp.rst @@ -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.