Skip to content

Commit a025467

Browse files
committed
Validate the time fields too; move the digit reader to module level
1 parent 46f6881 commit a025467

2 files changed

Lines changed: 22 additions & 14 deletions

File tree

Lib/_pydatetime.py

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -355,27 +355,30 @@ def _find_isoformat_datetime_separator(dtstr):
355355
return 8
356356

357357

358+
def _read_isoformat_component(s, n):
359+
# The caller has verified the string is ASCII, so isdigit() matches only
360+
# the ASCII digits accepted by the C parser.
361+
if len(s) != n or not s.isdigit():
362+
raise ValueError("Invalid isoformat string")
363+
return int(s)
364+
365+
358366
def _parse_isoformat_date(dtstr):
359367
# It is assumed that this is an ASCII-only string of lengths 7, 8 or 10,
360368
# see the comment on Modules/_datetimemodule.c:_find_isoformat_datetime_separator
361369
if len(dtstr) not in (7, 8, 10):
362370
raise ValueError("Invalid isoformat string")
363371
if not dtstr.isascii():
364-
raise ValueError(f"Invalid isoformat string: {dtstr!r}")
365-
def _read(s, n):
366-
# dtstr is ASCII, so isdigit() matches only ASCII digits.
367-
if len(s) != n or not s.isdigit():
368-
raise ValueError(f"Invalid isoformat string: {dtstr!r}")
369-
return int(s)
370-
371-
year = _read(dtstr[0:4], 4)
372+
raise ValueError("Invalid isoformat string")
373+
374+
year = _read_isoformat_component(dtstr[0:4], 4)
372375
has_sep = dtstr[4] == '-'
373376

374377
pos = 4 + has_sep
375378
if dtstr[pos:pos + 1] == "W":
376379
# YYYY-?Www-?D?
377380
pos += 1
378-
weekno = _read(dtstr[pos:pos + 2], 2)
381+
weekno = _read_isoformat_component(dtstr[pos:pos + 2], 2)
379382
pos += 2
380383

381384
dayno = 1
@@ -385,17 +388,17 @@ def _read(s, n):
385388

386389
pos += has_sep
387390

388-
dayno = _read(dtstr[pos:pos + 1], 1)
391+
dayno = _read_isoformat_component(dtstr[pos:pos + 1], 1)
389392

390393
return list(_isoweek_to_gregorian(year, weekno, dayno))
391394
else:
392-
month = _read(dtstr[pos:pos + 2], 2)
395+
month = _read_isoformat_component(dtstr[pos:pos + 2], 2)
393396
pos += 2
394397
if (dtstr[pos:pos + 1] == "-") != has_sep:
395398
raise ValueError("Inconsistent use of dash separator")
396399

397400
pos += has_sep
398-
day = _read(dtstr[pos:pos + 2], 2)
401+
day = _read_isoformat_component(dtstr[pos:pos + 2], 2)
399402

400403
return [year, month, day]
401404

@@ -413,7 +416,7 @@ def _parse_hh_mm_ss_ff(tstr):
413416
if (len_str - pos) < 2:
414417
raise ValueError("Incomplete time component")
415418

416-
time_comps[comp] = int(tstr[pos:pos+2])
419+
time_comps[comp] = _read_isoformat_component(tstr[pos:pos+2], 2)
417420

418421
pos += 2
419422
next_char = tstr[pos:pos+1]
@@ -455,6 +458,8 @@ def _parse_isoformat_time(tstr):
455458
len_str = len(tstr)
456459
if len_str < 2:
457460
raise ValueError("Isoformat time too short")
461+
if not tstr.isascii():
462+
raise ValueError("Invalid isoformat string")
458463

459464
# This is equivalent to re.search('[+-Z]', tstr), but faster
460465
tz_pos = (tstr.find('-') + 1 or tstr.find('+') + 1 or tstr.find('Z') + 1)

Lib/test/datetimetester.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2116,7 +2116,7 @@ def test_fromisoformat_fails(self):
21162116
'2020123', # 7 chars: day slice reads a 1-character tail
21172117
'9999121', # 7 chars: day slice reads a 1-character tail
21182118
'2020-W2', # 1-digit week number
2119-
'٢025-03-09' # Unicode characters
2119+
'٢025-03-09', # Unicode characters
21202120
'2009\ud80002\ud80028', # Separators are surrogate codepoints
21212121
]
21222122

@@ -3768,6 +3768,9 @@ def test_fromisoformat_fails_datetime(self):
37683768
'2009-04-19T12:30:45-00:90:00', # Time zone field out from range
37693769
'2009-04-19T12:30:45-00:00:90', # Time zone field out from range
37703770
'2020-2020', # Ambiguous 9-char date portion
3771+
'2009-04-19T12:30:4٥', # Unicode digit in the seconds
3772+
'20201212T0102٣٤', # Unicode digits in the time (gh-152204)
3773+
'2009-04-19T12:30:45+0٥:00', # Unicode digit in the timezone
37713774
]
37723775

37733776
for bad_str in bad_strs:

0 commit comments

Comments
 (0)