From a4ecfa9993ecfb5fc6aff0c47cea463ff5918573 Mon Sep 17 00:00:00 2001 From: tonghuaroot Date: Thu, 9 Jul 2026 16:39:51 +0800 Subject: [PATCH] gh-153406: Raise ValueError, not OverflowError, for out-of-range dates in email.utils.parsedate_to_datetime email.utils.parsedate_to_datetime documented that it raises ValueError for an invalid date, but it leaked OverflowError when the parsed year or timezone offset was too large for the datetime and timedelta constructors, and that OverflowError also escaped the modern header parsing path since DateHeader.parse only caught ValueError. Wrap the datetime and timezone construction so an OverflowError is re-raised as a ValueError with the original chained as the cause, which restores the documented contract and lets the existing header handler record an InvalidDateDefect instead of raising. --- Lib/email/utils.py | 11 +++++++---- Lib/test/test_email/test_headerregistry.py | 8 ++++++++ Lib/test/test_email/test_utils.py | 9 +++++++++ .../2026-07-09-00-00-00.gh-issue-153406.vyMmB6.rst | 3 +++ 4 files changed, 27 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-07-09-00-00-00.gh-issue-153406.vyMmB6.rst diff --git a/Lib/email/utils.py b/Lib/email/utils.py index 7aefc24c15dc767..6889c5591bf0306 100644 --- a/Lib/email/utils.py +++ b/Lib/email/utils.py @@ -324,10 +324,13 @@ def parsedate_to_datetime(data): if parsed_date_tz is None: raise ValueError('Invalid date value or format "%s"' % str(data)) *dtuple, tz = parsed_date_tz - if tz is None: - return datetime.datetime(*dtuple[:6]) - return datetime.datetime(*dtuple[:6], - tzinfo=datetime.timezone(datetime.timedelta(seconds=tz))) + try: + if tz is None: + return datetime.datetime(*dtuple[:6]) + return datetime.datetime(*dtuple[:6], + tzinfo=datetime.timezone(datetime.timedelta(seconds=tz))) + except OverflowError as exc: + raise ValueError('Invalid date value or format "%s"' % str(data)) from exc def parseaddr(addr, *, strict=True): diff --git a/Lib/test/test_email/test_headerregistry.py b/Lib/test/test_email/test_headerregistry.py index aa918255d15c37e..c118fd5e20d048f 100644 --- a/Lib/test/test_email/test_headerregistry.py +++ b/Lib/test/test_email/test_headerregistry.py @@ -221,6 +221,14 @@ def test_invalid_date_value(self): self.assertEqual(len(h.defects), 1) self.assertIsInstance(h.defects[0], errors.InvalidDateDefect) + def test_out_of_range_date_value(self): + s = 'Mon, 20 Nov 9999999999 12:00:00 +0000' + h = self.make_header('date', s) + self.assertEqual(h, s) + self.assertIsNone(h.datetime) + self.assertEqual(len(h.defects), 1) + self.assertIsInstance(h.defects[0], errors.InvalidDateDefect) + def test_datetime_read_only(self): h = self.make_header('date', self.datestring) with self.assertRaises(AttributeError): diff --git a/Lib/test/test_email/test_utils.py b/Lib/test/test_email/test_utils.py index c9d09098b502f9d..86d1bfff891a7e6 100644 --- a/Lib/test/test_email/test_utils.py +++ b/Lib/test/test_email/test_utils.py @@ -77,6 +77,15 @@ def test_parsedate_to_datetime_with_invalid_raises_valueerror(self): with self.subTest(dtstr=dtstr): self.assertRaises(ValueError, utils.parsedate_to_datetime, dtstr) + def test_parsedate_to_datetime_out_of_range_raises_valueerror(self): + out_of_range_dates = [ + 'Mon, 20 Nov 9999999999 12:00:00 +0000', + 'Mon, 20 Nov 2017 12:00:00 +24000000000000', + ] + for dtstr in out_of_range_dates: + with self.subTest(dtstr=dtstr): + self.assertRaises(ValueError, utils.parsedate_to_datetime, dtstr) + class LocaltimeTests(unittest.TestCase): def test_localtime_is_tz_aware_daylight_true(self): diff --git a/Misc/NEWS.d/next/Library/2026-07-09-00-00-00.gh-issue-153406.vyMmB6.rst b/Misc/NEWS.d/next/Library/2026-07-09-00-00-00.gh-issue-153406.vyMmB6.rst new file mode 100644 index 000000000000000..c09575754e33804 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-07-09-00-00-00.gh-issue-153406.vyMmB6.rst @@ -0,0 +1,3 @@ +:func:`email.utils.parsedate_to_datetime` now raises :exc:`ValueError` +instead of :exc:`OverflowError` when the parsed year or timezone offset is +out of range, matching its documented behavior.