diff --git a/Lib/plistlib.py b/Lib/plistlib.py
index 93f3ef5e38af84..3fa548e63f84d9 100644
--- a/Lib/plistlib.py
+++ b/Lib/plistlib.py
@@ -139,16 +139,22 @@ def _decode_base64(s):
def _date_from_string(s, aware_datetime):
order = ('year', 'month', 'day', 'hour', 'minute', 'second')
- gd = _dateParser.match(s).groupdict()
+ m = _dateParser.match(s)
+ if m is None:
+ raise ValueError(f"invalid date string: {s!r}")
+ gd = m.groupdict()
lst = []
for key in order:
val = gd[key]
if val is None:
break
lst.append(int(val))
- if aware_datetime:
- return datetime.datetime(*lst, tzinfo=datetime.UTC)
- return datetime.datetime(*lst)
+ try:
+ if aware_datetime:
+ return datetime.datetime(*lst, tzinfo=datetime.UTC)
+ return datetime.datetime(*lst)
+ except (TypeError, ValueError):
+ raise ValueError(f"invalid date string: {s!r}") from None
def _date_to_string(d, aware_datetime):
diff --git a/Lib/test/test_plistlib.py b/Lib/test/test_plistlib.py
index b9c261310bb567..b9025f81e63205 100644
--- a/Lib/test/test_plistlib.py
+++ b/Lib/test/test_plistlib.py
@@ -876,6 +876,13 @@ def test_invalidreal(self):
self.assertRaises(ValueError, plistlib.loads,
b"not real")
+ def test_invaliddate(self):
+ for xml in (b"not a date",
+ b"",
+ b"2004Z",
+ b"2004-11Z"):
+ self.assertRaises(ValueError, plistlib.loads, xml)
+
def test_integer_notations(self):
pl = b"456"
value = plistlib.loads(pl)
diff --git a/Misc/NEWS.d/next/Library/2026-07-03-12-29-41.gh-issue-152957.Cat86k.rst b/Misc/NEWS.d/next/Library/2026-07-03-12-29-41.gh-issue-152957.Cat86k.rst
new file mode 100644
index 00000000000000..c7b20e39f5a0a9
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-07-03-12-29-41.gh-issue-152957.Cat86k.rst
@@ -0,0 +1,2 @@
+:mod:`plistlib` now raises :exc:`ValueError` for an invalid ```` in an
+XML plist. Patch by tonghuaroot.