
https://hg.python.org/cpython/rev/479787100b91 changeset: 97170:479787100b91 parent: 97167:c13839a3d4fc parent: 97169:94b43b36e464 user: Robert Collins <rbtcollins@hp.com> date: Sat Aug 01 08:20:04 2015 +1200 summary: Issue #22932: Fix timezones in email.utils.formatdate. Patch from Dmitry Shachnev. files: Lib/email/utils.py | 30 ++++-------------- Lib/test/test_email/test_utils.py | 20 ++++++++++++ Misc/ACKS | 1 + Misc/NEWS | 3 + 4 files changed, 31 insertions(+), 23 deletions(-) diff --git a/Lib/email/utils.py b/Lib/email/utils.py --- a/Lib/email/utils.py +++ b/Lib/email/utils.py @@ -155,30 +155,14 @@ # 2822 requires that day and month names be the English abbreviations. if timeval is None: timeval = time.time() + if localtime or usegmt: + dt = datetime.datetime.fromtimestamp(timeval, datetime.timezone.utc) + else: + dt = datetime.datetime.utcfromtimestamp(timeval) if localtime: - now = time.localtime(timeval) - # Calculate timezone offset, based on whether the local zone has - # daylight savings time, and whether DST is in effect. - if time.daylight and now[-1]: - offset = time.altzone - else: - offset = time.timezone - hours, minutes = divmod(abs(offset), 3600) - # Remember offset is in seconds west of UTC, but the timezone is in - # minutes east of UTC, so the signs differ. - if offset > 0: - sign = '-' - else: - sign = '+' - zone = '%s%02d%02d' % (sign, hours, minutes // 60) - else: - now = time.gmtime(timeval) - # Timezone offset is always -0000 - if usegmt: - zone = 'GMT' - else: - zone = '-0000' - return _format_timetuple_and_zone(now, zone) + dt = dt.astimezone() + usegmt = False + return format_datetime(dt, usegmt) def format_datetime(dt, usegmt=False): """Turn a datetime into a date string as specified in RFC 2822. diff --git a/Lib/test/test_email/test_utils.py b/Lib/test/test_email/test_utils.py --- a/Lib/test/test_email/test_utils.py +++ b/Lib/test/test_email/test_utils.py @@ -136,5 +136,25 @@ t1 = utils.localtime(t0) self.assertEqual(t1.tzname(), 'EET') +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') + if __name__ == '__main__': unittest.main() diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1282,6 +1282,7 @@ Pete Sevander Denis Severson Ian Seyer +Dmitry Shachnev Daniel Shahaf Ha Shao Mark Shannon diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -13,6 +13,9 @@ Library ------- +- Issue #22932: Fix timezones in email.utils.formatdate. + Patch from Dmitry Shachnev. + - Issue #23779: imaplib raises TypeError if authenticator tries to abort. Patch from Craig Holmquist. -- Repository URL: https://hg.python.org/cpython