2011-07-20 12:41:21 -03:00
|
|
|
import datetime
|
|
|
|
from email import utils
|
2012-05-26 00:22:59 -03:00
|
|
|
import test.support
|
|
|
|
import time
|
2011-07-20 12:41:21 -03:00
|
|
|
import unittest
|
2012-08-22 23:06:37 -03:00
|
|
|
import sys
|
2013-05-08 05:16:02 -03:00
|
|
|
import os.path
|
2011-07-20 12:41:21 -03:00
|
|
|
|
|
|
|
class DateTimeTests(unittest.TestCase):
|
|
|
|
|
|
|
|
datestring = 'Sun, 23 Sep 2001 20:10:55'
|
|
|
|
dateargs = (2001, 9, 23, 20, 10, 55)
|
|
|
|
offsetstring = ' -0700'
|
|
|
|
utcoffset = datetime.timedelta(hours=-7)
|
|
|
|
tz = datetime.timezone(utcoffset)
|
|
|
|
naive_dt = datetime.datetime(*dateargs)
|
|
|
|
aware_dt = datetime.datetime(*dateargs, tzinfo=tz)
|
|
|
|
|
|
|
|
def test_naive_datetime(self):
|
|
|
|
self.assertEqual(utils.format_datetime(self.naive_dt),
|
|
|
|
self.datestring + ' -0000')
|
|
|
|
|
|
|
|
def test_aware_datetime(self):
|
|
|
|
self.assertEqual(utils.format_datetime(self.aware_dt),
|
|
|
|
self.datestring + self.offsetstring)
|
|
|
|
|
|
|
|
def test_usegmt(self):
|
|
|
|
utc_dt = datetime.datetime(*self.dateargs,
|
|
|
|
tzinfo=datetime.timezone.utc)
|
|
|
|
self.assertEqual(utils.format_datetime(utc_dt, usegmt=True),
|
|
|
|
self.datestring + ' GMT')
|
|
|
|
|
|
|
|
def test_usegmt_with_naive_datetime_raises(self):
|
|
|
|
with self.assertRaises(ValueError):
|
|
|
|
utils.format_datetime(self.naive_dt, usegmt=True)
|
|
|
|
|
|
|
|
def test_usegmt_with_non_utc_datetime_raises(self):
|
|
|
|
with self.assertRaises(ValueError):
|
|
|
|
utils.format_datetime(self.aware_dt, usegmt=True)
|
|
|
|
|
|
|
|
def test_parsedate_to_datetime(self):
|
|
|
|
self.assertEqual(
|
|
|
|
utils.parsedate_to_datetime(self.datestring + self.offsetstring),
|
|
|
|
self.aware_dt)
|
|
|
|
|
|
|
|
def test_parsedate_to_datetime_naive(self):
|
|
|
|
self.assertEqual(
|
|
|
|
utils.parsedate_to_datetime(self.datestring + ' -0000'),
|
|
|
|
self.naive_dt)
|
2012-05-26 00:22:59 -03:00
|
|
|
|
bpo-30681: Support invalid date format or value in email Date header (GH-22090)
I am re-submitting an older PR which was abandoned but is still relevant, #10783 by @timb07.
The issue being solved () is still relevant. The original PR #10783 was closed as
the final request changes were not applied and since abandoned.
In this new PR I have re-used the original patch plus applied both comments from the review, by @maxking and @pganssle.
For reference, here is the original PR description:
In email.utils.parsedate_to_datetime(), a failure to parse the date, or invalid date components (such as hour outside 0..23) raises an exception. Document this behaviour, and add tests to test_email/test_utils.py to confirm this behaviour.
In email.headerregistry.DateHeader.parse(), check when parsedate_to_datetime() raises an exception and add a new defect InvalidDateDefect; preserve the invalid value as the string value of the header, but set the datetime attribute to None.
Add tests to test_email/test_headerregistry.py to confirm this behaviour; also added test to test_email/test_inversion.py to confirm emails with such defective date headers round trip successfully.
This pull request incorporates feedback gratefully received from @bitdancer, @brettcannon, @Mariatta and @warsaw, and replaces the earlier PR #2254.
Automerge-Triggered-By: GH:warsaw
2020-10-26 21:31:06 -03:00
|
|
|
def test_parsedate_to_datetime_with_invalid_raises_valueerror(self):
|
2022-07-25 03:17:25 -03:00
|
|
|
# See also test_parsedate_returns_None_for_invalid_strings in test_email.
|
|
|
|
invalid_dates = [
|
|
|
|
'',
|
|
|
|
' ',
|
|
|
|
'0',
|
|
|
|
'A Complete Waste of Time',
|
|
|
|
'Wed, 3 Apr 2002 12.34.56.78+0800'
|
|
|
|
'Tue, 06 Jun 2017 27:39:33 +0600',
|
|
|
|
'Tue, 06 Jun 2017 07:39:33 +2600',
|
|
|
|
'Tue, 06 Jun 2017 27:39:33',
|
|
|
|
'17 June , 2022',
|
|
|
|
'Friday, -Nov-82 16:14:55 EST',
|
|
|
|
'Friday, Nov--82 16:14:55 EST',
|
|
|
|
'Friday, 19-Nov- 16:14:55 EST',
|
|
|
|
]
|
bpo-30681: Support invalid date format or value in email Date header (GH-22090)
I am re-submitting an older PR which was abandoned but is still relevant, #10783 by @timb07.
The issue being solved () is still relevant. The original PR #10783 was closed as
the final request changes were not applied and since abandoned.
In this new PR I have re-used the original patch plus applied both comments from the review, by @maxking and @pganssle.
For reference, here is the original PR description:
In email.utils.parsedate_to_datetime(), a failure to parse the date, or invalid date components (such as hour outside 0..23) raises an exception. Document this behaviour, and add tests to test_email/test_utils.py to confirm this behaviour.
In email.headerregistry.DateHeader.parse(), check when parsedate_to_datetime() raises an exception and add a new defect InvalidDateDefect; preserve the invalid value as the string value of the header, but set the datetime attribute to None.
Add tests to test_email/test_headerregistry.py to confirm this behaviour; also added test to test_email/test_inversion.py to confirm emails with such defective date headers round trip successfully.
This pull request incorporates feedback gratefully received from @bitdancer, @brettcannon, @Mariatta and @warsaw, and replaces the earlier PR #2254.
Automerge-Triggered-By: GH:warsaw
2020-10-26 21:31:06 -03:00
|
|
|
for dtstr in invalid_dates:
|
|
|
|
with self.subTest(dtstr=dtstr):
|
|
|
|
self.assertRaises(ValueError, utils.parsedate_to_datetime, dtstr)
|
2012-05-26 00:22:59 -03:00
|
|
|
|
|
|
|
class LocaltimeTests(unittest.TestCase):
|
|
|
|
|
|
|
|
def test_localtime_is_tz_aware_daylight_true(self):
|
|
|
|
test.support.patch(self, time, 'daylight', True)
|
|
|
|
t = utils.localtime()
|
2013-11-16 06:56:23 -04:00
|
|
|
self.assertIsNotNone(t.tzinfo)
|
2012-05-26 00:22:59 -03:00
|
|
|
|
|
|
|
def test_localtime_is_tz_aware_daylight_false(self):
|
|
|
|
test.support.patch(self, time, 'daylight', False)
|
|
|
|
t = utils.localtime()
|
2013-11-16 06:56:23 -04:00
|
|
|
self.assertIsNotNone(t.tzinfo)
|
2012-05-26 00:22:59 -03:00
|
|
|
|
|
|
|
def test_localtime_daylight_true_dst_false(self):
|
|
|
|
test.support.patch(self, time, 'daylight', True)
|
|
|
|
t0 = datetime.datetime(2012, 3, 12, 1, 1)
|
2023-03-19 21:20:20 -03:00
|
|
|
t1 = utils.localtime(t0)
|
2012-05-26 00:22:59 -03:00
|
|
|
t2 = utils.localtime(t1)
|
|
|
|
self.assertEqual(t1, t2)
|
|
|
|
|
|
|
|
def test_localtime_daylight_false_dst_false(self):
|
|
|
|
test.support.patch(self, time, 'daylight', False)
|
|
|
|
t0 = datetime.datetime(2012, 3, 12, 1, 1)
|
2023-03-19 21:20:20 -03:00
|
|
|
t1 = utils.localtime(t0)
|
2012-05-26 00:22:59 -03:00
|
|
|
t2 = utils.localtime(t1)
|
|
|
|
self.assertEqual(t1, t2)
|
|
|
|
|
2018-11-27 07:40:50 -04:00
|
|
|
@test.support.run_with_tz('Europe/Minsk')
|
2012-05-26 00:22:59 -03:00
|
|
|
def test_localtime_daylight_true_dst_true(self):
|
|
|
|
test.support.patch(self, time, 'daylight', True)
|
|
|
|
t0 = datetime.datetime(2012, 3, 12, 1, 1)
|
2023-03-19 21:20:20 -03:00
|
|
|
t1 = utils.localtime(t0)
|
2012-05-26 00:22:59 -03:00
|
|
|
t2 = utils.localtime(t1)
|
|
|
|
self.assertEqual(t1, t2)
|
|
|
|
|
2018-11-27 07:40:50 -04:00
|
|
|
@test.support.run_with_tz('Europe/Minsk')
|
2012-05-26 00:22:59 -03:00
|
|
|
def test_localtime_daylight_false_dst_true(self):
|
|
|
|
test.support.patch(self, time, 'daylight', False)
|
|
|
|
t0 = datetime.datetime(2012, 3, 12, 1, 1)
|
2023-03-19 21:20:20 -03:00
|
|
|
t1 = utils.localtime(t0)
|
2012-05-26 00:22:59 -03:00
|
|
|
t2 = utils.localtime(t1)
|
|
|
|
self.assertEqual(t1, t2)
|
|
|
|
|
2012-08-22 22:34:00 -03:00
|
|
|
@test.support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0')
|
2012-05-26 00:22:59 -03:00
|
|
|
def test_localtime_epoch_utc_daylight_true(self):
|
|
|
|
test.support.patch(self, time, 'daylight', True)
|
2012-11-04 21:06:13 -04:00
|
|
|
t0 = datetime.datetime(1990, 1, 1, tzinfo = datetime.timezone.utc)
|
2012-05-26 00:22:59 -03:00
|
|
|
t1 = utils.localtime(t0)
|
2012-08-22 22:34:00 -03:00
|
|
|
t2 = t0 - datetime.timedelta(hours=5)
|
|
|
|
t2 = t2.replace(tzinfo = datetime.timezone(datetime.timedelta(hours=-5)))
|
|
|
|
self.assertEqual(t1, t2)
|
2012-05-26 00:22:59 -03:00
|
|
|
|
2012-08-22 22:34:00 -03:00
|
|
|
@test.support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0')
|
2012-05-26 00:22:59 -03:00
|
|
|
def test_localtime_epoch_utc_daylight_false(self):
|
|
|
|
test.support.patch(self, time, 'daylight', False)
|
2012-11-04 21:06:13 -04:00
|
|
|
t0 = datetime.datetime(1990, 1, 1, tzinfo = datetime.timezone.utc)
|
2012-05-26 00:22:59 -03:00
|
|
|
t1 = utils.localtime(t0)
|
2012-08-22 22:34:00 -03:00
|
|
|
t2 = t0 - datetime.timedelta(hours=5)
|
|
|
|
t2 = t2.replace(tzinfo = datetime.timezone(datetime.timedelta(hours=-5)))
|
|
|
|
self.assertEqual(t1, t2)
|
2012-05-26 00:22:59 -03:00
|
|
|
|
|
|
|
def test_localtime_epoch_notz_daylight_true(self):
|
|
|
|
test.support.patch(self, time, 'daylight', True)
|
2012-11-04 21:06:13 -04:00
|
|
|
t0 = datetime.datetime(1990, 1, 1)
|
2012-05-26 00:22:59 -03:00
|
|
|
t1 = utils.localtime(t0)
|
|
|
|
t2 = utils.localtime(t0.replace(tzinfo=None))
|
|
|
|
self.assertEqual(t1, t2)
|
|
|
|
|
|
|
|
def test_localtime_epoch_notz_daylight_false(self):
|
|
|
|
test.support.patch(self, time, 'daylight', False)
|
2012-11-04 21:06:13 -04:00
|
|
|
t0 = datetime.datetime(1990, 1, 1)
|
2012-05-26 00:22:59 -03:00
|
|
|
t1 = utils.localtime(t0)
|
|
|
|
t2 = utils.localtime(t0.replace(tzinfo=None))
|
|
|
|
self.assertEqual(t1, t2)
|
|
|
|
|
2012-08-22 23:06:37 -03:00
|
|
|
# XXX: Need a more robust test for Olson's tzdata
|
|
|
|
@unittest.skipIf(sys.platform.startswith('win'),
|
|
|
|
"Windows does not use Olson's TZ database")
|
2013-05-08 05:16:02 -03:00
|
|
|
@unittest.skipUnless(os.path.exists('/usr/share/zoneinfo') or
|
|
|
|
os.path.exists('/usr/lib/zoneinfo'),
|
|
|
|
"Can't find the Olson's TZ database")
|
2012-08-22 23:06:37 -03:00
|
|
|
@test.support.run_with_tz('Europe/Kiev')
|
|
|
|
def test_variable_tzname(self):
|
|
|
|
t0 = datetime.datetime(1984, 1, 1, tzinfo=datetime.timezone.utc)
|
|
|
|
t1 = utils.localtime(t0)
|
|
|
|
self.assertEqual(t1.tzname(), 'MSK')
|
|
|
|
t0 = datetime.datetime(1994, 1, 1, tzinfo=datetime.timezone.utc)
|
|
|
|
t1 = utils.localtime(t0)
|
|
|
|
self.assertEqual(t1.tzname(), 'EET')
|
2012-05-26 00:22:59 -03:00
|
|
|
|
2023-03-19 21:20:20 -03:00
|
|
|
def test_isdst_deprecation(self):
|
|
|
|
with self.assertWarns(DeprecationWarning):
|
|
|
|
t0 = datetime.datetime(1990, 1, 1)
|
|
|
|
t1 = utils.localtime(t0, isdst=True)
|
|
|
|
|
2015-09-18 11:32:23 -03:00
|
|
|
# Issue #24836: The timezone files are out of date (pre 2011k)
|
|
|
|
# on Mac OS X Snow Leopard.
|
|
|
|
@test.support.requires_mac_ver(10, 7)
|
2015-07-31 17:18:22 -03:00
|
|
|
class FormatDateTests(unittest.TestCase):
|
|
|
|
|
|
|
|
@test.support.run_with_tz('Europe/Minsk')
|
|
|
|
def test_formatdate(self):
|
|
|
|
timeval = time.mktime((2011, 12, 1, 18, 0, 0, 4, 335, 0))
|
|
|
|
string = utils.formatdate(timeval, localtime=False, usegmt=False)
|
|
|
|
self.assertEqual(string, 'Thu, 01 Dec 2011 15:00:00 -0000')
|
|
|
|
string = utils.formatdate(timeval, localtime=False, usegmt=True)
|
|
|
|
self.assertEqual(string, 'Thu, 01 Dec 2011 15:00:00 GMT')
|
|
|
|
|
|
|
|
@test.support.run_with_tz('Europe/Minsk')
|
|
|
|
def test_formatdate_with_localtime(self):
|
|
|
|
timeval = time.mktime((2011, 1, 1, 18, 0, 0, 6, 1, 0))
|
|
|
|
string = utils.formatdate(timeval, localtime=True)
|
|
|
|
self.assertEqual(string, 'Sat, 01 Jan 2011 18:00:00 +0200')
|
|
|
|
# Minsk moved from +0200 (with DST) to +0300 (without DST) in 2011
|
|
|
|
timeval = time.mktime((2011, 12, 1, 18, 0, 0, 4, 335, 0))
|
|
|
|
string = utils.formatdate(timeval, localtime=True)
|
|
|
|
self.assertEqual(string, 'Thu, 01 Dec 2011 18:00:00 +0300')
|
|
|
|
|
2012-05-26 00:22:59 -03:00
|
|
|
if __name__ == '__main__':
|
|
|
|
unittest.main()
|