Skip to content

Commit 0083db8

Browse files
gh-155175: Reject fractional seconds without a decimal mark in C fromisoformat
1 parent ef0affb commit 0083db8

3 files changed

Lines changed: 25 additions & 0 deletions

File tree

Lib/test/datetimetester.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3762,6 +3762,11 @@ def test_fromisoformat_fails_datetime(self):
37623762
'2009-04-19T12:30:45.-05:00', # Empty fraction before offset
37633763
'2009-04-19T12:30:45.Z', # Empty fraction before Z
37643764
'2009-04-19T12:30:45,+05:00', # Empty fraction (comma) before offset
3765+
'2009-04-19T12304578', # Fraction without a decimal mark
3766+
'2009-04-19T123045789', # Fraction without a decimal mark
3767+
'2009-04-19T123045123456789', # Fraction without a decimal mark
3768+
'2009-04-19T123045+00000000', # Offset fraction without decimal mark
3769+
'2009-04-19T12:30:45+00000000', # Offset fraction without decimal mark
37653770
]
37663771

37673772
for bad_str in bad_strs:
@@ -5042,6 +5047,11 @@ def test_fromisoformat_fails(self):
50425047
'12:30:45.-05:00', # Empty fraction before offset
50435048
'12:30:45.Z', # Empty fraction before Z
50445049
'12:30:45,+05:00', # Empty fraction (comma) before offset
5050+
'12304578', # Fraction without a decimal mark
5051+
'123045789', # Fraction without a decimal mark
5052+
'123045123456789', # Fraction without a decimal mark
5053+
'123045+00000000', # Offset fraction without decimal mark
5054+
'12:30:45+00000000', # Offset fraction without decimal mark
50455055
]
50465056

50475057
for bad_str in bad_strs:
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Fix :meth:`datetime.time.fromisoformat` and
2+
:meth:`datetime.datetime.fromisoformat` in the C implementation accepting a
3+
fractional seconds component that is not introduced by a decimal mark, such
4+
as ``'12345678'`` (previously parsed as ``12:34:56.780000``). Such strings
5+
are now rejected, matching the pure-Python implementation.

Modules/_datetimemodule.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1021,6 +1021,7 @@ parse_hh_mm_ss_ff(const char *tstr, const char *tstr_end, int *hour,
10211021
int *vals[3] = {hour, minute, second};
10221022
// This is initialized to satisfy an erroneous compiler warning.
10231023
unsigned char has_separator = 1;
1024+
unsigned char has_fraction = 0;
10241025

10251026
// Parse [HH[:?MM[:?SS]]]
10261027
for (size_t i = 0; i < 3; ++i) {
@@ -1041,6 +1042,7 @@ parse_hh_mm_ss_ff(const char *tstr, const char *tstr_end, int *hour,
10411042
if (p >= p_end) {
10421043
return -3; // Decimal mark not followed by any digit
10431044
}
1045+
has_fraction = 1;
10441046
break;
10451047
}
10461048
else if (p >= p_end) {
@@ -1060,6 +1062,14 @@ parse_hh_mm_ss_ff(const char *tstr, const char *tstr_end, int *hour,
10601062
}
10611063
}
10621064

1065+
// A fractional component must be introduced by a decimal mark. Falling
1066+
// out of the loop above without having seen one means the basic format
1067+
// left unconsumed characters after SS (e.g. "12345678"), which would
1068+
// otherwise be silently parsed as a fraction.
1069+
if (!has_fraction) {
1070+
return -4; // Malformed microsecond separator
1071+
}
1072+
10631073
// Parse fractional components
10641074
size_t len_remains = p_end - p;
10651075
size_t to_parse = len_remains;

0 commit comments

Comments
 (0)