[Python-checkins] r82641 - sandbox/branches/py3k-datetime/datetime.py

alexander.belopolsky python-checkins at python.org
Thu Jul 8 01:46:38 CEST 2010


Author: alexander.belopolsky
Date: Thu Jul  8 01:46:38 2010
New Revision: 82641

Log:
Issue #5288: Eliminated round-trips between timdelta and int offsets

Modified:
   sandbox/branches/py3k-datetime/datetime.py

Modified: sandbox/branches/py3k-datetime/datetime.py
==============================================================================
--- sandbox/branches/py3k-datetime/datetime.py	(original)
+++ sandbox/branches/py3k-datetime/datetime.py	Thu Jul  8 01:46:38 2010
@@ -176,7 +176,7 @@
     if year < 1900:
         raise ValueError("year=%d is before 1900; the datetime strftime() "
                          "methods require year >= 1900" % year)
-    # Don't call _utcoffset() or tzname() unless actually needed.
+    # Don't call utcoffset() or tzname() unless actually needed.
     freplace = None # the string to use for %f
     zreplace = None # the string to use for %z
     Zreplace = None # the string to use for %Z
@@ -200,14 +200,16 @@
                 elif ch == 'z':
                     if zreplace is None:
                         zreplace = ""
-                        if hasattr(object, "_utcoffset"):
-                            offset = object._utcoffset()
+                        if hasattr(object, "utcoffset"):
+                            offset = object.utcoffset()
                             if offset is not None:
                                 sign = '+'
-                                if offset < 0:
+                                if offset.days < 0:
                                     offset = -offset
                                     sign = '-'
-                                h, m = divmod(offset, 60)
+                                h, m = divmod(offset, timedelta(hours=1))
+                                assert not m % timedelta(0, 60), "whole minute"
+                                m //= timedelta(0, 60)
                                 zreplace = '%c%02d%02d' % (sign, h, m)
                     assert '%' not in zreplace
                     newformat.append(zreplace)
@@ -250,23 +252,17 @@
 def _check_utc_offset(name, offset):
     assert name in ("utcoffset", "dst")
     if offset is None:
-        return None
+        return
     if not isinstance(offset, timedelta):
         raise TypeError("tzinfo.%s() must return None "
                         "or timedelta, not '%s'" % (name, type(offset)))
-    days = offset.days
-    if days < -1 or days > 0:
-        offset = 1440  # trigger out-of-range
-    else:
-        seconds = days * 86400 + offset.seconds
-        minutes, seconds = divmod(seconds, 60)
-        if seconds or offset.microseconds:
-            raise ValueError("tzinfo.%s() must return a whole number "
-                             "of minutes" % name)
-        offset = minutes
-    if -1440 < offset < 1440:
-        return offset
-    raise ValueError("%s()=%d, must be in -1439..1439" % (name, offset))
+    if offset % timedelta(0, 60) or offset.microseconds:
+        raise ValueError("tzinfo.%s() must return a whole number "
+                         "of minutes, got %s" % (name, offset))
+    if not -timedelta(1) < offset < timedelta(1):
+        raise ValueError("%s()=%s, must be must be strictly between"
+                         " -timedelta(hours=24) and timedelta(hours=24)"
+                         % (name, offset))
 
 def _check_date_fields(year, month, day):
     if not isinstance(year, int):
@@ -1112,8 +1108,8 @@
         if mytz is ottz:
             base_compare = True
         else:
-            myoff = self._utcoffset()
-            otoff = other._utcoffset()
+            myoff = self.utcoffset()
+            otoff = other.utcoffset()
             base_compare = myoff == otoff
 
         if base_compare:
@@ -1123,17 +1119,20 @@
                         other._microsecond))
         if myoff is None or otoff is None:
             raise TypeError("cannot compare naive and aware times")
-        myhhmm = self._hour * 60 + self._minute - myoff
-        othhmm = other._hour * 60 + other._minute - otoff
+        myhhmm = self._hour * 60 + self._minute - myoff//timedelta(0, 60)
+        othhmm = other._hour * 60 + other._minute - otoff//timedelta(0, 60)
         return _cmp((myhhmm, self._second, self._microsecond),
                     (othhmm, other._second, other._microsecond))
 
     def __hash__(self):
         """Hash."""
-        tzoff = self._utcoffset()
+        tzoff = self.utcoffset()
         if not tzoff: # zero or None
             return hash(self._getstate()[0])
-        h, m = divmod(self.hour * 60 + self.minute - tzoff, 60)
+        h, m = divmod(timedelta(hours=self.hour, minutes=self.minute) - tzoff,
+                      timedelta(hours=1))
+        assert not m % timedelta(0, 60), "whole minute"
+        m //= timedelta(0, 60)
         if 0 <= h < 24:
             return hash(time(h, m, self.second, self.microsecond))
         return hash((h, m, self.second, self.microsecond))
@@ -1142,14 +1141,16 @@
 
     def _tzstr(self, sep=":"):
         """Return formatted timezone offset (+xx:xx) or None."""
-        off = self._utcoffset()
+        off = self.utcoffset()
         if off is not None:
-            if off < 0:
+            if off.days < 0:
                 sign = "-"
                 off = -off
             else:
                 sign = "+"
-            hh, mm = divmod(off, 60)
+            hh, mm = divmod(off, timedelta(hours=1))
+            assert not mm % timedelta(0, 60), "whole minute"
+            mm //= timedelta(0, 60)
             assert 0 <= hh < 24
             off = "%s%02d%s%02d" % (sign, hh, sep, mm)
         return off
@@ -1205,16 +1206,10 @@
     def utcoffset(self):
         """Return the timezone offset in minutes east of UTC (negative west of
         UTC)."""
-        offset = _call_tzinfo_method(self._tzinfo, "utcoffset", None)
-        offset = _check_utc_offset("utcoffset", offset)
-        if offset is not None:
-            offset = timedelta(minutes=offset)
-        return offset
-
-    # Return an integer (or None) instead of a timedelta (or None).
-    def _utcoffset(self):
-        offset = _call_tzinfo_method(self._tzinfo, "utcoffset", None)
-        offset = _check_utc_offset("utcoffset", offset)
+        if self._tzinfo is None:
+            return None
+        offset = self._tzinfo.utcoffset(None)
+        _check_utc_offset("utcoffset", offset)
         return offset
 
     def tzname(self):
@@ -1224,7 +1219,9 @@
         it mean anything in particular. For example, "GMT", "UTC", "-500",
         "-5:00", "EDT", "US/Eastern", "America/New York" are all valid replies.
         """
-        name = _call_tzinfo_method(self._tzinfo, "tzname", None)
+        if self._tzinfo is None:
+            return None
+        name = self._tzinfo.tzname(None)
         _check_tzname(name)
         return name
 
@@ -1237,10 +1234,10 @@
         need to consult dst() unless you're interested in displaying the DST
         info.
         """
-        offset = _call_tzinfo_method(self._tzinfo, "dst", None)
-        offset = _check_utc_offset("dst", offset)
-        if offset is not None:
-            offset = timedelta(minutes=offset)
+        if self._tzinfo is None:
+            return None
+        offset = self._tzinfo.dst(None)
+        _check_utc_offset("dst", offset)
         return offset
 
     def replace(self, hour=None, minute=None, second=None, microsecond=None,
@@ -1263,8 +1260,8 @@
     def __bool__(self):
         if self.second or self.microsecond:
             return True
-        offset = self._utcoffset() or 0
-        return self.hour * 60 + self.minute - offset != 0
+        offset = self.utcoffset() or timedelta(0)
+        return timedelta(hours=self.hour, minutes=self.minute) != offset
 
     # Pickle support.
 
@@ -1417,11 +1414,13 @@
 
     def timetuple(self):
         "Return local time tuple compatible with time.localtime()."
-        dst = self._dst()
+        dst = self.dst()
         if dst is None:
             dst = -1
         elif dst:
             dst = 1
+        else:
+            dst = 0
         return _build_struct_time(self.year, self.month, self.day,
                                   self.hour, self.minute, self.second,
                                   dst)
@@ -1521,14 +1520,16 @@
                                   sep) +
                 _format_time(self._hour, self._minute, self._second,
                              self._microsecond))
-        off = self._utcoffset()
+        off = self.utcoffset()
         if off is not None:
-            if off < 0:
+            if off.days < 0:
                 sign = "-"
                 off = -off
             else:
                 sign = "+"
-            hh, mm = divmod(off, 60)
+            hh, mm = divmod(off, timedelta(hours=1))
+            assert not mm % timedelta(0, 60), "whole minute"
+            mm //= timedelta(0, 60)
             s += "%s%02d:%02d" % (sign, hh, mm)
         return s
 
@@ -1560,16 +1561,10 @@
     def utcoffset(self):
         """Return the timezone offset in minutes east of UTC (negative west of
         UTC)."""
-        offset = _call_tzinfo_method(self._tzinfo, "utcoffset", self)
-        offset = _check_utc_offset("utcoffset", offset)
-        if offset is not None:
-            offset = timedelta(minutes=offset)
-        return offset
-
-    # Return an integer (or None) instead of a timedelta (or None).
-    def _utcoffset(self):
-        offset = _call_tzinfo_method(self._tzinfo, "utcoffset", self)
-        offset = _check_utc_offset("utcoffset", offset)
+        if self._tzinfo is None:
+            return None
+        offset = self._tzinfo.utcoffset(self)
+        _check_utc_offset("utcoffset", offset)
         return offset
 
     def tzname(self):
@@ -1592,16 +1587,10 @@
         need to consult dst() unless you're interested in displaying the DST
         info.
         """
-        offset = _call_tzinfo_method(self._tzinfo, "dst", self)
-        offset = _check_utc_offset("dst", offset)
-        if offset is not None:
-            offset = timedelta(minutes=offset)
-        return offset
-
-    # Return an integer (or None) instead of a timedelta (or None).1573
-    def _dst(self):
-        offset = _call_tzinfo_method(self._tzinfo, "dst", self)
-        offset = _check_utc_offset("dst", offset)
+        if self._tzinfo is None:
+            return None
+        offset = self._tzinfo.dst(self)
+        _check_utc_offset("dst", offset)
         return offset
 
     # Comparisons of datetime objects with other.
@@ -1664,9 +1653,9 @@
             base_compare = True
         else:
             if mytz is not None:
-                myoff = self._utcoffset()
+                myoff = self.utcoffset()
             if ottz is not None:
-                otoff = other._utcoffset()
+                otoff = other.utcoffset()
             base_compare = myoff == otoff
 
         if base_compare:
@@ -1721,21 +1710,21 @@
                          self._microsecond - other._microsecond)
         if self._tzinfo is other._tzinfo:
             return base
-        myoff = self._utcoffset()
-        otoff = other._utcoffset()
+        myoff = self.utcoffset()
+        otoff = other.utcoffset()
         if myoff == otoff:
             return base
         if myoff is None or otoff is None:
             raise TypeError("cannot mix naive and timezone-aware time")
-        return base + timedelta(minutes = otoff-myoff)
+        return base + otoff - myoff
 
     def __hash__(self):
-        tzoff = self._utcoffset()
+        tzoff = self.utcoffset()
         if tzoff is None:
             return hash(self._getstate()[0])
         days = _ymd2ord(self.year, self.month, self.day)
-        seconds = self.hour * 3600 + (self.minute - tzoff) * 60 + self.second
-        return hash(timedelta(days, seconds, self.microsecond))
+        seconds = self.hour * 3600 + self.minute * 60 + self.second
+        return hash(timedelta(days, seconds, self.microsecond) - tzoff)
 
     # Pickle support.
 


More information about the Python-checkins mailing list