Skip to content

Commit 9f8f8d9

Browse files
rajoringpshead
andauthored
gh-158501: Reject stray text before a UTC offset in fromisoformat (GH-159224)
The C parser treated the timezone designator as leftover input, then ignored that leftover whenever an offset was present. Strings such as '12:30x+05:00' were accepted even though the pure-Python implementation rejects them. --------- Co-authored-by: Gregory P. Smith <greg@krypto.org>
1 parent aff7a2e commit 9f8f8d9

3 files changed

Lines changed: 23 additions & 12 deletions

File tree

‎Lib/test/datetimetester.py‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3770,6 +3770,10 @@ def test_fromisoformat_fails_datetime(self):
37703770
'2009-04-19T12:30:45.400 +02:30', # Space between ms and timezone (gh-130959)
37713771
'2009-04-19T12:30:45.400 ', # Trailing space (gh-130959)
37723772
'2009-04-19T12:30:45. 400', # Space before fraction (gh-130959)
3773+
'2009-04-19T12:30x+05:00', # Stray text before offset (gh-158501)
3774+
'2009-04-19T12:30:45 +02:00', # Space before offset (gh-158501)
3775+
'2009-04-19T12:30:45.123456 junk Z', # Stray text before Z (gh-158501)
3776+
'20090419T1230:Z', # Stray separator before Z (gh-158501)
37733777
'2009-04-19T12:30:45+00:90:00', # Time zone field out from range
37743778
'2009-04-19T12:30:45+00:00:90', # Time zone field out from range
37753779
'2009-04-19T12:30:45-00:90:00', # Time zone field out from range
@@ -5094,6 +5098,10 @@ def test_fromisoformat_fails(self):
50945098
'12:30:45.400 +02:30', # Space between ms and timezone (gh-130959)
50955099
'12:30:45.400 ', # Trailing space (gh-130959)
50965100
'12:30:45. 400', # Space before fraction (gh-130959)
5101+
'12:30x+05:00', # Stray text before offset (gh-158501)
5102+
'12:30:45 +02:00', # Space before offset (gh-158501)
5103+
'12:30:45.123456 junk Z', # Stray text before Z (gh-158501)
5104+
'1230:Z', # Stray separator before Z (gh-158501)
50975105
'24:00:00.000001', # Has non-zero microseconds on 24:00
50985106
'24:00:01.000000', # Has non-zero seconds on 24:00
50995107
'24:01:00.000000', # Has non-zero minutes on 24:00
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
:meth:`datetime.time.fromisoformat` and
2+
:meth:`datetime.datetime.fromisoformat` now reject stray text between the
3+
time and a UTC offset. The C implementation previously accepted strings
4+
that the pure-Python implementation rejected.

‎Modules/_datetimemodule.c‎

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1046,8 +1046,11 @@ parse_hh_mm_ss_ff(const char *tstr, const char *tstr_end, int *hour,
10461046
}
10471047
break;
10481048
}
1049-
else if (p >= p_end) {
1050-
return c != '\0';
1049+
else if (p > p_end) {
1050+
return 0; // Stopped at p_end: an offset designator or NUL
1051+
}
1052+
else if (p == p_end) {
1053+
return 1; // Stray character just before p_end
10511054
}
10521055
else if (has_separator && (c == ':')) {
10531056
if (i == 2) {
@@ -1087,8 +1090,8 @@ parse_hh_mm_ss_ff(const char *tstr, const char *tstr_end, int *hour,
10871090
++p; // skip truncated digits
10881091
}
10891092

1090-
// Return 1 if it's not the end of the string
1091-
return *p != '\0';
1093+
// Return 1 if there is unparsed text before p_end
1094+
return p != p_end;
10921095
}
10931096

10941097
static int
@@ -1122,15 +1125,11 @@ parse_isoformat_time(const char *dtstr, size_t dtlen, int *hour, int *minute,
11221125
if (rv < 0) {
11231126
return rv;
11241127
}
1128+
else if (rv == 1) {
1129+
return -5; // Unparsed text after the time
1130+
}
11251131
else if (tzinfo_pos == p_end) {
1126-
// We know that there's no time zone, so if there's stuff at the
1127-
// end of the string it's an error.
1128-
if (rv == 1) {
1129-
return -5;
1130-
}
1131-
else {
1132-
return 0;
1133-
}
1132+
return 0;
11341133
}
11351134

11361135
// Special case UTC / Zulu time.

0 commit comments

Comments
 (0)