From d549737a982d75dd94b3fb1df004ca80dfb2142b Mon Sep 17 00:00:00 2001 From: JavaZeroo <2487163254@qq.com> Date: Wed, 10 Jun 2026 03:20:46 +0000 Subject: [PATCH 1/3] Fix plistlib loading of partial ISO 8601 dates plistlib's date regex makes the month, day and time components optional, but _date_from_string stopped building the datetime arguments at the first missing component, so a partial date such as ``2024-06Z`` raised a confusing TypeError instead of producing a datetime. Default the omitted components to the start of the period. --- Lib/plistlib.py | 10 ++++------ Lib/test/test_plistlib.py | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/Lib/plistlib.py b/Lib/plistlib.py index 93f3ef5e38af843..3a8ca370e04dab6 100644 --- a/Lib/plistlib.py +++ b/Lib/plistlib.py @@ -139,13 +139,11 @@ def _decode_base64(s): def _date_from_string(s, aware_datetime): order = ('year', 'month', 'day', 'hour', 'minute', 'second') + # Smaller units may be omitted; default them to the start of the period. + defaults = (1, 1, 1, 0, 0, 0) gd = _dateParser.match(s).groupdict() - lst = [] - for key in order: - val = gd[key] - if val is None: - break - lst.append(int(val)) + lst = [int(val) if (val := gd[key]) is not None else default + for key, default in zip(order, defaults)] if aware_datetime: return datetime.datetime(*lst, tzinfo=datetime.UTC) return datetime.datetime(*lst) diff --git a/Lib/test/test_plistlib.py b/Lib/test/test_plistlib.py index b9c261310bb5670..545a2bb947c38d8 100644 --- a/Lib/test/test_plistlib.py +++ b/Lib/test/test_plistlib.py @@ -938,6 +938,24 @@ def test_load_aware_datetime(self): aware_datetime=True) self.assertEqual(dt.tzinfo, datetime.UTC) + def test_load_partial_datetime(self): + # Smaller units may be omitted; missing components default to the + # start of the period. + for data, expected in [ + (b"2024Z", + datetime.datetime(2024, 1, 1)), + (b"2024-06Z", + datetime.datetime(2024, 6, 1)), + (b"2024-06-07Z", + datetime.datetime(2024, 6, 7)), + (b"2024-06-07T08Z", + datetime.datetime(2024, 6, 7, 8)), + (b"2024-06-07T08:09Z", + datetime.datetime(2024, 6, 7, 8, 9)), + ]: + with self.subTest(data=data): + self.assertEqual(plistlib.loads(data), expected) + @unittest.skipUnless("America/Los_Angeles" in zoneinfo.available_timezones(), "Can't find timezone datebase") def test_dump_aware_datetime(self): From 526bbb5c1bcd801ce4ce861e1480e4dd87051824 Mon Sep 17 00:00:00 2001 From: JavaZeroo <2487163254@qq.com> Date: Wed, 10 Jun 2026 06:36:40 +0000 Subject: [PATCH 2/3] Add NEWS entry --- .../Library/2026-06-10-06-36-35.gh-issue-151221.DPeEZr.rst | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2026-06-10-06-36-35.gh-issue-151221.DPeEZr.rst diff --git a/Misc/NEWS.d/next/Library/2026-06-10-06-36-35.gh-issue-151221.DPeEZr.rst b/Misc/NEWS.d/next/Library/2026-06-10-06-36-35.gh-issue-151221.DPeEZr.rst new file mode 100644 index 000000000000000..ae59bff28dd0e66 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-10-06-36-35.gh-issue-151221.DPeEZr.rst @@ -0,0 +1,3 @@ +Fix :mod:`plistlib` raising a confusing :exc:`TypeError` when loading a +```` that omits smaller units (for example ``2024-06Z`` or ``2024Z``). +The omitted components now default to the start of the period. From c0c74b2f2bc56ce4a78335a97809151d06e37186 Mon Sep 17 00:00:00 2001 From: JavaZeroo <2487163254@qq.com> Date: Wed, 10 Jun 2026 08:33:05 +0000 Subject: [PATCH 3/3] Document the behaviour change in plistlib docs --- Doc/library/plistlib.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Doc/library/plistlib.rst b/Doc/library/plistlib.rst index 72140e41675c350..4cfdf3655a1f0b3 100644 --- a/Doc/library/plistlib.rst +++ b/Doc/library/plistlib.rst @@ -81,6 +81,11 @@ This module defines the following functions: .. versionchanged:: 3.13 The keyword-only parameter *aware_datetime* has been added. + .. versionchanged:: next + ```` values that omit smaller units (for example ``2024-06Z``) + are now accepted, with the omitted components defaulting to the start + of the period. Previously such values raised :exc:`TypeError`. + .. function:: loads(data, *, fmt=None, dict_type=dict, aware_datetime=False)