https://github.com/python/cpython/commit/99b580857f32a7cd9e25e1b453755757d3a4060b
commit: 99b580857f32a7cd9e25e1b453755757d3a4060b
branch: main
author: Stan Ulbrych <[email protected]>
committer: pganssle <[email protected]>
date: 2025-05-20T16:39:58Z
summary:
gh-122781: Allow empty offset for `%z` in `strptime` (#132922)
* commit
* Move tests
files:
A Misc/NEWS.d/next/Library/2025-04-25-11-48-00.gh-issue-122781.ajsdns.rst
M Lib/_strptime.py
M Lib/test/datetimetester.py
diff --git a/Lib/_strptime.py b/Lib/_strptime.py
index aa63933a49d16d..ae67949626d460 100644
--- a/Lib/_strptime.py
+++ b/Lib/_strptime.py
@@ -302,7 +302,7 @@ def __init__(self, locale_time=None):
# W is set below by using 'U'
'y': r"(?P<y>\d\d)",
'Y': r"(?P<Y>\d\d\d\d)",
- 'z': r"(?P<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))?",
'A': self.__seqToRE(self.locale_time.f_weekday, 'A'),
'a': self.__seqToRE(self.locale_time.a_weekday, 'a'),
'B': self.__seqToRE(self.locale_time.f_month[1:], 'B'),
@@ -548,27 +548,28 @@ def _strptime(data_string, format="%a %b %d %H:%M:%S %Y"):
iso_week = int(found_dict['V'])
elif group_key == 'z':
z = found_dict['z']
- if z == 'Z':
- gmtoff = 0
- else:
- if z[3] == ':':
- z = z[:3] + z[4:]
- if len(z) > 5:
- if z[5] != ':':
- msg = f"Inconsistent use of : in {found_dict['z']}"
- raise ValueError(msg)
- z = z[:5] + z[6:]
- hours = int(z[1:3])
- minutes = int(z[3:5])
- seconds = int(z[5:7] or 0)
- gmtoff = (hours * 60 * 60) + (minutes * 60) + seconds
- gmtoff_remainder = z[8:]
- # Pad to always return microseconds.
- gmtoff_remainder_padding = "0" * (6 - len(gmtoff_remainder))
- gmtoff_fraction = int(gmtoff_remainder +
gmtoff_remainder_padding)
- if z.startswith("-"):
- gmtoff = -gmtoff
- gmtoff_fraction = -gmtoff_fraction
+ if z:
+ if z == 'Z':
+ gmtoff = 0
+ else:
+ if z[3] == ':':
+ z = z[:3] + z[4:]
+ if len(z) > 5:
+ if z[5] != ':':
+ msg = f"Inconsistent use of : in
{found_dict['z']}"
+ raise ValueError(msg)
+ z = z[:5] + z[6:]
+ hours = int(z[1:3])
+ minutes = int(z[3:5])
+ seconds = int(z[5:7] or 0)
+ gmtoff = (hours * 60 * 60) + (minutes * 60) + seconds
+ gmtoff_remainder = z[8:]
+ # Pad to always return microseconds.
+ gmtoff_remainder_padding = "0" * (6 -
len(gmtoff_remainder))
+ gmtoff_fraction = int(gmtoff_remainder +
gmtoff_remainder_padding)
+ if z.startswith("-"):
+ gmtoff = -gmtoff
+ gmtoff_fraction = -gmtoff_fraction
elif group_key == 'Z':
# Since -1 is default value only need to worry about setting tz if
# it can be something other than -1.
diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py
index 345698cfb5f1a4..df5e45f5f20e32 100644
--- a/Lib/test/datetimetester.py
+++ b/Lib/test/datetimetester.py
@@ -2972,6 +2972,17 @@ def test_strptime_leap_year(self):
with self._assertNotWarns(DeprecationWarning):
self.theclass.strptime('02-29,2024', '%m-%d,%Y')
+ def test_strptime_z_empty(self):
+ for directive in ('z',):
+ string = '2025-04-25 11:42:47'
+ format = f'%Y-%m-%d %H:%M:%S%{directive}'
+ target = self.theclass(2025, 4, 25, 11, 42, 47)
+ with self.subTest(string=string,
+ format=format,
+ target=target):
+ result = self.theclass.strptime(string, format)
+ self.assertEqual(result, target)
+
def test_more_timetuple(self):
# This tests fields beyond those tested by the TestDate.test_timetuple.
t = self.theclass(2004, 12, 31, 6, 22, 33)
diff --git
a/Misc/NEWS.d/next/Library/2025-04-25-11-48-00.gh-issue-122781.ajsdns.rst
b/Misc/NEWS.d/next/Library/2025-04-25-11-48-00.gh-issue-122781.ajsdns.rst
new file mode 100644
index 00000000000000..5a9a0cdf7986dc
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-04-25-11-48-00.gh-issue-122781.ajsdns.rst
@@ -0,0 +1,2 @@
+Fix ``%z`` directive in :func:`datetime.datetime.strptime` to allow for no
provided
+offset as was documented.
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]