From 0083db8a0badac0a646b79112cada72643e6f4eb Mon Sep 17 00:00:00 2001 From: SEPURI-SAI-KRISHNA Date: Tue, 4 Aug 2026 20:24:02 +0530 Subject: [PATCH] gh-155175: Reject fractional seconds without a decimal mark in C fromisoformat --- Lib/test/datetimetester.py | 10 ++++++++++ .../2026-08-04-19-40-00.gh-issue-155175.Kf3xQ1.rst | 5 +++++ Modules/_datetimemodule.c | 10 ++++++++++ 3 files changed, 25 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2026-08-04-19-40-00.gh-issue-155175.Kf3xQ1.rst diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index c11e9c068bed3bd..2328cee12db11f0 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -3762,6 +3762,11 @@ def test_fromisoformat_fails_datetime(self): '2009-04-19T12:30:45.-05:00', # Empty fraction before offset '2009-04-19T12:30:45.Z', # Empty fraction before Z '2009-04-19T12:30:45,+05:00', # Empty fraction (comma) before offset + '2009-04-19T12304578', # Fraction without a decimal mark + '2009-04-19T123045789', # Fraction without a decimal mark + '2009-04-19T123045123456789', # Fraction without a decimal mark + '2009-04-19T123045+00000000', # Offset fraction without decimal mark + '2009-04-19T12:30:45+00000000', # Offset fraction without decimal mark ] for bad_str in bad_strs: @@ -5042,6 +5047,11 @@ def test_fromisoformat_fails(self): '12:30:45.-05:00', # Empty fraction before offset '12:30:45.Z', # Empty fraction before Z '12:30:45,+05:00', # Empty fraction (comma) before offset + '12304578', # Fraction without a decimal mark + '123045789', # Fraction without a decimal mark + '123045123456789', # Fraction without a decimal mark + '123045+00000000', # Offset fraction without decimal mark + '12:30:45+00000000', # Offset fraction without decimal mark ] for bad_str in bad_strs: diff --git a/Misc/NEWS.d/next/Library/2026-08-04-19-40-00.gh-issue-155175.Kf3xQ1.rst b/Misc/NEWS.d/next/Library/2026-08-04-19-40-00.gh-issue-155175.Kf3xQ1.rst new file mode 100644 index 000000000000000..9f8a1dffffce3d0 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-08-04-19-40-00.gh-issue-155175.Kf3xQ1.rst @@ -0,0 +1,5 @@ +Fix :meth:`datetime.time.fromisoformat` and +:meth:`datetime.datetime.fromisoformat` in the C implementation accepting a +fractional seconds component that is not introduced by a decimal mark, such +as ``'12345678'`` (previously parsed as ``12:34:56.780000``). Such strings +are now rejected, matching the pure-Python implementation. diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index cd02b298b406e6a..d3eeed3ef757a26 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -1021,6 +1021,7 @@ parse_hh_mm_ss_ff(const char *tstr, const char *tstr_end, int *hour, int *vals[3] = {hour, minute, second}; // This is initialized to satisfy an erroneous compiler warning. unsigned char has_separator = 1; + unsigned char has_fraction = 0; // Parse [HH[:?MM[:?SS]]] 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, if (p >= p_end) { return -3; // Decimal mark not followed by any digit } + has_fraction = 1; break; } else if (p >= p_end) { @@ -1060,6 +1062,14 @@ parse_hh_mm_ss_ff(const char *tstr, const char *tstr_end, int *hour, } } + // A fractional component must be introduced by a decimal mark. Falling + // out of the loop above without having seen one means the basic format + // left unconsumed characters after SS (e.g. "12345678"), which would + // otherwise be silently parsed as a fraction. + if (!has_fraction) { + return -4; // Malformed microsecond separator + } + // Parse fractional components size_t len_remains = p_end - p; size_t to_parse = len_remains;