Skip to content

Commit 6efe5c6

Browse files
JavaZerooclaude
andcommitted
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. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 4084141 commit 6efe5c6

2 files changed

Lines changed: 22 additions & 6 deletions

File tree

Lib/plistlib.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -139,13 +139,11 @@ def _decode_base64(s):
139139

140140
def _date_from_string(s, aware_datetime):
141141
order = ('year', 'month', 'day', 'hour', 'minute', 'second')
142+
# Smaller units may be omitted; default them to the start of the period.
143+
defaults = (1, 1, 1, 0, 0, 0)
142144
gd = _dateParser.match(s).groupdict()
143-
lst = []
144-
for key in order:
145-
val = gd[key]
146-
if val is None:
147-
break
148-
lst.append(int(val))
145+
lst = [int(val) if (val := gd[key]) is not None else default
146+
for key, default in zip(order, defaults)]
149147
if aware_datetime:
150148
return datetime.datetime(*lst, tzinfo=datetime.UTC)
151149
return datetime.datetime(*lst)

Lib/test/test_plistlib.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -938,6 +938,24 @@ def test_load_aware_datetime(self):
938938
aware_datetime=True)
939939
self.assertEqual(dt.tzinfo, datetime.UTC)
940940

941+
def test_load_partial_datetime(self):
942+
# Smaller units may be omitted; missing components default to the
943+
# start of the period.
944+
for data, expected in [
945+
(b"<plist><date>2024Z</date></plist>",
946+
datetime.datetime(2024, 1, 1)),
947+
(b"<plist><date>2024-06Z</date></plist>",
948+
datetime.datetime(2024, 6, 1)),
949+
(b"<plist><date>2024-06-07Z</date></plist>",
950+
datetime.datetime(2024, 6, 7)),
951+
(b"<plist><date>2024-06-07T08Z</date></plist>",
952+
datetime.datetime(2024, 6, 7, 8)),
953+
(b"<plist><date>2024-06-07T08:09Z</date></plist>",
954+
datetime.datetime(2024, 6, 7, 8, 9)),
955+
]:
956+
with self.subTest(data=data):
957+
self.assertEqual(plistlib.loads(data), expected)
958+
941959
@unittest.skipUnless("America/Los_Angeles" in zoneinfo.available_timezones(),
942960
"Can't find timezone datebase")
943961
def test_dump_aware_datetime(self):

0 commit comments

Comments
 (0)