]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.13] gh-153406: Raise ValueError, not OverflowError, for out-of-range dates in...
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Thu, 9 Jul 2026 19:54:19 +0000 (21:54 +0200)
committerGitHub <noreply@github.com>
Thu, 9 Jul 2026 19:54:19 +0000 (15:54 -0400)
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.
(cherry picked from commit 37a26b9b94b147b5d683bfce0ef5a4c2fbc21085)

Co-authored-by: tonghuaroot (童话) <tonghuaroot@gmail.com>
Lib/email/utils.py
Lib/test/test_email/test_headerregistry.py
Lib/test/test_email/test_utils.py
Misc/NEWS.d/next/Library/2026-07-09-00-00-00.gh-issue-153406.vyMmB6.rst [new file with mode: 0644]

index e4d35f06abcc09f17a0d60d0e82f19a18c8596c6..c82b82fb9170f63be97d8eac541f23bb4470f5e9 100644 (file)
@@ -317,10 +317,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):
index ae29ce3426c0169778c6fba03e39029f3582b714..3da9a495a2334f01fcf8ecded018c34ed8fa53d9 100644 (file)
@@ -221,6 +221,14 @@ class TestDateHeader(TestHeaderBase):
         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):
index d04b3909efa643816da3f03971e6c4f8ebbd2da2..ff4a03de9f42a0e3d6f31c5568663cab70cc55b2 100644 (file)
@@ -69,6 +69,15 @@ class DateTimeTests(unittest.TestCase):
             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 (file)
index 0000000..c095757
--- /dev/null
@@ -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.