https://github.com/python/cpython/commit/17a64027984f1aa417e451f6d56afa88d070906d
commit: 17a64027984f1aa417e451f6d56afa88d070906d
branch: main
author: Saugat Pandey <[email protected]>
committer: StanFromIreland <[email protected]>
date: 2026-11-06T16:41:33Z
summary:

gh-158500: Reject trailing characters in `datetime.date.fromisoformat()` 
(#160737)

Co-authored-by: Stan Ulbrych <[email protected]>

files:
A Misc/NEWS.d/next/Library/2026-10-26-21-37-42.gh-issue-158500.zxyuq5.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 5bbb6d433ee2ad6..5a8fec43e668a53 100644
--- a/Lib/_pydatetime.py
+++ b/Lib/_pydatetime.py
@@ -396,6 +396,10 @@ def _parse_isoformat_date(dtstr):
             pos += has_sep
 
             dayno = _read_isoformat_component(dtstr[pos:pos + 1], 1)
+            pos += 1
+
+        if pos != len(dtstr):
+            raise ValueError("Invalid isoformat string")
 
         return list(_isoweek_to_gregorian(year, weekno, dayno))
     else:
@@ -406,6 +410,10 @@ def _parse_isoformat_date(dtstr):
 
         pos += has_sep
         day = _read_isoformat_component(dtstr[pos:pos + 2], 2)
+        pos += 2
+
+        if pos != len(dtstr):
+            raise ValueError("Invalid isoformat string")
 
         return [year, month, day]
 
@@ -1931,7 +1939,7 @@ def fromisoformat(cls, date_string):
             raise ValueError(
                 f'Invalid isoformat string: {date_string!r}') from None
 
-        if tstr:
+        if separator_location < len(date_string):
             try:
                 (time_components,
                  became_next_day,
diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py
index 92607e381db20c7..7d242f663824b39 100644
--- a/Lib/test/datetimetester.py
+++ b/Lib/test/datetimetester.py
@@ -2243,6 +2243,10 @@ def test_fromisoformat_fails(self):
             '2020-W 5',         # space in the week number
             '2020061',          # 7 chars: day slice reads a 1-character tail
             '2020-W2',          # 1-digit week number
+            # gh-158500: trailing characters must not be ignored
+            '2020010112',       # YYYYMMDD followed by two characters
+            '2020W011xx',       # YYYYWwwD followed by two characters
+            '20200101\u00e9',   # 9 characters, but 10 bytes in UTF-8
             '٢025-03-09',       # Unicode characters
             '2009\ud80002\ud80028',     # Separators are surrogate codepoints
         ]
@@ -3984,6 +3988,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
+            # gh-158500: a separator must be followed by a time
+            '2009-04-19T',                  # Separator without a time
+            '20090419T',                    # Separator without a time
+            '2009-W16-7T',                  # Separator without a time
+            '2009W167T',                    # Separator without a time
         ]
 
         for bad_str in bad_strs:
diff --git 
a/Misc/NEWS.d/next/Library/2026-10-26-21-37-42.gh-issue-158500.zxyuq5.rst 
b/Misc/NEWS.d/next/Library/2026-10-26-21-37-42.gh-issue-158500.zxyuq5.rst
new file mode 100644
index 000000000000000..98588df2f8ce83a
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-10-26-21-37-42.gh-issue-158500.zxyuq5.rst
@@ -0,0 +1,4 @@
+:meth:`datetime.date.fromisoformat` now rejects strings with trailing
+characters after a basic-format date, such as ``'2020010112'``. The pure-Python
+:meth:`datetime.datetime.fromisoformat` now rejects a date followed by a bare
+separator, such as ``'2020-01-01T'``, matching the C implementation.
diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c
index 959abcf5e421cf5..380d8b435e73d6c 100644
--- a/Modules/_datetimemodule.c
+++ b/Modules/_datetimemodule.c
@@ -977,6 +977,10 @@ parse_isoformat_date(const char *dtstr, const size_t len, 
int *year, int *month,
             iso_day = 1;
         }
 
+        if ((size_t)(p - dtstr) != len) {
+            return -1;
+        }
+
         int rv = iso_to_ymd(*year, iso_week, iso_day, year, month, day);
         if (rv) {
             return -3 + rv;
@@ -997,6 +1001,9 @@ parse_isoformat_date(const char *dtstr, const size_t len, 
int *year, int *month,
     if (p == NULL) {
         return -1;
     }
+    if ((size_t)(p - dtstr) != len) {
+        return -1;
+    }
     return 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