Skip to content
Merged
4 changes: 4 additions & 0 deletions Lib/test/datetimetester.py
Original file line number Diff line number Diff line change
Expand Up @@ -1927,6 +1927,10 @@ def test_fromisoformat_fails(self):
'2009-02-29', # Invalid leap day
'2019-W53-1', # No week 53 in 2019
'2020-W54-1', # No week 54
'0000-W25-1', # Invalid year
'10000-W25-1', # Invalid year
'2020-W25-0', # Invalid day-of-week
'2020-W25-8', # Invalid day-of-week
'2009\ud80002\ud80028', # Separators are surrogate codepoints
]

Expand Down
1 change: 1 addition & 0 deletions Misc/ACKS
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,7 @@ David Edelsohn
John Edmonds
Benjamin Edwards
Grant Edwards
Vlad Efanov
Zvi Effron
John Ehresman
Tal Einat
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Improve validation logic in the C implementation of :meth:`datetime.fromisoformat`
to better handle invalid years. Patch by Vlad Efanov.
16 changes: 9 additions & 7 deletions Modules/_datetimemodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,10 @@ iso_week1_monday(int year)
static int
iso_to_ymd(const int iso_year, const int iso_week, const int iso_day,
int *year, int *month, int *day) {
// Year is bounded to 0 < year < 10000 because 9999-12-31 is (9999, 52, 5)
if (iso_year < MINYEAR || iso_year > MAXYEAR) {
return -4;
}
if (iso_week <= 0 || iso_week >= 53) {
int out_of_range = 1;
if (iso_week == 53) {
Expand Down Expand Up @@ -762,7 +766,7 @@ parse_isoformat_date(const char *dtstr, const size_t len, int *year, int *month,
* -2: Inconsistent date separator usage
* -3: Failed to parse ISO week.
* -4: Failed to parse ISO day.
* -5, -6: Failure in iso_to_ymd
* -5, -6, -7: Failure in iso_to_ymd
*/
const char *p = dtstr;
p = parse_digits(p, year, 4);
Expand Down Expand Up @@ -3142,15 +3146,13 @@ date_fromisocalendar(PyObject *cls, PyObject *args, PyObject *kw)
return NULL;
}

// Year is bounded to 0 < year < 10000 because 9999-12-31 is (9999, 52, 5)
if (year < MINYEAR || year > MAXYEAR) {
PyErr_Format(PyExc_ValueError, "Year is out of range: %d", year);
return NULL;
}

int month;
int rv = iso_to_ymd(year, week, day, &year, &month, &day);

if (rv == -4) {
PyErr_Format(PyExc_ValueError, "Year is out of range: %d", year);
return NULL;
}

if (rv == -2) {
PyErr_Format(PyExc_ValueError, "Invalid week: %d", week);
Expand Down