Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Lib/test/datetimetester.py
Original file line number Diff line number Diff line change
Expand Up @@ -1908,6 +1908,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 @@ -492,6 +492,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 @@ -392,6 +392,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 @@ -738,7 +742,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 @@ -3097,15 +3101,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