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)