Index: datetime.py =================================================================== --- datetime.py (revision 82454) +++ datetime.py (working copy) @@ -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): @@ -1100,8 +1096,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: @@ -1111,17 +1107,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)) @@ -1130,14 +1129,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 @@ -1193,18 +1194,12 @@ 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) + if self._tzinfo is None: + return None + offset = self._tzinfo.utcoffset(None) + _check_utc_offset("utcoffset", 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) - return offset - def tzname(self): """Return the timezone name. @@ -1212,7 +1207,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 @@ -1225,10 +1222,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, @@ -1251,8 +1248,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. @@ -1406,11 +1403,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) @@ -1510,14 +1509,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 @@ -1549,18 +1550,12 @@ 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) + if self._tzinfo is None: + return None + offset = self._tzinfo.utcoffset(self) + _check_utc_offset("utcoffset", 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) - return offset - def tzname(self): """Return the timezone name. @@ -1581,18 +1576,12 @@ 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) + if self._tzinfo is None: + return None + offset = self._tzinfo.dst(self) + _check_utc_offset("dst", 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) - return offset - # Comparisons of datetime objects with other. def __eq__(self, other): @@ -1653,9 +1642,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: @@ -1710,21 +1699,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.