summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/email/utils.py30
-rw-r--r--Lib/test/test_email/test_utils.py20
-rw-r--r--Misc/ACKS1
-rw-r--r--Misc/NEWS3
4 files changed, 31 insertions, 23 deletions
diff --git a/Lib/email/utils.py b/Lib/email/utils.py
index 317fdfa..5080d81 100644
--- a/Lib/email/utils.py
+++ b/Lib/email/utils.py
@@ -155,30 +155,14 @@ def formatdate(timeval=None, localtime=False, usegmt=False):
# 2822 requires that day and month names be the English abbreviations.
if timeval is None:
timeval = time.time()
- 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)
+ if localtime or usegmt:
+ dt = datetime.datetime.fromtimestamp(timeval, datetime.timezone.utc)
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 = datetime.datetime.utcfromtimestamp(timeval)
+ if localtime:
+ 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
index 4abdc04..1e9cd63 100644
--- a/Lib/test/test_email/test_utils.py
+++ b/Lib/test/test_email/test_utils.py
@@ -136,5 +136,25 @@ class LocaltimeTests(unittest.TestCase):
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
index de33df1..cc557b3 100644
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -1247,6 +1247,7 @@ Jerry Seutter
Pete Sevander
Denis Severson
Ian Seyer
+Dmitry Shachnev
Daniel Shahaf
Ha Shao
Mark Shannon
diff --git a/Misc/NEWS b/Misc/NEWS
index 6798182..1aa2ea3 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -66,6 +66,9 @@ Core and Builtins
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.