Skip to content

Commit d5f1139

Browse files
gh-117534: Add checking for input parameter in iso_to_ymd (#117543)
Moves the validation for invalid years in the C implementation of the `datetime` module into a common location between `fromisoformat` and `fromisocalendar`, which improves the error message and fixes a failed assertion when parsing invalid ISO 8601 years using one of the "ISO weeks" formats. --------- Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
1 parent a25c02e commit d5f1139

File tree

4 files changed

+16
-7
lines changed

4 files changed

+16
-7
lines changed

Diff for: Lib/test/datetimetester.py

+4
Original file line numberDiff line numberDiff line change
@@ -1927,6 +1927,10 @@ def test_fromisoformat_fails(self):
19271927
'2009-02-29', # Invalid leap day
19281928
'2019-W53-1', # No week 53 in 2019
19291929
'2020-W54-1', # No week 54
1930+
'0000-W25-1', # Invalid year
1931+
'10000-W25-1', # Invalid year
1932+
'2020-W25-0', # Invalid day-of-week
1933+
'2020-W25-8', # Invalid day-of-week
19301934
'2009\ud80002\ud80028', # Separators are surrogate codepoints
19311935
]
19321936

Diff for: Misc/ACKS

+1
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,7 @@ David Edelsohn
496496
John Edmonds
497497
Benjamin Edwards
498498
Grant Edwards
499+
Vlad Efanov
499500
Zvi Effron
500501
John Ehresman
501502
Tal Einat
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Improve validation logic in the C implementation of :meth:`datetime.fromisoformat`
2+
to better handle invalid years. Patch by Vlad Efanov.

Diff for: Modules/_datetimemodule.c

+9-7
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,10 @@ iso_week1_monday(int year)
416416
static int
417417
iso_to_ymd(const int iso_year, const int iso_week, const int iso_day,
418418
int *year, int *month, int *day) {
419+
// Year is bounded to 0 < year < 10000 because 9999-12-31 is (9999, 52, 5)
420+
if (iso_year < MINYEAR || iso_year > MAXYEAR) {
421+
return -4;
422+
}
419423
if (iso_week <= 0 || iso_week >= 53) {
420424
int out_of_range = 1;
421425
if (iso_week == 53) {
@@ -762,7 +766,7 @@ parse_isoformat_date(const char *dtstr, const size_t len, int *year, int *month,
762766
* -2: Inconsistent date separator usage
763767
* -3: Failed to parse ISO week.
764768
* -4: Failed to parse ISO day.
765-
* -5, -6: Failure in iso_to_ymd
769+
* -5, -6, -7: Failure in iso_to_ymd
766770
*/
767771
const char *p = dtstr;
768772
p = parse_digits(p, year, 4);
@@ -3142,15 +3146,13 @@ date_fromisocalendar(PyObject *cls, PyObject *args, PyObject *kw)
31423146
return NULL;
31433147
}
31443148

3145-
// Year is bounded to 0 < year < 10000 because 9999-12-31 is (9999, 52, 5)
3146-
if (year < MINYEAR || year > MAXYEAR) {
3147-
PyErr_Format(PyExc_ValueError, "Year is out of range: %d", year);
3148-
return NULL;
3149-
}
3150-
31513149
int month;
31523150
int rv = iso_to_ymd(year, week, day, &year, &month, &day);
31533151

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

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

0 commit comments

Comments
 (0)