Skip to content

Commit e0fda79

Browse files
gh-70647: Raise a more informative error for when date is out of range (GH-131335)
More informative error messages mean less debugging what went wrong.
1 parent e356468 commit e0fda79

File tree

4 files changed

+20
-6
lines changed

4 files changed

+20
-6
lines changed

Lib/_pydatetime.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -576,7 +576,7 @@ def _check_date_fields(year, month, day):
576576
raise ValueError(f"month must be in 1..12, not {month}")
577577
dim = _days_in_month(year, month)
578578
if not 1 <= day <= dim:
579-
raise ValueError(f"day must be in 1..{dim}, not {day}")
579+
raise ValueError(f"day {day} must be in range 1..{dim} for month {month} in year {year}")
580580
return year, month, day
581581

582582
def _check_time_fields(hour, minute, second, microsecond, fold):

Lib/test/datetimetester.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1988,8 +1988,6 @@ def test_valuerror_messages(self):
19881988
r"(year|month|day) must be in \d+\.\.\d+, not \d+"
19891989
)
19901990
test_cases = [
1991-
(2009, 1, 32), # Day out of range
1992-
(2009, 2, 31), # Day out of range
19931991
(2009, 13, 1), # Month out of range
19941992
(2009, 0, 1), # Month out of range
19951993
(10000, 12, 31), # Year out of range
@@ -2000,6 +1998,11 @@ def test_valuerror_messages(self):
20001998
with self.assertRaisesRegex(ValueError, pattern):
20011999
self.theclass(*case)
20022000

2001+
# days out of range have their own error message, see issue 70647
2002+
with self.assertRaises(ValueError) as msg:
2003+
self.theclass(2009, 1, 32)
2004+
self.assertIn(f"day 32 must be in range 1..31 for month 1 in year 2009", str(msg.exception))
2005+
20032006
def test_fromisoformat(self):
20042007
# Test that isoformat() is reversible
20052008
base_dates = [
@@ -3259,7 +3262,6 @@ def test_valuerror_messages(self):
32593262
(2009, 4, 1, 12, 30, 90), # Second out of range
32603263
(2009, 4, 1, 12, 90, 45), # Minute out of range
32613264
(2009, 4, 1, 25, 30, 45), # Hour out of range
3262-
(2009, 4, 32, 24, 0, 0), # Day out of range
32633265
(2009, 13, 1, 24, 0, 0), # Month out of range
32643266
(9999, 12, 31, 24, 0, 0), # Year out of range
32653267
]
@@ -3268,6 +3270,11 @@ def test_valuerror_messages(self):
32683270
with self.assertRaisesRegex(ValueError, pattern):
32693271
self.theclass(*case)
32703272

3273+
# days out of range have their own error message, see issue 70647
3274+
with self.assertRaises(ValueError) as msg:
3275+
self.theclass(2009, 4, 32, 24, 0, 0)
3276+
self.assertIn(f"day 32 must be in range 1..30 for month 4 in year 2009", str(msg.exception))
3277+
32713278
def test_fromisoformat_datetime(self):
32723279
# Test that isoformat() is reversible
32733280
base_dates = [
@@ -3575,7 +3582,6 @@ def test_fromisoformat_fails_datetime_valueerror(self):
35753582
"2009-04-01T12:30:90", # Second out of range
35763583
"2009-04-01T12:90:45", # Minute out of range
35773584
"2009-04-01T25:30:45", # Hour out of range
3578-
"2009-04-32T24:00:00", # Day out of range
35793585
"2009-13-01T24:00:00", # Month out of range
35803586
"9999-12-31T24:00:00", # Year out of range
35813587
]
@@ -3585,6 +3591,11 @@ def test_fromisoformat_fails_datetime_valueerror(self):
35853591
with self.assertRaisesRegex(ValueError, pattern):
35863592
self.theclass.fromisoformat(bad_str)
35873593

3594+
# days out of range have their own error message, see issue 70647
3595+
with self.assertRaises(ValueError) as msg:
3596+
self.theclass.fromisoformat("2009-04-32T24:00:00")
3597+
self.assertIn(f"day 32 must be in range 1..30 for month 4 in year 2009", str(msg.exception))
3598+
35883599
def test_fromisoformat_fails_surrogate(self):
35893600
# Test that when fromisoformat() fails with a surrogate character as
35903601
# the separator, the error message contains the original string
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
When creating a :mod:`datetime` object with an out of range date a more informative
2+
error is raised.

Modules/_datetimemodule.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -664,7 +664,8 @@ check_date_args(int year, int month, int day)
664664
int dim = days_in_month(year, month);
665665
if (day < 1 || day > dim) {
666666
PyErr_Format(PyExc_ValueError,
667-
"day must be in 1..%d, not %d", dim, day);
667+
"day %i must be in range 1..%d for month %i in year %i",
668+
day, dim, month, year);
668669
return -1;
669670
}
670671
return 0;

0 commit comments

Comments
 (0)