[Zope3-checkins] CVS: Zope3/lib/python/datetime - _datetime.py:1.14 doc.txt:1.2

Tim Peters tim.one@comcast.net
Sat, 21 Dec 2002 21:02:52 -0500


Update of /cvs-repository/Zope3/lib/python/datetime
In directory cvs.zope.org:/tmp/cvs-serv20490/lib/python/datetime

Modified Files:
	_datetime.py doc.txt 
Log Message:
Relatively massive changes to implement a Wiki suggestion:
{timetz,datetimetz}.{utcoffset,dst}() now return a timetuple (or None)
instead of an int (or None).
tzinfo.{utcoffset,dst)() can now return a timetuple (or an int, or None).
The change is so invasive primarily because the *internals* don't want to
work with timetuples at all, so every internal use of utcoffset() and
dst() had to change.


=== Zope3/lib/python/datetime/_datetime.py 1.13 => 1.14 ===
--- Zope3/lib/python/datetime/_datetime.py:1.13	Sat Dec 21 12:42:02 2002
+++ Zope3/lib/python/datetime/_datetime.py	Sat Dec 21 21:02:20 2002
@@ -164,7 +164,7 @@
 
 # Correctly substitute for %z and %Z escapes in strftime formats.
 def _wrap_strftime(object, format, timetuple):
-    # Don't call utcoffset() or tzname() unless actually needed.
+    # Don't call _utcoffset() or tzname() unless actually needed.
     zreplace = None # the string to use for %z
     Zreplace = None # the string to use for %Z
 
@@ -182,8 +182,8 @@
                 if 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:
@@ -217,20 +217,40 @@
         return None
     return getattr(tzinfo, methname)(self)
 
+# Just raise TypeError if the arg isn't None or a string.
+def _check_tzname(name):
+    if name is not None and not isinstance(name, str):
+        raise TypeError("tzinfo.tzname() must return None or string, "
+                        "not '%s'" % type(name))
+
+# name is the offset-producing method, "utcoffset" or "dst".
+# offset is what it returned.
+# If offset isn't None, int, long, or timedelta, raises TypeError.
+# If offset is None, returns None.
+# Else offset is checked for being in range, and a whole # of minutes.
+# If it is, its integer value is returned.  Else ValueError is raised.
 def _check_utc_offset(name, offset):
+    assert name in ("utcoffset", "dst")
     if offset is None:
-        return
+        return None
     if not isinstance(offset, (int, long, timedelta)):
         raise TypeError("tzinfo.%s() must return None, integer "
                         "or timedelta, not '%s'" % (name, type(offset)))
+    if isinstance(offset, timedelta):
+        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
+        return offset
     raise ValueError("%s()=%d, must be in -1439..1439" % (name, offset))
 
-def _check_tzname(name):
-    if name is not None and not isinstance(name, str):
-        raise TypeError("tzinfo.tzname() must return None or string, "
-                        "not '%s'" % type(name))
 
 # This is a start at a struct tm workalike.  Goals:
 #
@@ -1007,8 +1027,8 @@
             ottz = other.__tzinfo
         if mytz is ottz:
             return supercmp(other)
-        myoff = self.utcoffset()
-        otoff = other.utcoffset()
+        myoff = self._utcoffset()
+        otoff = other._utcoffset()
         if myoff == otoff:
             return supercmp(other)
         if myoff is None or otoff is None:
@@ -1020,7 +1040,7 @@
 
     def __hash__(self):
         """Hash."""
-        tzoff = self.utcoffset()
+        tzoff = self._utcoffset()
         if not tzoff: # zero or None!
             return super(timetz, self).__hash__()
         h, m = divmod(self.hour * 60 + self.minute - tzoff, 60)
@@ -1034,7 +1054,7 @@
 
     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:
                 sign = "-"
@@ -1042,8 +1062,7 @@
             else:
                 sign = "+"
             hh, mm = divmod(off, 60)
-            if hh >= 24:
-                raise ValueError("utcoffset must be in -1439 .. 1439")
+            assert 0 <= hh < 24
             off = "%s%02d%s%02d" % (sign, hh, sep, mm)
         return off
 
@@ -1074,7 +1093,15 @@
         """Return the timezone offset in minutes east of UTC (negative west of
         UTC)."""
         offset = _call_tzinfo_method(self, self.__tzinfo, "utcoffset")
-        _check_utc_offset("utcoffset", offset)
+        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, self.__tzinfo, "utcoffset")
+        offset = _check_utc_offset("utcoffset", offset)
         return offset
 
     def tzname(self):
@@ -1098,13 +1125,21 @@
         info.
         """
         offset = _call_tzinfo_method(self, self.__tzinfo, "dst")
-        _check_utc_offset("dst", offset)
+        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).
+    def _dst(self):
+        offset = _call_tzinfo_method(self, self.__tzinfo, "dst")
+        offset = _check_utc_offset("dst", offset)
         return offset
 
     def __nonzero__(self):
         if self.second or self.microsecond:
             return 1
-        offset = self.utcoffset() or 0
+        offset = self._utcoffset() or 0
         return self.hour * 60 + self.minute - offset != 0
 
     # Pickle support.
@@ -1429,7 +1464,7 @@
 
     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:
@@ -1442,7 +1477,7 @@
         "Return UTC time tuple compatible with time.gmtime()."
         y, m, d = self.year, self.month, self.day
         hh, mm, ss = self.hour, self.minute, self.second
-        offset = self.utcoffset()
+        offset = self._utcoffset()
         if offset:  # neither None nor 0
             tm = tmxxx(y, m, d, hh, mm - offset)
             y, m, d = tm.year, tm.month, tm.day
@@ -1456,7 +1491,7 @@
 
     def isoformat(self, sep='T'):
         s = super(datetimetz, self).isoformat(sep)
-        off = self.utcoffset()
+        off = self._utcoffset()
         if off is not None:
             if off < 0:
                 sign = "-"
@@ -1478,7 +1513,15 @@
         """Return the timezone offset in minutes east of UTC (negative west of
         UTC)."""
         offset = _call_tzinfo_method(self, self.__tzinfo, "utcoffset")
-        _check_utc_offset("utcoffset", offset)
+        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, self.__tzinfo, "utcoffset")
+        offset = _check_utc_offset("utcoffset", offset)
         return offset
 
     def tzname(self):
@@ -1502,7 +1545,15 @@
         info.
         """
         offset = _call_tzinfo_method(self, self.__tzinfo, "dst")
-        _check_utc_offset("dst", offset)
+        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, self.__tzinfo, "dst")
+        offset = _check_utc_offset("dst", offset)
         return offset
 
     def __add__(self, other):
@@ -1523,8 +1574,8 @@
             ottz = other.__tzinfo
         if mytz is ottz:
             return supersub(other)
-        myoff = self.utcoffset()
-        otoff = other.utcoffset()
+        myoff = self._utcoffset()
+        otoff = other._utcoffset()
         if myoff == otoff:
             return supersub(other)
         if myoff is None or otoff is None:
@@ -1537,10 +1588,10 @@
                             type(other).__name__)
         superself = super(datetimetz, self)
         supercmp = superself.__cmp__
-        myoff = self.utcoffset()
+        myoff = self._utcoffset()
         otoff = None
         if isinstance(other, datetimetz):
-            otoff = other.utcoffset()
+            otoff = other._utcoffset()
         if myoff == otoff:
             return supercmp(other)
         if myoff is None or otoff is None:
@@ -1554,7 +1605,7 @@
         return 1
 
     def __hash__(self):
-        tzoff = self.utcoffset()
+        tzoff = self._utcoffset()
         if tzoff is None:
             return super(datetimetz, self).__hash__()
         days = _ymd2ord(self.year, self.month, self.day)


=== Zope3/lib/python/datetime/doc.txt 1.1 => 1.2 ===
--- Zope3/lib/python/datetime/doc.txt:1.1	Sat Dec 21 00:10:43 2002
+++ Zope3/lib/python/datetime/doc.txt	Sat Dec 21 21:02:21 2002
@@ -759,9 +759,10 @@
     is intended to be the total offset from UTC; for example, if a
     tzinfo object represents both time zone and DST adjustments,
     utcoffset() should return their sum.  If the UTC offset isn't known,
-    return None.  Else the value returned must be an int (or long), in
-    the range -1439 to 1439 inclusive (1440 = 24*60; the magnitude of
-    the offset must be less than one day).
+    return None.  Else the value returned must be an int, long, or
+    timedelta object, in the range -1439 to 1439 inclusive (1440 = 24*60;
+    the magnitude of the offset must be less than one day, and must be
+    a whole number of minutes).
 
   - tzname(dt)
     Return the timezone name corresponding to the datetime represented
@@ -777,13 +778,14 @@
   - dst(dt)
     Return the DST offset, in minutes east of UTC, or None if DST
     information isn't known.  Return 0 if DST is not in effect.
-    If DST is in effect, return an int (or long), in the range
-    -1439 to 1439 inclusive.  Note that DST offset, if applicable,
-    has already been added to the UTC offset returned by utcoffset(),
-    so there's no need to consult dst() unless you're interested in
-    displaying DST info separately.  For example, datetimetz.timetuple()
-    calls its tzinfo object's dst() method to determine how the tm_isdst
-    flag should be set.
+    If DST is in effect, return an int, long, or timedelta object, in
+    the range -1439 to 1439 inclusive.  This must be a whole number of
+    minutes.  Note that DST offset, if applicable, has already been added
+    to the UTC offset returned by utcoffset(), so there's no need to
+    consult dst() unless you're interested in displaying DST info
+    separately.  For example, datetimetz.timetuple() calls its
+    tzinfo object's dst() method to determine how the tm_isdst flag
+    should be set.
 
 Example tzinfo classes:
 
@@ -923,19 +925,19 @@
     format string.  See the section on strftime() behavior.
 
   - utcoffset()
-    If self.tzinfo is None, returns None, else self.tzinfo.utcoffset(self).
+    If self.tzinfo is None, returns None, else self.tzinfo.utcoffset(self),
+    converted a timedelta object.
 
   - tzname():
     If self.tzinfo is None, returns None, else self.tzinfo.tzname(self).
 
   - dst()
-    If self.tzinfo is None, returns None, else self.tzinfo.dst(self).
+    If self.tzinfo is None, returns None, else self.tzinfo.dst(self),
+    converted to a timedelta object.
 
 
 class datetimetz
 ================
-XXX I think this is *still* missing some methods from the
-XXX Python implementation.
 A datetimetz object is a single object containing all the information
 from a date object and a timetz object.
 
@@ -1072,12 +1074,14 @@
 
   - utcoffset()
     If self.tzinfo is None, returns None, else self.tzinfo.utcoffset(self).
+    converted to a timedelta object.
 
   - tzname():
     If self.tzinfo is None, returns None, else self.tzinfo.tzname(self).
 
   - dst()
-    If self.tzinfo is None, returns None, else self.tzinfo.dst(self).
+    If self.tzinfo is None, returns None, else self.tzinfo.dst(self),
+    converted to a timedelta object.
 
   - timetuple()
     Like datetime.timetuple(), but sets the tm_isdst flag according to