[Python-checkins] python/nondist/sandbox/datetime US.py,1.6,1.7 datetime.py,1.126,1.127 doc.txt,1.72,1.73 test_datetime.py,1.83,1.84

tim_one@users.sourceforge.net tim_one@users.sourceforge.net
Thu, 26 Dec 2002 16:13:58 -0800


Update of /cvsroot/python/python/nondist/sandbox/datetime
In directory sc8-pr-cvs1:/tmp/cvs-serv5177

Modified Files:
	US.py datetime.py doc.txt test_datetime.py 
Log Message:
Brrrr.  Many changes to make comparison and subtraction of aware objects
ignore tzinfo if the operands have identical tzinfo members (meaning
object identity -- "is").  I misunderstood the intent here, reading
a wrong conclusion into conflicting clues.

The Python implementation's datetime.__cmp__ now handles all comparisons
for datetime and datetimetz, and likewise for time.__cmp__ wrt timetz.
The C implementation does that already.  The problem was that, e.g.,
comparing a naive time to an aware timetz failed to raise TypeError,
as previously time.__cmp__ silently ignored that "other" was actually
of type timetz and just compared the common base fields.  Unfortunately,
no test case caught this, so added one.  Unfortunately again, the new
test has to be skipped when running the Python implementation under 2.2.2,
because it triggers a Python bug (which Guido already fixed in 2.2 CVS
for Python 2.2.3).


Index: US.py
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/US.py,v
retrieving revision 1.6
retrieving revision 1.7
diff -C2 -d -r1.6 -r1.7
*** US.py	26 Dec 2002 04:46:34 -0000	1.6
--- US.py	27 Dec 2002 00:13:55 -0000	1.7
***************
*** 134,142 ****
  to 3:00:00), and is taken to be in DST, as a redundant spelling of 1:00:00
  standard time.  So we actually expect b to be about an hour *before* a.
  
! >>> print -(after - before)
! 0:59:59
  
! Converting to UTC and subtracting, we should get the same thing.
  
  >>> class UTC(tzinfo):
--- 134,144 ----
  to 3:00:00), and is taken to be in DST, as a redundant spelling of 1:00:00
  standard time.  So we actually expect b to be about an hour *before* a.
+ However, subtraction of two objects with the same tzinfo member is "naive",
+ so comes out to 1 second:
  
! >>> print after - before
! 0:00:01
  
! Converting to UTC and subtracting, we get the "about an hour":
  
  >>> class UTC(tzinfo):
***************
*** 171,178 ****
  The naive clock repeats the times in 1:HH:MM, so 1:59:59 was actually
  ambiguous, and resolved arbitrarily as being in DST.  2:00:00 is in standard
! time, and is actually about an hour later.
  
  >>> print after - before
! 1:00:01
  >>> print after.astimezone(utc) - before.astimezone(utc)
  1:00:01
--- 173,182 ----
  The naive clock repeats the times in 1:HH:MM, so 1:59:59 was actually
  ambiguous, and resolved arbitrarily as being in DST.  2:00:00 is in standard
! time, and is actually about an hour later.  Again, because these have the
! same tzinfo member, the utcoffsets are ignored by straight subtraction,
! but revealed by converting to UTC first:
  
  >>> print after - before
! 0:00:01
  >>> print after.astimezone(utc) - before.astimezone(utc)
  1:00:01

Index: datetime.py
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/datetime.py,v
retrieving revision 1.126
retrieving revision 1.127
diff -C2 -d -r1.126 -r1.127
*** datetime.py	26 Dec 2002 04:46:34 -0000	1.126
--- datetime.py	27 Dec 2002 00:13:55 -0000	1.127
***************
*** 893,903 ****
      def __cmp__(self, other):
          """Three-way comparison."""
!         if isinstance(other, time):
              return cmp((self.__hour, self.__minute, self.__second,
                          self.__microsecond),
                         (other.__hour, other.__minute, other.__second,
                          other.__microsecond))
!         raise TypeError, ("can't compare time to %s instance" %
!                           type(other).__name__)
  
      def __hash__(self):
--- 893,928 ----
      def __cmp__(self, other):
          """Three-way comparison."""
!         if not isinstance(other, time):
!             # XXX Buggy in 2.2.2.
!             raise TypeError("can't compare '%s' to '%s'" % (
!                             type(self).__name__, type(other).__name__))
!         mytz = myoff = None
!         if isinstance(self, timetz):
!             mytz = self._tzinfo
!         ottz = otoff = None
!         if isinstance(other, timetz):
!             ottz = other._tzinfo
! 
!         if mytz is ottz:
!             base_compare = True
!         else:
!             if isinstance(self, timetz):
!                 myoff = self._utcoffset()
!             if isinstance(other, timetz):
!                 otoff = other._utcoffset()
!             base_compare = myoff == otoff
! 
!         if base_compare:
              return cmp((self.__hour, self.__minute, self.__second,
                          self.__microsecond),
                         (other.__hour, other.__minute, other.__second,
                          other.__microsecond))
!         if myoff is None or otoff is None:
!             # XXX Buggy in 2.2.2.
!             raise TypeError("cannot compare naive and aware times")
!         myhhmm = self.__hour * 60 + self.__minute - myoff
!         othhmm = other.__hour * 60 + other.__minute - otoff
!         return cmp((myhhmm, self.__second, self.__microsecond),
!                    (othhmm, other.__second, other.__microsecond))
  
      def __hash__(self):
***************
*** 1043,1075 ****
          _check_tzinfo_arg(tzinfo)
          super(timetz, self).__init__(hour, minute, second, microsecond)
!         self.__tzinfo = tzinfo
  
      # Read-only field accessors
!     tzinfo = property(lambda self: self.__tzinfo, doc="timezone info object")
! 
!     # Standard conversions, __cmp__, __hash__ (and helpers)
  
!     def __cmp__(self, other):
!         """Three-way comparison."""
!         if not isinstance(other, time):
!             raise TypeError("can't compare timetz to %s instance" %
!                             type(other).__name__)
!         superself = super(timetz, self)
!         supercmp = superself.__cmp__
!         myoff = self._utcoffset()
!         otoff = other._utcoffset()
!         if myoff == otoff:
!             return supercmp(other)
!         if myoff is None or otoff is None:
!             raise TypeError, "cannot mix naive and timezone-aware time"
!         myhhmm = self.hour * 60 + self.minute - myoff
!         othhmm = other.hour * 60 + other.minute - otoff
!         return cmp((myhhmm, self.second, self.microsecond),
!                    (othhmm, other.second, other.microsecond))
  
      def __hash__(self):
          """Hash."""
          tzoff = self._utcoffset()
!         if not tzoff: # zero or None!
              return super(timetz, self).__hash__()
          h, m = divmod(self.hour * 60 + self.minute - tzoff, 60)
--- 1068,1082 ----
          _check_tzinfo_arg(tzinfo)
          super(timetz, self).__init__(hour, minute, second, microsecond)
!         self._tzinfo = tzinfo
  
      # Read-only field accessors
!     tzinfo = property(lambda self: self._tzinfo, doc="timezone info object")
  
!     # Standard conversions, __hash__ (and helpers)
  
      def __hash__(self):
          """Hash."""
          tzoff = self._utcoffset()
!         if not tzoff: # zero or None
              return super(timetz, self).__hash__()
          h, m = divmod(self.hour * 60 + self.minute - tzoff, 60)
***************
*** 1099,1105 ****
          """Convert to formal string, for repr()."""
          s = super(timetz, self).__repr__()
!         if self.__tzinfo is not None:
              assert s[-1:] == ")"
!             s = s[:-1] + ", tzinfo=%r" % self.__tzinfo + ")"
          return s
  
--- 1106,1112 ----
          """Convert to formal string, for repr()."""
          s = super(timetz, self).__repr__()
!         if self._tzinfo is not None:
              assert s[-1:] == ")"
!             s = s[:-1] + ", tzinfo=%r" % self._tzinfo + ")"
          return s
  
***************
*** 1122,1126 ****
          """Return the timezone offset in minutes east of UTC (negative west of
          UTC)."""
!         offset = _call_tzinfo_method(self, self.__tzinfo, "utcoffset")
          offset = _check_utc_offset("utcoffset", offset)
          if offset is not None:
--- 1129,1133 ----
          """Return the timezone offset in minutes east of UTC (negative west of
          UTC)."""
!         offset = _call_tzinfo_method(self, self._tzinfo, "utcoffset")
          offset = _check_utc_offset("utcoffset", offset)
          if offset is not None:
***************
*** 1130,1134 ****
      # 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
--- 1137,1141 ----
      # 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
***************
*** 1141,1145 ****
          "-5:00", "EDT", "US/Eastern", "America/New York" are all valid replies.
          """
!         name = _call_tzinfo_method(self, self.__tzinfo, "tzname")
          _check_tzname(name)
          return name
--- 1148,1152 ----
          "-5:00", "EDT", "US/Eastern", "America/New York" are all valid replies.
          """
!         name = _call_tzinfo_method(self, self._tzinfo, "tzname")
          _check_tzname(name)
          return name
***************
*** 1154,1158 ****
          info.
          """
!         offset = _call_tzinfo_method(self, self.__tzinfo, "dst")
          offset = _check_utc_offset("dst", offset)
          if offset is not None:
--- 1161,1165 ----
          info.
          """
!         offset = _call_tzinfo_method(self, self._tzinfo, "dst")
          offset = _check_utc_offset("dst", offset)
          if offset is not None:
***************
*** 1179,1183 ****
      # 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
--- 1186,1190 ----
      # 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
***************
*** 1193,1200 ****
      def __getstate__(self):
          basestate = time.__getstate__(self)
!         if self.__tzinfo is None:
              return (basestate,)
          else:
!             return (basestate, self.__tzinfo)
  
      def __setstate__(self, state):
--- 1200,1207 ----
      def __getstate__(self):
          basestate = time.__getstate__(self)
!         if self._tzinfo is None:
              return (basestate,)
          else:
!             return (basestate, self._tzinfo)
  
      def __setstate__(self, state):
***************
*** 1206,1212 ****
          time.__setstate__(self, state[0])
          if len(state) == 1:
!             self.__tzinfo = None
          else:
!             self.__tzinfo = state[1]
  
  timetz.min = timetz(0, 0, 0)
--- 1213,1219 ----
          time.__setstate__(self, state[0])
          if len(state) == 1:
!             self._tzinfo = None
          else:
!             self._tzinfo = state[1]
  
  timetz.min = timetz(0, 0, 0)
***************
*** 1374,1379 ****
  
      def __cmp__(self, other):
!         "Three-way comparison."
!         if isinstance(other, datetime):
              return cmp((self.__year, self.__month, self.__day,
                          self.__hour, self.__minute, self.__second,
--- 1381,1406 ----
  
      def __cmp__(self, other):
!         import datetime
!         if not isinstance(other, datetime.datetime):
!             # XXX Buggy in 2.2.2.
!             raise TypeError("can't compare '%s' to '%s'" % (
!                             type(self).__name__, type(other).__name__))
!         mytz = myoff = None
!         if isinstance(self, datetimetz):
!             mytz = self._tzinfo
!         ottz = otoff = None
!         if isinstance(other, datetimetz):
!             ottz = other._tzinfo
! 
!         if mytz is ottz:
!             base_compare = True
!         else:
!             if isinstance(self, datetimetz):
!                 myoff = self._utcoffset()
!             if isinstance(other, datetimetz):
!                 otoff = other._utcoffset()
!             base_compare = myoff == otoff
! 
!         if base_compare:
              return cmp((self.__year, self.__month, self.__day,
                          self.__hour, self.__minute, self.__second,
***************
*** 1382,1387 ****
                          other.__hour, other.__minute, other.__second,
                          other.__microsecond))
!         raise TypeError, ("can't compare datetime to %s instance" %
!                           type(other).__name__)
  
      def __hash__(self):
--- 1409,1423 ----
                          other.__hour, other.__minute, other.__second,
                          other.__microsecond))
!         if myoff is None or otoff is None:
!             # XXX Buggy in 2.2.2.
!             raise TypeError("cannot compare naive and aware datetimes")
!         # XXX What follows could be done more efficiently...
!         diff = (datetime.datetime.__sub__(self, other) +
!                 timedelta(minutes=otoff-myoff))
!         if diff.days < 0:
!             return -1
!         if diff == timedelta():
!             return 0
!         return 1
  
      def __hash__(self):
***************
*** 1498,1504 ****
          super(datetimetz, self).__init__(year, month, day,
                                           hour, minute, second, microsecond)
!         self.__tzinfo = tzinfo
  
!     tzinfo = property(lambda self: self.__tzinfo, doc="timezone info object")
  
      def fromtimestamp(cls, t, tzinfo=None):
--- 1534,1540 ----
          super(datetimetz, self).__init__(year, month, day,
                                           hour, minute, second, microsecond)
!         self._tzinfo = tzinfo
  
!     tzinfo = property(lambda self: self._tzinfo, doc="timezone info object")
  
      def fromtimestamp(cls, t, tzinfo=None):
***************
*** 1550,1554 ****
          "Return the time part."
          return timetz(self.hour, self.minute, self.second, self.microsecond,
!                       self.__tzinfo)
  
      def replace(self, year=None, month=None, day=None, hour=None,
--- 1586,1590 ----
          "Return the time part."
          return timetz(self.hour, self.minute, self.second, self.microsecond,
!                       self._tzinfo)
  
      def replace(self, year=None, month=None, day=None, hour=None,
***************
*** 1604,1610 ****
      def __repr__(self):
          s = super(datetimetz, self).__repr__()
!         if self.__tzinfo is not None:
              assert s[-1:] == ")"
!             s = s[:-1] + ", tzinfo=%r" % self.__tzinfo + ")"
          return s
  
--- 1640,1646 ----
      def __repr__(self):
          s = super(datetimetz, self).__repr__()
!         if self._tzinfo is not None:
              assert s[-1:] == ")"
!             s = s[:-1] + ", tzinfo=%r" % self._tzinfo + ")"
          return s
  
***************
*** 1612,1616 ****
          """Return the timezone offset in minutes east of UTC (negative west of
          UTC)."""
!         offset = _call_tzinfo_method(self, self.__tzinfo, "utcoffset")
          offset = _check_utc_offset("utcoffset", offset)
          if offset is not None:
--- 1648,1652 ----
          """Return the timezone offset in minutes east of UTC (negative west of
          UTC)."""
!         offset = _call_tzinfo_method(self, self._tzinfo, "utcoffset")
          offset = _check_utc_offset("utcoffset", offset)
          if offset is not None:
***************
*** 1620,1624 ****
      # 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
--- 1656,1660 ----
      # 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
***************
*** 1631,1635 ****
          "-5:00", "EDT", "US/Eastern", "America/New York" are all valid replies.
          """
!         name = _call_tzinfo_method(self, self.__tzinfo, "tzname")
          _check_tzname(name)
          return name
--- 1667,1671 ----
          "-5:00", "EDT", "US/Eastern", "America/New York" are all valid replies.
          """
!         name = _call_tzinfo_method(self, self._tzinfo, "tzname")
          _check_tzname(name)
          return name
***************
*** 1644,1648 ****
          info.
          """
!         offset = _call_tzinfo_method(self, self.__tzinfo, "dst")
          offset = _check_utc_offset("dst", offset)
          if offset is not None:
--- 1680,1684 ----
          info.
          """
!         offset = _call_tzinfo_method(self, self._tzinfo, "dst")
          offset = _check_utc_offset("dst", offset)
          if offset is not None:
***************
*** 1652,1656 ****
      # 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
--- 1688,1692 ----
      # 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
***************
*** 1659,1663 ****
          result = super(datetimetz, self).__add__(other)
          assert isinstance(result, datetimetz)
!         result.__tzinfo = self.__tzinfo
          return result
  
--- 1695,1699 ----
          result = super(datetimetz, self).__add__(other)
          assert isinstance(result, datetimetz)
!         result._tzinfo = self._tzinfo
          return result
  
***************
*** 1672,1689 ****
              # handled by datetimetz.__add__.
              return supersub(other)
!         myoff = self._utcoffset()
!         otoff = other._utcoffset()
!         if myoff == otoff:
              return supersub(other)
-         if myoff is None or otoff is None:
-             raise TypeError, "cannot mix naive and timezone-aware time"
-         return supersub(other) + timedelta(minutes=otoff-myoff)
  
-     def __cmp__(self, other):
-         if not isinstance(other, datetime):
-             raise TypeError("can't compare datetime to %s instance" %
-                             type(other).__name__)
-         superself = super(datetimetz, self)
-         supercmp = superself.__cmp__
          myoff = self._utcoffset()
          otoff = None
--- 1708,1718 ----
              # handled by datetimetz.__add__.
              return supersub(other)
!         mytz = self._tzinfo
!         ottz = None
!         if isinstance(other, datetimetz):
!             ottz = other._tzinfo
!         if mytz is ottz:
              return supersub(other)
  
          myoff = self._utcoffset()
          otoff = None
***************
*** 1691,1704 ****
              otoff = other._utcoffset()
          if myoff == otoff:
!             return supercmp(other)
          if myoff is None or otoff is None:
              raise TypeError, "cannot mix naive and timezone-aware time"
!         # XXX What follows could be done more efficiently...
!         diff = superself.__sub__(other) + timedelta(minutes=otoff-myoff)
!         if diff.days < 0:
!             return -1
!         if diff == timedelta():
!             return 0
!         return 1
  
      def __hash__(self):
--- 1720,1727 ----
              otoff = other._utcoffset()
          if myoff == otoff:
!             return supersub(other)
          if myoff is None or otoff is None:
              raise TypeError, "cannot mix naive and timezone-aware time"
!         return supersub(other) + timedelta(minutes=otoff-myoff)
  
      def __hash__(self):
***************
*** 1712,1719 ****
      def __getstate__(self):
          basestate = datetime.__getstate__(self)
!         if self.__tzinfo is None:
              return (basestate,)
          else:
!             return (basestate, self.__tzinfo)
  
      def __setstate__(self, state):
--- 1735,1742 ----
      def __getstate__(self):
          basestate = datetime.__getstate__(self)
!         if self._tzinfo is None:
              return (basestate,)
          else:
!             return (basestate, self._tzinfo)
  
      def __setstate__(self, state):
***************
*** 1725,1731 ****
          datetime.__setstate__(self, state[0])
          if len(state) == 1:
!             self.__tzinfo = None
          else:
!             self.__tzinfo = state[1]
  
  
--- 1748,1754 ----
          datetime.__setstate__(self, state[0])
          if len(state) == 1:
!             self._tzinfo = None
          else:
!             self._tzinfo = state[1]
  
  

Index: doc.txt
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/doc.txt,v
retrieving revision 1.72
retrieving revision 1.73
diff -C2 -d -r1.72 -r1.73
*** doc.txt	25 Dec 2002 06:49:28 -0000	1.72
--- doc.txt	27 Dec 2002 00:13:55 -0000	1.73
***************
*** 920,927 ****
  Supported operations:
  
!     - comparison of timetz to timetz, where timetz1 is considered
!       less than timetz2 when timetz1 precedes timetz2 in time, and
!       where the timetz objects are first adjusted by subtracting
!       their UTC offsets (obtained from self.utcoffset()).
  
      - hash, use as dict key
--- 920,931 ----
  Supported operations:
  
!     - comparison of timetz to time or timetz, where a is considered
!       less than b when a precedes b in time.  If one comparand is
!       naive and the other is aware, TypeError is raised.  If both
!       comparands are aware, and have the same tzinfo member, the
!       common tzinfo member is ignored and the base times are compared.
!       If both comparands are aware and have different tzinfo members,
!       the comparands are first adjusted by subtracting their UTC
!       offsets (obtained from self.utcoffset()).
  
      - hash, use as dict key
***************
*** 1053,1058 ****
  
      - datetimetz1 - timedelta -> datetimetz2
!       The same as addition of datetime objects, except that
!       datetimetz2.tzinfo is set to datetimetz1.tzinfo.
  
      - aware_datetimetz1 - aware_datetimetz2 -> timedelta
--- 1057,1062 ----
  
      - datetimetz1 - timedelta -> datetimetz2
!       The same as subtraction of timedelta from datetime objects, except
!       that datetimetz2.tzinfo is set to datetimetz1.tzinfo.
  
      - aware_datetimetz1 - aware_datetimetz2 -> timedelta
***************
*** 1066,1085 ****
        is raised.
  
!       If both are naive, subtraction acts as for datetime subtraction.
  
!       If both are aware datetimetz objects, a-b acts as if a and b were
!       first converted to UTC datetimes (by subtracting a.utcoffset()
!       minutes from a, and b.utcoffset() minutes from b), and then doing
!       datetime subtraction, except that the implementation never
!       overflows.
  
!     - Comparison of datetimetz to datetime or datetimetz.  As for
!       subtraction, comparison is defined only if both operands are
!       naive or both are aware.  If both are naive, comparison is as
!       for datetime objects with the same date and time components.
!       If both are aware, comparison acts as if both were converted to
!       UTC datetimes first, except the the implementation never
!       overflows.  If one comparand is naive and the other aware,
!       TypeError is raised.
  
      - hash, use as dict key
--- 1070,1090 ----
        is raised.
  
!       If both are naive, or both are aware and have the same tzinfo
!       member, subtraction acts as for datetime subtraction.
  
!       If both are aware and have different tzinfo members, a-b acts as
!       if a and b were first converted to UTC datetimes (by subtracting
!       a.utcoffset() minutes from a, and b.utcoffset() minutes from b),
!       and then doing datetime subtraction, except that the implementation
!       never  overflows.
  
!     - Comparison of datetimetz to datetime or datetimetz, where a is
!       considered less than b when a precedes b in time.  If one
!       comparand is naive and the other is aware, TypeError is raised.
!       If both comparands are aware, and have the same tzinfo member,
!       the common tzinfo member is ignored and the base datetimes
!       are compared.  If both comparands are aware and have different
!       tzinfo members, the comparands are first adjusted by subtracting
!       their UTC offsets (obtained from self.utcoffset()).
  
      - hash, use as dict key

Index: test_datetime.py
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/test_datetime.py,v
retrieving revision 1.83
retrieving revision 1.84
diff -C2 -d -r1.83 -r1.84
*** test_datetime.py	26 Dec 2002 04:46:34 -0000	1.83
--- test_datetime.py	27 Dec 2002 00:13:56 -0000	1.84
***************
*** 1670,1676 ****
          cls = self.theclass
  
!         # Primarily trying to ensure that utcoffset() gets called even if
!         # the comparands have the same tzinfo member.  timetz comparison
!         # didn't used to do so, although datetimetz comparison did.
          class OperandDependentOffset(tzinfo):
              def utcoffset(self, t):
--- 1670,1675 ----
          cls = self.theclass
  
!         # Ensure that utcoffset() gets ignored if the comparands have
!         # the same tzinfo member.
          class OperandDependentOffset(tzinfo):
              def utcoffset(self, t):
***************
*** 1687,1690 ****
--- 1686,1699 ----
              for y in d0, d1, d2:
                  got = cmp(x, y)
+                 expected = cmp(x.minute, y.minute)
+                 self.assertEqual(got, expected)
+ 
+         # However, if they're different members, uctoffset is not ignored.
+         d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
+         d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
+         d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
+         for x in d0, d1, d2:
+             for y in d0, d1, d2:
+                 got = cmp(x, y)
                  if (x is d0 or x is d1) and (y is d0 or y is d1):
                      expected = 0
***************
*** 1906,1909 ****
--- 1915,1949 ----
          self.assertRaises(ValueError, base.replace, microsecond=1000000)
  
+     def test_mixed_compare(self):
+         t1 = time(1, 2, 3)
+         t2 = timetz(1, 2, 3)
+         self.assertEqual(t1, t2)
+         t2 = t2.replace(tzinfo=None)
+         self.assertEqual(t1, t2)
+         t2 = t2.replace(tzinfo=FixedOffset(None, ""))
+         self.assertEqual(t1, t2)
+         if CMP_BUG_FIXED:
+             t2 = t2.replace(tzinfo=FixedOffset(0, ""))
+             self.assertRaises(TypeError, lambda: t1 == t2)
+ 
+         # In timetz w/ identical tzinfo objects, utcoffset is ignored.
+         class Varies(tzinfo):
+             def __init__(self):
+                 self.offset = 22
+             def utcoffset(self, t):
+                 self.offset += 1
+                 return self.offset
+ 
+         v = Varies()
+         t1 = t2.replace(tzinfo=v)
+         t2 = t2.replace(tzinfo=v)
+         self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
+         self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
+         self.assertEqual(t1, t2)
+ 
+         # But if they're not identical, it isn't ignored.
+         t2 = t2.replace(tzinfo=Varies())
+         self.failUnless(t1 < t2)  # t1's offset counter still going up
+ 
  
  class TestDateTimeTZ(TestDateTime, TZInfoBase):
***************
*** 1984,1988 ****
          t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
          t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
!         self.assertRaises(ValueError, lambda: t1 == t1)
  
      def test_pickling(self):
--- 2024,2028 ----
          t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
          t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
!         self.assertRaises(ValueError, lambda: t1 == t2)
  
      def test_pickling(self):
***************
*** 2402,2409 ****
          cls = self.theclass
  
!         # Primarily trying to ensure that utcoffset() gets called even if
!         # the operands have the same tzinfo member.  Subtraction didn't
!         # used to do this, and it makes a difference for DST-aware tzinfo
!         # instances.
          class OperandDependentOffset(tzinfo):
              def utcoffset(self, t):
--- 2442,2447 ----
          cls = self.theclass
  
!         # Ensure that utcoffset() is ignored when the operands have the
!         # same tzinfo member.
          class OperandDependentOffset(tzinfo):
              def utcoffset(self, t):
***************
*** 2420,2423 ****
--- 2458,2473 ----
              for y in d0, d1, d2:
                  got = x - y
+                 expected = timedelta(minutes=x.minute - y.minute)
+                 self.assertEqual(got, expected)
+ 
+         # OTOH, if the tzinfo members are distinct, utcoffsets aren't
+         # ignored.
+         base = cls(8, 9, 10, 11, 12, 13, 14)
+         d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
+         d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
+         d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
+         for x in d0, d1, d2:
+             for y in d0, d1, d2:
+                 got = x - y
                  if (x is d0 or x is d1) and (y is d0 or y is d1):
                      expected = timedelta(0)
***************
*** 2431,2434 ****
--- 2481,2514 ----
                  self.assertEqual(got, expected)
  
+     def test_mixed_compare(self):
+         t1 = datetime(1, 2, 3, 4, 5, 6, 7)
+         t2 = datetimetz(1, 2, 3, 4, 5, 6, 7)
+         self.assertEqual(t1, t2)
+         t2 = t2.replace(tzinfo=None)
+         self.assertEqual(t1, t2)
+         t2 = t2.replace(tzinfo=FixedOffset(None, ""))
+         self.assertEqual(t1, t2)
+         if CMP_BUG_FIXED:
+             t2 = t2.replace(tzinfo=FixedOffset(0, ""))
+             self.assertRaises(TypeError, lambda: t1 == t2)
+ 
+         # In datetimetz w/ identical tzinfo objects, utcoffset is ignored.
+         class Varies(tzinfo):
+             def __init__(self):
+                 self.offset = 22
+             def utcoffset(self, t):
+                 self.offset += 1
+                 return self.offset
+ 
+         v = Varies()
+         t1 = t2.replace(tzinfo=v)
+         t2 = t2.replace(tzinfo=v)
+         self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
+         self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
+         self.assertEqual(t1, t2)
+ 
+         # But if they're not identical, it isn't ignored.
+         t2 = t2.replace(tzinfo=Varies())
+         self.failUnless(t1 < t2)  # t1's offset counter still going up
  
  def test_suite():