[Zope3-checkins] CVS: Zope3/src/datetime - _datetime.py:1.33

Tim Peters tim.one@comcast.net
Fri, 7 Feb 2003 16:07:09 -0500


Update of /cvs-repository/Zope3/src/datetime
In directory cvs.zope.org:/tmp/cvs-serv1986/src/datetime

Modified Files:
	_datetime.py 
Log Message:
Comparison for timedelta, time, date and datetime objects:  rewrote to
use rich comparisons.  __eq__ and __ne__ no longer complain if they
don't know how to compare to the other thing.  If no meaningful way to
compare is known, saying "not equal" is sensible.  This allows things
like

    if adatetime in some_sequence:
and
    somedict[adatetime] = whatever

to work as expected even if some_sequence contains non-datetime objects,
or somedict non-datetime keys, because they only call __eq__.

It still complains (raises TypeError) for mixed-type comparisons in
contexts that require a total ordering, such as list.sort(), use as a
key in a BTree-based data structure, and cmp().


=== Zope3/src/datetime/_datetime.py 1.32 => 1.33 ===
--- Zope3/src/datetime/_datetime.py:1.32	Fri Jan 31 22:24:43 2003
+++ Zope3/src/datetime/_datetime.py	Fri Feb  7 16:06:38 2003
@@ -286,6 +286,34 @@
     if tz is not None and not isinstance(tz, tzinfo):
         raise TypeError("tzinfo argument must be None or of a tzinfo subclass")
 
+
+# Notes on comparison:  In general, datetime module comparison operators raise
+# TypeError when they don't know how to do a comparison themself.  If they
+# returned NotImplemented instead, comparison could (silently) fall back to
+# the default compare-objects-by-comparing-their-memory-addresses strategy,
+# and that's not helpful.  There are two exceptions:
+#
+# 1. For date and datetime, if the other object has a "timetuple" attr,
+#    NotImplemented is returned.  This is a hook to allow other kinds of
+#    datetime-like objects a chance to intercept the comparison.
+#
+# 2. Else __eq__ and __ne__ return False and True, respectively.  This is
+#    so opertaions like
+#
+#        x == y
+#        x != y
+#        x in sequence
+#        x not in sequence
+#        dict[x] = y
+#
+#    don't raise annoying TypeErrors just because a datetime object
+#    is part of a heterogeneous collection.  If there's no known way to
+#    compare X to a datetime, saying they're not equal is reasonable.
+
+def _cmperror(x, y):
+    raise TypeError("can't compare '%s' to '%s'" % (
+                    type(x).__name__, type(y).__name__))
+
 # This is a start at a struct tm workalike.  Goals:
 #
 # + Works the same way across platforms.
@@ -596,10 +624,46 @@
 
     __floordiv__ = __div__
 
-    def __cmp__(self, other):
-        if not isinstance(other, timedelta):
-            raise TypeError, ("can't compare timedelta to %s instance" %
-                              type(other).__name__)
+    # Comparisons.
+
+    def __eq__(self, other):
+        if isinstance(other, timedelta):
+            return self.__cmp(other) == 0
+        else:
+            return False
+
+    def __ne__(self, other):
+        if isinstance(other, timedelta):
+            return self.__cmp(other) != 0
+        else:
+            return True
+
+    def __le__(self, other):
+        if isinstance(other, timedelta):
+            return self.__cmp(other) <= 0
+        else:
+            _cmperror(self, other)
+
+    def __lt__(self, other):
+        if isinstance(other, timedelta):
+            return self.__cmp(other) < 0
+        else:
+            _cmperror(self, other)
+
+    def __ge__(self, other):
+        if isinstance(other, timedelta):
+            return self.__cmp(other) >= 0
+        else:
+            _cmperror(self, other)
+
+    def __gt__(self, other):
+        if isinstance(other, timedelta):
+            return self.__cmp(other) > 0
+        else:
+            _cmperror(self, other)
+
+    def __cmp(self, other):
+        assert isinstance(other, timedelta)
         return cmp(self.__getstate(), other.__getstate())
 
     def __hash__(self):
@@ -763,17 +827,61 @@
         _check_date_fields(year, month, day)
         return date(year, month, day)
 
-    def __cmp__(self, other):
-        "Three-way comparison."
+    # Comparisons.
+
+    def __eq__(self, other):
+        if isinstance(other, date):
+            return self.__cmp(other) == 0
+        elif hasattr(other, "timetuple"):
+            return NotImplemented
+        else:
+            return False
+
+    def __ne__(self, other):
+        if isinstance(other, date):
+            return self.__cmp(other) != 0
+        elif hasattr(other, "timetuple"):
+            return NotImplemented
+        else:
+            return True
+
+    def __le__(self, other):
+        if isinstance(other, date):
+            return self.__cmp(other) <= 0
+        elif hasattr(other, "timetuple"):
+            return NotImplemented
+        else:
+            _cmperror(self, other)
+
+    def __lt__(self, other):
+        if isinstance(other, date):
+            return self.__cmp(other) < 0
+        elif hasattr(other, "timetuple"):
+            return NotImplemented
+        else:
+            _cmperror(self, other)
+
+    def __ge__(self, other):
         if isinstance(other, date):
-            y, m, d = self.__year, self.__month, self.__day
-            y2, m2, d2 = other.__year, other.__month, other.__day
-            return cmp((y, m, d), (y2, m2, d2))
+            return self.__cmp(other) >= 0
         elif hasattr(other, "timetuple"):
             return NotImplemented
         else:
-            raise TypeError, ("can't compare date to %s instance" %
-                              type(other).__name__)
+            _cmperror(self, other)
+
+    def __gt__(self, other):
+        if isinstance(other, date):
+            return self.__cmp(other) > 0
+        elif hasattr(other, "timetuple"):
+            return NotImplemented
+        else:
+            _cmperror(self, other)
+
+    def __cmp(self, other):
+        assert isinstance(other, date)
+        y, m, d = self.__year, self.__month, self.__day
+        y2, m2, d2 = other.__year, other.__month, other.__day
+        return cmp((y, m, d), (y2, m2, d2))
 
     def __hash__(self):
         "Hash."
@@ -1003,12 +1111,46 @@
 
     # Standard conversions, __hash__ (and helpers)
 
-    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__))
+    # Comparisons.
+
+    def __eq__(self, other):
+        if isinstance(other, time):
+            return self.__cmp(other) == 0
+        else:
+            return False
+
+    def __ne__(self, other):
+        if isinstance(other, time):
+            return self.__cmp(other) != 0
+        else:
+            return True
+
+    def __le__(self, other):
+        if isinstance(other, time):
+            return self.__cmp(other) <= 0
+        else:
+            _cmperror(self, other)
+
+    def __lt__(self, other):
+        if isinstance(other, time):
+            return self.__cmp(other) < 0
+        else:
+            _cmperror(self, other)
+
+    def __ge__(self, other):
+        if isinstance(other, time):
+            return self.__cmp(other) >= 0
+        else:
+            _cmperror(self, other)
+
+    def __gt__(self, other):
+        if isinstance(other, time):
+            return self.__cmp(other) > 0
+        else:
+            _cmperror(self, other)
+
+    def __cmp(self, other):
+        assert isinstance(other, time)
         mytz = self._tzinfo
         ottz = other._tzinfo
         myoff = otoff = None
@@ -1477,14 +1619,58 @@
         offset = _check_utc_offset("dst", offset)
         return offset
 
-    def __cmp__(self, other):
-        if not isinstance(other, datetime):
-            if hasattr(other, "timetuple"):
-                return NotImplemented
-            else:
-                # XXX Buggy in 2.2.2.
-                raise TypeError("can't compare '%s' to '%s'" % (
-                                type(self).__name__, type(other).__name__))
+    # Comparisons.
+
+    def __eq__(self, other):
+        if isinstance(other, datetime):
+            return self.__cmp(other) == 0
+        elif hasattr(other, "timetuple"):
+            return NotImplemented
+        else:
+            return False
+
+    def __ne__(self, other):
+        if isinstance(other, datetime):
+            return self.__cmp(other) != 0
+        elif hasattr(other, "timetuple"):
+            return NotImplemented
+        else:
+            return True
+
+    def __le__(self, other):
+        if isinstance(other, datetime):
+            return self.__cmp(other) <= 0
+        elif hasattr(other, "timetuple"):
+            return NotImplemented
+        else:
+            _cmperror(self, other)
+
+    def __lt__(self, other):
+        if isinstance(other, datetime):
+            return self.__cmp(other) < 0
+        elif hasattr(other, "timetuple"):
+            return NotImplemented
+        else:
+            _cmperror(self, other)
+
+    def __ge__(self, other):
+        if isinstance(other, datetime):
+            return self.__cmp(other) >= 0
+        elif hasattr(other, "timetuple"):
+            return NotImplemented
+        else:
+            _cmperror(self, other)
+
+    def __gt__(self, other):
+        if isinstance(other, datetime):
+            return self.__cmp(other) > 0
+        elif hasattr(other, "timetuple"):
+            return NotImplemented
+        else:
+            _cmperror(self, other)
+
+    def __cmp(self, other):
+        assert isinstance(other, datetime)
         mytz = self._tzinfo
         ottz = other._tzinfo
         myoff = otoff = None