From 3a4d379baba411e3277c42a20d6d667c60b2ab0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?tonghuaroot=20=28=E7=AB=A5=E8=AF=9D=29?= Date: Sun, 21 Jun 2026 00:33:16 +0800 Subject: [PATCH] gh-151770: Fix `datetime.fromisoformat()` on an out-of-range month w/ a 24:00 time (GH-151771) (cherry picked from commit 1fb874cc076e771c39a7bbc650dce386e3c5b7a0) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: tonghuaroot (童话) Co-authored-by: Stan Ulbrych --- Lib/_pydatetime.py | 4 ++-- Lib/test/datetimetester.py | 1 + .../Library/2026-06-20-15-00-00.gh-issue-151770.dtiso0.rst | 3 +++ Modules/_datetimemodule.c | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-06-20-15-00-00.gh-issue-151770.dtiso0.rst diff --git a/Lib/_pydatetime.py b/Lib/_pydatetime.py index b6d68f2372850a7..c1448374402de4a 100644 --- a/Lib/_pydatetime.py +++ b/Lib/_pydatetime.py @@ -55,7 +55,7 @@ def _days_before_year(year): def _days_in_month(year, month): "year, month -> number of days in that month in that year." - assert 1 <= month <= 12, month + assert 1 <= month <= 12, f"month must be in 1..12, not {month}" if month == 2 and _is_leap(year): return 29 return _DAYS_IN_MONTH[month] @@ -1987,7 +1987,7 @@ def fromisoformat(cls, date_string): if became_next_day: year, month, day = date_components # Only wrap day/month when it was previously valid - if month <= 12 and day <= (days_in_month := _days_in_month(year, month)): + if 1 <= month <= 12 and day <= (days_in_month := _days_in_month(year, month)): # Calculate midnight of the next day day += 1 if day > days_in_month: diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index d26e41982deb811..1cbe78c1ecbfdc6 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -3773,6 +3773,7 @@ def test_fromisoformat_fails_datetime_valueerror(self): "2009-04-01T12:30:90", # Second out of range "2009-04-01T12:90:45", # Minute out of range "2009-04-01T25:30:45", # Hour out of range + "2009-00-01T24:00:00", # Month below range "2009-13-01T24:00:00", # Month out of range "9999-12-31T24:00:00", # Year out of range ] diff --git a/Misc/NEWS.d/next/Library/2026-06-20-15-00-00.gh-issue-151770.dtiso0.rst b/Misc/NEWS.d/next/Library/2026-06-20-15-00-00.gh-issue-151770.dtiso0.rst new file mode 100644 index 000000000000000..10b3db8efa42b0f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-20-15-00-00.gh-issue-151770.dtiso0.rst @@ -0,0 +1,3 @@ +Fix :meth:`datetime.datetime.fromisoformat` raising :exc:`AssertionError` +instead of :exc:`ValueError` for an out-of-range month combined with a +``24:00`` time. diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 789e9a8b1488b9d..21bb911d0fb03f1 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -6144,7 +6144,7 @@ datetime_datetime_fromisoformat_impl(PyTypeObject *type, PyObject *string) goto error; } - if ((hour == 24) && (month <= 12)) { + if ((hour == 24) && (month >= 1 && month <= 12)) { int d_in_month = days_in_month(year, month); if (day <= d_in_month) { if (minute == 0 && second == 0 && microsecond == 0) {