Browse Source

[Py3] Fixes problem with ETA and local timezones. Closes #2306

Ask Solem 10 years ago
parent
commit
7015c3b1ad
1 changed files with 37 additions and 12 deletions
  1. 37 12
      celery/utils/timeutils.py

+ 37 - 12
celery/utils/timeutils.py

@@ -10,6 +10,7 @@ from __future__ import absolute_import, print_function
 
 import numbers
 import os
+import sys
 import time as _time
 
 from calendar import monthrange
@@ -17,7 +18,7 @@ from datetime import date, datetime, timedelta, tzinfo
 
 from kombu.utils import cached_property, reprcall
 
-from pytz import timezone as _timezone, AmbiguousTimeError
+from pytz import timezone as _timezone, AmbiguousTimeError, FixedOffset
 
 from celery.five import string_t
 
@@ -31,6 +32,9 @@ __all__ = ['LocalTimezone', 'timezone', 'maybe_timedelta',
            'localize', 'to_utc', 'maybe_make_aware', 'ffwd', 'utcoffset',
            'adjust_timestamp', 'maybe_s_to_ms']
 
+PY3 = sys.version_info[0] == 3
+PY33 = sys.version_info >= (3, 3)
+
 C_REMDEBUG = os.environ.get('C_REMDEBUG', False)
 
 DAYNAMES = 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'
@@ -55,6 +59,7 @@ class LocalTimezone(tzinfo):
 
     Used only when UTC is not enabled.
     """
+    _offset_cache = {}
 
     def __init__(self):
         # This code is moved in __init__ to execute it as late as possible
@@ -68,23 +73,34 @@ class LocalTimezone(tzinfo):
         tzinfo.__init__(self)
 
     def __repr__(self):
-        return '<LocalTimezone>'
+        return '<LocalTimezone: UTC{0:+03d}>'.format(
+            int(self.DSTOFFSET.total_seconds() / 3600),
+        )
 
     def utcoffset(self, dt):
-        if self._isdst(dt):
-            return self.DSTOFFSET
-        else:
-            return self.STDOFFSET
+        return self.DSTOFFSET if self._isdst(dt) else self.STDOFFSET
 
     def dst(self, dt):
-        if self._isdst(dt):
-            return self.DSTDIFF
-        else:
-            return ZERO
+        return self.DSTDIFF if self._isdst(dt) else ZERO
 
     def tzname(self, dt):
         return _time.tzname[self._isdst(dt)]
 
+    if PY3:
+
+        def fromutc(self, dt):
+            # The base tzinfo class no longer implements a DST
+            # offset aware .fromutc() in Python3 (Issue #2306).
+
+            # I'd rather rely on pytz to do this, than port
+            # the C code from cpython's fromutc [asksol]
+            offset = int(self.utcoffset(dt).seconds / 60.0)
+            try:
+                tz = self._offset_cache[offset]
+            except KeyError:
+                tz = self._offset_cache[offset] = FixedOffset(offset)
+            return tz.fromutc(dt.replace(tzinfo=tz))
+
     def _isdst(self, dt):
         tt = (dt.year, dt.month, dt.day,
               dt.hour, dt.minute, dt.second,
@@ -106,8 +122,17 @@ class _Zone(object):
             dt = make_aware(dt, orig or self.utc)
         return localize(dt, self.tz_or_local(local))
 
-    def to_system(self, dt):
-        return localize(dt, self.local)
+    if PY33:
+
+        def to_system(self, dt):
+            # tz=None is a special case since Python 3.3, and will
+            # convert to the current local timezone (Issue #2306).
+            return dt.astimezone(tz=None)
+
+    else:
+
+        def to_system(self, dt):  # noqa
+            return localize(dt, self.local)
 
     def to_local_fallback(self, dt):
         if is_naive(dt):