https://github.com/python/cpython/commit/e0fda794fadb09f08a5d1a04aa6a7e0b42a99666 commit: e0fda794fadb09f08a5d1a04aa6a7e0b42a99666 branch: main author: Stan Ulbrych <89152624+stanfromirel...@users.noreply.github.com> committer: gpshead <g...@krypto.org> date: 2025-03-20T20:47:09-07:00 summary:
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. files: A Misc/NEWS.d/next/Library/2025-03-16-18-30-00.gh-issue-70647.1qq2r3.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 26bcd1e491d78c..50e21a12335611 100644 --- a/Lib/_pydatetime.py +++ b/Lib/_pydatetime.py @@ -576,7 +576,7 @@ def _check_date_fields(year, month, day): raise ValueError(f"month must be in 1..12, not {month}") dim = _days_in_month(year, month) if not 1 <= day <= dim: - raise ValueError(f"day must be in 1..{dim}, not {day}") + raise ValueError(f"day {day} must be in range 1..{dim} for month {month} in year {year}") return year, month, day def _check_time_fields(hour, minute, second, microsecond, fold): diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index 84eb872f964ba1..f9d20ef9c626a9 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -1988,8 +1988,6 @@ def test_valuerror_messages(self): r"(year|month|day) must be in \d+\.\.\d+, not \d+" ) test_cases = [ - (2009, 1, 32), # Day out of range - (2009, 2, 31), # Day out of range (2009, 13, 1), # Month out of range (2009, 0, 1), # Month out of range (10000, 12, 31), # Year out of range @@ -2000,6 +1998,11 @@ def test_valuerror_messages(self): with self.assertRaisesRegex(ValueError, pattern): self.theclass(*case) + # days out of range have their own error message, see issue 70647 + with self.assertRaises(ValueError) as msg: + self.theclass(2009, 1, 32) + self.assertIn(f"day 32 must be in range 1..31 for month 1 in year 2009", str(msg.exception)) + def test_fromisoformat(self): # Test that isoformat() is reversible base_dates = [ @@ -3259,7 +3262,6 @@ def test_valuerror_messages(self): (2009, 4, 1, 12, 30, 90), # Second out of range (2009, 4, 1, 12, 90, 45), # Minute out of range (2009, 4, 1, 25, 30, 45), # Hour out of range - (2009, 4, 32, 24, 0, 0), # Day out of range (2009, 13, 1, 24, 0, 0), # Month out of range (9999, 12, 31, 24, 0, 0), # Year out of range ] @@ -3268,6 +3270,11 @@ def test_valuerror_messages(self): with self.assertRaisesRegex(ValueError, pattern): self.theclass(*case) + # days out of range have their own error message, see issue 70647 + with self.assertRaises(ValueError) as msg: + self.theclass(2009, 4, 32, 24, 0, 0) + self.assertIn(f"day 32 must be in range 1..30 for month 4 in year 2009", str(msg.exception)) + def test_fromisoformat_datetime(self): # Test that isoformat() is reversible base_dates = [ @@ -3575,7 +3582,6 @@ def test_fromisoformat_fails_datetime_valueerror(self): "2009-04-01T12:30:90", # Second out of range "2009-04-01T12:90:45", # Minute out of range "2009-04-01T25:30:45", # Hour out of range - "2009-04-32T24:00:00", # Day out of range "2009-13-01T24:00:00", # Month out of range "9999-12-31T24:00:00", # Year out of range ] @@ -3585,6 +3591,11 @@ def test_fromisoformat_fails_datetime_valueerror(self): with self.assertRaisesRegex(ValueError, pattern): self.theclass.fromisoformat(bad_str) + # days out of range have their own error message, see issue 70647 + with self.assertRaises(ValueError) as msg: + self.theclass.fromisoformat("2009-04-32T24:00:00") + self.assertIn(f"day 32 must be in range 1..30 for month 4 in year 2009", str(msg.exception)) + def test_fromisoformat_fails_surrogate(self): # Test that when fromisoformat() fails with a surrogate character as # the separator, the error message contains the original string diff --git a/Misc/NEWS.d/next/Library/2025-03-16-18-30-00.gh-issue-70647.1qq2r3.rst b/Misc/NEWS.d/next/Library/2025-03-16-18-30-00.gh-issue-70647.1qq2r3.rst new file mode 100644 index 00000000000000..33fb244cd563ba --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-03-16-18-30-00.gh-issue-70647.1qq2r3.rst @@ -0,0 +1,2 @@ +When creating a :mod:`datetime` object with an out of range date a more informative +error is raised. diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 70b59d67f56bda..9bba0e3354b26b 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -664,7 +664,8 @@ check_date_args(int year, int month, int day) int dim = days_in_month(year, month); if (day < 1 || day > dim) { PyErr_Format(PyExc_ValueError, - "day must be in 1..%d, not %d", dim, day); + "day %i must be in range 1..%d for month %i in year %i", + day, dim, month, year); return -1; } return 0; _______________________________________________ Python-checkins mailing list -- python-checkins@python.org To unsubscribe send an email to python-checkins-le...@python.org https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: arch...@mail-archive.com