From 54ecfa5e88573fd278fe69ecaf74039417325271 Mon Sep 17 00:00:00 2001 From: tonghuaroot Date: Thu, 25 Jun 2026 07:08:26 +0000 Subject: [PATCH 1/3] gh-152157: Reject empty fraction before timezone in C fromisoformat The C accelerator for datetime.fromisoformat() and time.fromisoformat() accepted a decimal separator (. or ,) with no following digit when a timezone designator came next, e.g. '12:34:56.+05:00', while the pure-Python implementation correctly raised ValueError. Handle the decimal-separator case before the generic end-of-substring check so an empty fraction is rejected. --- Lib/test/datetimetester.py | 8 ++++++++ ...26-06-25-07-08-17.gh-issue-152157.dt5Ef0.rst | 5 +++++ Modules/_datetimemodule.c | 17 ++++++++++------- 3 files changed, 23 insertions(+), 7 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-06-25-07-08-17.gh-issue-152157.dt5Ef0.rst diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index e29f5e3ecb5fd4f..34101f63c743645 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -3758,6 +3758,10 @@ def test_fromisoformat_fails_datetime(self): '2009-04-19T12:30:45-00:90:00', # Time zone field out from range '2009-04-19T12:30:45-00:00:90', # Time zone field out from range '2020-2020', # Ambiguous 9-char date portion + '2009-04-19T12:30:45.+05:00', # Empty fraction before offset + '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 ] for bad_str in bad_strs: @@ -5003,6 +5007,10 @@ def test_fromisoformat_fails(self): '24:01:00.000000', # Has non-zero minutes on 24:00 '12:30:45+00:90:00', # Time zone field out from range '12:30:45+00:00:90', # Time zone field out from range + '12:30:45.+05:00', # Empty fraction before offset + '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 ] for bad_str in bad_strs: diff --git a/Misc/NEWS.d/next/Library/2026-06-25-07-08-17.gh-issue-152157.dt5Ef0.rst b/Misc/NEWS.d/next/Library/2026-06-25-07-08-17.gh-issue-152157.dt5Ef0.rst new file mode 100644 index 000000000000000..65042ca5aa08bf0 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-25-07-08-17.gh-issue-152157.dt5Ef0.rst @@ -0,0 +1,5 @@ +:meth:`~datetime.datetime.fromisoformat` and :meth:`~datetime.time.fromisoformat` +now reject a decimal separator (``.`` or ``,``) that is not followed by any +fractional digit before a timezone designator, such as ``'12:34:56.+05:00'``. +The C implementation previously accepted such strings while the pure-Python +implementation correctly raised :exc:`ValueError`. diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 979aa1beb8657b2..202dca5b924bffc 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -1034,7 +1034,16 @@ parse_hh_mm_ss_ff(const char *tstr, const char *tstr_end, int *hour, has_separator = (c == ':'); } - if (p >= p_end) { + if (c == '.' || c == ',') { + if (i < 2) { + return -3; // Decimal mark on hour or minute + } + if (p >= p_end) { + return -3; // Decimal mark not followed by any digit + } + break; + } + else if (p >= p_end) { return c != '\0'; } else if (has_separator && (c == ':')) { @@ -1042,12 +1051,6 @@ parse_hh_mm_ss_ff(const char *tstr, const char *tstr_end, int *hour, return -4; // Malformed microsecond separator } continue; - } - else if (c == '.' || c == ',') { - if (i < 2) { - return -3; // Decimal mark on hour or minute - } - break; } else if (!has_separator) { --p; } else { From 4dc17e25bf1d6cd293e6689599a13df99be463d8 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Tue, 30 Jun 2026 19:22:36 +0200 Subject: [PATCH 2/3] Some minor PEP 7 fixes --- Modules/_datetimemodule.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 202dca5b924bffc..fa35af7177d8712 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -1036,7 +1036,7 @@ parse_hh_mm_ss_ff(const char *tstr, const char *tstr_end, int *hour, if (c == '.' || c == ',') { if (i < 2) { - return -3; // Decimal mark on hour or minute + return -3; // Decimal mark on hour or minute } if (p >= p_end) { return -3; // Decimal mark not followed by any digit @@ -1051,9 +1051,11 @@ parse_hh_mm_ss_ff(const char *tstr, const char *tstr_end, int *hour, return -4; // Malformed microsecond separator } continue; - } else if (!has_separator) { + } + else if (!has_separator) { --p; - } else { + } + else { return -4; // Malformed time separator } } From e03e051b240bef4227787859969da9a892e30555 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Tue, 30 Jun 2026 19:23:43 +0200 Subject: [PATCH 3/3] Succinct news entry --- .../2026-06-25-07-08-17.gh-issue-152157.dt5Ef0.rst | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Misc/NEWS.d/next/Library/2026-06-25-07-08-17.gh-issue-152157.dt5Ef0.rst b/Misc/NEWS.d/next/Library/2026-06-25-07-08-17.gh-issue-152157.dt5Ef0.rst index 65042ca5aa08bf0..00e83b4af6be7a9 100644 --- a/Misc/NEWS.d/next/Library/2026-06-25-07-08-17.gh-issue-152157.dt5Ef0.rst +++ b/Misc/NEWS.d/next/Library/2026-06-25-07-08-17.gh-issue-152157.dt5Ef0.rst @@ -1,5 +1,3 @@ -:meth:`~datetime.datetime.fromisoformat` and :meth:`~datetime.time.fromisoformat` -now reject a decimal separator (``.`` or ``,``) that is not followed by any -fractional digit before a timezone designator, such as ``'12:34:56.+05:00'``. -The C implementation previously accepted such strings while the pure-Python -implementation correctly raised :exc:`ValueError`. +The C implementations of :meth:`~datetime.datetime.fromisoformat` and :meth:`~datetime.time.fromisoformat` +now reject a decimal separator that is not followed by any +fractional digit before a timezone designator.