https://github.com/python/cpython/commit/9c23ee02e5ae7a8dacc7eb0690a8859d8bf4cef6
commit: 9c23ee02e5ae7a8dacc7eb0690a8859d8bf4cef6
branch: 3.14
author: Miss Islington (bot) <[email protected]>
committer: StanFromIreland <[email protected]>
date: 2026-06-20T16:59:20Z
summary:

[3.14] gh-151770: Fix `datetime.fromisoformat()` on an out-of-range month w/ a 
24:00 time (GH-151771) (#151810)

(cherry picked from commit 1fb874cc076e771c39a7bbc650dce386e3c5b7a0)

Co-authored-by: tonghuaroot (童话) <[email protected]>
Co-authored-by: Stan Ulbrych <[email protected]>

files:
A Misc/NEWS.d/next/Library/2026-06-20-15-00-00.gh-issue-151770.dtiso0.rst
M Lib/_pydatetime.py
M Lib/test/datetimetester.py
M Modules/_datetimemodule.c

diff --git a/Lib/_pydatetime.py b/Lib/_pydatetime.py
index 70251dbb6535d26..a353fa6dc17a535 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]
@@ -1947,7 +1947,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 c1bb6138a1beb7b..80891903a5e53be 100644
--- a/Lib/test/datetimetester.py
+++ b/Lib/test/datetimetester.py
@@ -3597,6 +3597,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 b3b76acc97ee0a9..b4a93819b242804 100644
--- a/Modules/_datetimemodule.c
+++ b/Modules/_datetimemodule.c
@@ -5981,7 +5981,7 @@ datetime_fromisoformat(PyObject *cls, PyObject *dtstr)
         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) {

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to