Skip to content

Commit 9a24c1c

Browse files
[3.13] gh-153406: Raise ValueError, not OverflowError, for out-of-range dates in email.utils.parsedate_to_datetime (GH-153407) (#153433)
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 37a26b9) Co-authored-by: tonghuaroot (童话) <tonghuaroot@gmail.com>
1 parent 7994b41 commit 9a24c1c

4 files changed

Lines changed: 27 additions & 4 deletions

File tree

Lib/email/utils.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -317,10 +317,13 @@ def parsedate_to_datetime(data):
317317
if parsed_date_tz is None:
318318
raise ValueError('Invalid date value or format "%s"' % str(data))
319319
*dtuple, tz = parsed_date_tz
320-
if tz is None:
321-
return datetime.datetime(*dtuple[:6])
322-
return datetime.datetime(*dtuple[:6],
323-
tzinfo=datetime.timezone(datetime.timedelta(seconds=tz)))
320+
try:
321+
if tz is None:
322+
return datetime.datetime(*dtuple[:6])
323+
return datetime.datetime(*dtuple[:6],
324+
tzinfo=datetime.timezone(datetime.timedelta(seconds=tz)))
325+
except OverflowError as exc:
326+
raise ValueError('Invalid date value or format "%s"' % str(data)) from exc
324327

325328

326329
def parseaddr(addr, *, strict=True):

Lib/test/test_email/test_headerregistry.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,14 @@ def test_invalid_date_value(self):
221221
self.assertEqual(len(h.defects), 1)
222222
self.assertIsInstance(h.defects[0], errors.InvalidDateDefect)
223223

224+
def test_out_of_range_date_value(self):
225+
s = 'Mon, 20 Nov 9999999999 12:00:00 +0000'
226+
h = self.make_header('date', s)
227+
self.assertEqual(h, s)
228+
self.assertIsNone(h.datetime)
229+
self.assertEqual(len(h.defects), 1)
230+
self.assertIsInstance(h.defects[0], errors.InvalidDateDefect)
231+
224232
def test_datetime_read_only(self):
225233
h = self.make_header('date', self.datestring)
226234
with self.assertRaises(AttributeError):

Lib/test/test_email/test_utils.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,15 @@ def test_parsedate_to_datetime_with_invalid_raises_valueerror(self):
6969
with self.subTest(dtstr=dtstr):
7070
self.assertRaises(ValueError, utils.parsedate_to_datetime, dtstr)
7171

72+
def test_parsedate_to_datetime_out_of_range_raises_valueerror(self):
73+
out_of_range_dates = [
74+
'Mon, 20 Nov 9999999999 12:00:00 +0000',
75+
'Mon, 20 Nov 2017 12:00:00 +24000000000000',
76+
]
77+
for dtstr in out_of_range_dates:
78+
with self.subTest(dtstr=dtstr):
79+
self.assertRaises(ValueError, utils.parsedate_to_datetime, dtstr)
80+
7281
class LocaltimeTests(unittest.TestCase):
7382

7483
def test_localtime_is_tz_aware_daylight_true(self):
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
:func:`email.utils.parsedate_to_datetime` now raises :exc:`ValueError`
2+
instead of :exc:`OverflowError` when the parsed year or timezone offset is
3+
out of range, matching its documented behavior.

0 commit comments

Comments
 (0)