]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-122781: Allow empty offset for `%z` in `strptime` (#132922)
authorStan Ulbrych <89152624+StanFromIreland@users.noreply.github.com>
Tue, 20 May 2025 16:39:58 +0000 (17:39 +0100)
committerGitHub <noreply@github.com>
Tue, 20 May 2025 16:39:58 +0000 (16:39 +0000)
* commit

* Move tests

Lib/_strptime.py
Lib/test/datetimetester.py
Misc/NEWS.d/next/Library/2025-04-25-11-48-00.gh-issue-122781.ajsdns.rst [new file with mode: 0644]

index aa63933a49d16dfb98e74d9ee0b8233852da6584..ae67949626d460aca73eec9e1ed1e360ca943865 100644 (file)
@@ -302,7 +302,7 @@ class TimeRE(dict):
             # 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.
index 345698cfb5f1a41e97b7c6359e0b0d37d5f1ed08..df5e45f5f20e32c20a80f6ee4df18a3da49c1abf 100644 (file)
@@ -2972,6 +2972,17 @@ class TestDateTime(TestDate):
         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 (file)
index 0000000..5a9a0cd
--- /dev/null
@@ -0,0 +1,2 @@
+Fix ``%z`` directive in :func:`datetime.datetime.strptime` to allow for no provided
+offset as was documented.