diff options
author | R David Murray <rdmurray@bitdance.com> | 2012-08-23 01:34:00 (GMT) |
---|---|---|
committer | R David Murray <rdmurray@bitdance.com> | 2012-08-23 01:34:00 (GMT) |
commit | b8687df6532b809f41d77dfc617363d3c093ec88 (patch) | |
tree | 48e09484522d82be90843fe16584126f41a63116 | |
parent | 17183a2972e8981c0d0ca2d599b6e08fc662fc28 (diff) | |
download | cpython-b8687df6532b809f41d77dfc617363d3c093ec88.zip cpython-b8687df6532b809f41d77dfc617363d3c093ec88.tar.gz cpython-b8687df6532b809f41d77dfc617363d3c093ec88.tar.bz2 |
#665194: Update email.utils.localtime to use astimezone, and fix bug.
The new code correctly handles historic changes in UTC offsets.
A test for this should follow.
Original patch by Alexander Belopolsky.
-rw-r--r-- | Lib/email/utils.py | 51 | ||||
-rw-r--r-- | Lib/test/test_email/test_utils.py | 10 | ||||
-rw-r--r-- | Misc/NEWS | 3 |
3 files changed, 33 insertions, 31 deletions
diff --git a/Lib/email/utils.py b/Lib/email/utils.py index 39f7903..c6204da 100644 --- a/Lib/email/utils.py +++ b/Lib/email/utils.py @@ -386,33 +386,26 @@ def localtime(dt=None, isdst=-1): """ if dt is None: - seconds = time.time() - else: - if dt.tzinfo is None: - # A naive datetime is given. Convert to a (localtime) - # timetuple and pass to system mktime together with - # the isdst hint. System mktime will return seconds - # sysce epoch. - tm = dt.timetuple()[:-1] + (isdst,) - seconds = time.mktime(tm) + dt = datetime.datetime.now(datetime.timezone.utc) + if dt.tzinfo is not None: + return dt.astimezone() + # We have a naive datetime. Convert to a (localtime) timetuple and pass to + # system mktime together with the isdst hint. System mktime will return + # seconds since epoch. + tm = dt.timetuple()[:-1] + (isdst,) + seconds = time.mktime(tm) + localtm = time.localtime(seconds) + try: + delta = datetime.timedelta(seconds=localtm.tm_gmtoff) + tz = datetime.timezone(delta, localtm.tm_zone) + except AttributeError: + # Compute UTC offset and compare with the value implied by tm_isdst. + # If the values match, use the zone name implied by tm_isdst. + delta = dt - datetime.datetime(*time.gmtime(ts)[:6]) + dst = time.daylight and localtm.tm_isdst > 0 + gmtoff = -(time.altzone if dst else time.timezone) + if delta == datetime.timedelta(seconds=gmtoff): + tz = datetime.timezone(delta, time.tzname[dst]) else: - # An aware datetime is given. Use aware datetime - # arithmetics to find seconds since epoch. - delta = dt - datetime.datetime(1970, 1, 1, - tzinfo=datetime.timezone.utc) - seconds = delta.total_seconds() - tm = time.localtime(seconds) - - # XXX: The following logic may not work correctly if UTC - # offset has changed since time provided in dt. This will be - # corrected in C implementation for platforms that support - # tm_gmtoff. - if time.daylight and tm.tm_isdst: - offset = time.altzone - tzname = time.tzname[1] - else: - offset = time.timezone - tzname = time.tzname[0] - - tz = datetime.timezone(datetime.timedelta(seconds=-offset), tzname) - return datetime.datetime.fromtimestamp(seconds, tz) + tz = datetime.timezone(delta) + return dt.replace(tzinfo=tz) diff --git a/Lib/test/test_email/test_utils.py b/Lib/test/test_email/test_utils.py index d9c4d70..7d0267e 100644 --- a/Lib/test/test_email/test_utils.py +++ b/Lib/test/test_email/test_utils.py @@ -87,17 +87,23 @@ class LocaltimeTests(unittest.TestCase): t2 = utils.localtime(t1) self.assertEqual(t1, t2) + @test.support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0') def test_localtime_epoch_utc_daylight_true(self): test.support.patch(self, time, 'daylight', True) t0 = datetime.datetime(1970, 1, 1, tzinfo = datetime.timezone.utc) t1 = utils.localtime(t0) - self.assertEqual(t0, t1) + t2 = t0 - datetime.timedelta(hours=5) + t2 = t2.replace(tzinfo = datetime.timezone(datetime.timedelta(hours=-5))) + self.assertEqual(t1, t2) + @test.support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0') def test_localtime_epoch_utc_daylight_false(self): test.support.patch(self, time, 'daylight', False) t0 = datetime.datetime(1970, 1, 1, tzinfo = datetime.timezone.utc) t1 = utils.localtime(t0) - self.assertEqual(t0, t1) + t2 = t0 - datetime.timedelta(hours=5) + t2 = t2.replace(tzinfo = datetime.timezone(datetime.timedelta(hours=-5))) + self.assertEqual(t1, t2) def test_localtime_epoch_notz_daylight_true(self): test.support.patch(self, time, 'daylight', True) @@ -24,6 +24,9 @@ Core and Builtins Library ------- +- Issue ##665194: Update email.utils.localtime to use datetime.astimezone and + correctly handle historic changes in UTC offsets. + - Issue #15199: Fix JavaScript's default MIME type to application/javascript. Patch by Bohuslav Kabrda. |