Skip to content

Commit 3f8955f

Browse files
committed
gh-152847: Reject non-digit day-of-year in pure-Python zoneinfo POSIX TZ rules
The J and n day-of-year branches of _parse_dst_start_end() fell through to a bare int(date), accepting input the C accelerator rejects (for example J1_0, which int() reads as day 10, silently building a different zone). Guard the branch with an re.ASCII digit match mirroring the C parser's parse_digits(1, 3), so both implementations agree.
1 parent 384abb7 commit 3f8955f

3 files changed

Lines changed: 26 additions & 0 deletions

File tree

Lib/test/test_zoneinfo/test_zoneinfo.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1111,6 +1111,9 @@ def test_extreme_tzstr(self):
11111111
"AAA4BBB,J1/2,J1/14",
11121112
"AAA4BBB,J20/2,J365/2",
11131113
"AAA4BBB,J365/2,J365/14",
1114+
# Leading-zero day-of-year
1115+
"AAA4BBB,J001/2,J065/2",
1116+
"AAA4BBB,001/2,065/2",
11141117
# Extreme transition hour
11151118
"AAA4BBB,J60/167,J300/2",
11161119
"AAA4BBB,J60/+167,J300/2",
@@ -1209,6 +1212,15 @@ def test_invalid_tzstr(self):
12091212
# Invalid julian offset
12101213
"AAA4BBB,J0/2,J20/2",
12111214
"AAA4BBB,J20/2,J366/2",
1215+
# gh-152847: non-digit day-of-year (e.g. J1_0)
1216+
"AAA4BBB,J1_0,J300/2",
1217+
"AAA4BBB,J60/2,J30_0/2",
1218+
"AAA4BBB,1_0,J300/2",
1219+
"AAA4BBB,J+1,J300/2",
1220+
"AAA4BBB,J 1,J300/2",
1221+
"AAA4BBB, 1,J300/2",
1222+
"AAA4BBB,J0001,J300/2",
1223+
"AAA4BBB,0001,J300/2",
12121224
# Invalid transition time
12131225
"AAA4BBB,J60/2/3,J300/2",
12141226
"AAA4BBB,J60/2,J300/2/3",
@@ -1248,6 +1260,15 @@ def test_invalid_tzstr_non_ascii_abbr(self):
12481260
with self.assertRaisesRegex(ValueError, expected):
12491261
self.zone_from_tzstr(tzstr, encoding="utf-8")
12501262

1263+
def test_invalid_tzstr_non_ascii_dst_date(self):
1264+
tzstr = "AAA4BBB,J١,J300/2"
1265+
if self.module is py_zoneinfo:
1266+
expected = re.escape(tzstr)
1267+
else:
1268+
expected = re.escape(repr(tzstr.encode("utf-8")))
1269+
with self.assertRaisesRegex(ValueError, expected):
1270+
self.zone_from_tzstr(tzstr, encoding="utf-8")
1271+
12511272
@classmethod
12521273
def _populate_test_cases(cls):
12531274
# This method uses a somewhat unusual style in that it populates the

Lib/zoneinfo/_zoneinfo.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -720,6 +720,8 @@ def _parse_dst_start_end(dststr):
720720
else:
721721
n_is_julian = False
722722

723+
if re.fullmatch(r"\d{1,3}", date, re.ASCII) is None:
724+
raise ValueError(f"Invalid dst start/end date: {dststr}")
723725
doy = int(date)
724726
offset = _DayOffset(doy, n_is_julian)
725727

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix the pure-Python :mod:`zoneinfo` parser accepting non-digit characters in
2+
the day-of-year field of a POSIX TZ transition rule (such as ``J1_0``), which
3+
the C implementation rejects. Patch by tonghuaroot.

0 commit comments

Comments
 (0)