[Python-checkins] cpython: Issue #15006: Allow equality comparison between naive and aware time

alexander.belopolsky python-checkins at python.org
Sat Jun 16 02:20:18 CEST 2012


http://hg.python.org/cpython/rev/8272699973cb
changeset:   77458:8272699973cb
user:        Alexander Belopolsky <alexander.belopolsky at gmail.com>
date:        Fri Jun 15 20:19:47 2012 -0400
summary:
  Issue #15006: Allow equality comparison between naive and aware time
or datetime objects.

files:
  Doc/library/datetime.rst   |  20 ++++++++++++++++++--
  Lib/datetime.py            |  22 ++++++++++++++--------
  Lib/test/datetimetester.py |  12 ++++++------
  Misc/NEWS                  |   5 ++---
  Modules/_datetimemodule.c  |  16 ++++++++++++++++
  5 files changed, 56 insertions(+), 19 deletions(-)


diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst
--- a/Doc/library/datetime.rst
+++ b/Doc/library/datetime.rst
@@ -901,13 +901,21 @@
    *datetime1* is considered less than *datetime2* when *datetime1* precedes
    *datetime2* in time.
 
-   If one comparand is naive and the other is aware, :exc:`TypeError` is raised.
+   If one comparand is naive and the other is aware, :exc:`TypeError`
+   is raised if an order comparison is attempted.  For equality
+   comparisons, naive instances are never equal to aware instances.
+
    If both comparands are aware, and have the same :attr:`tzinfo` attribute, the
    common :attr:`tzinfo` attribute is ignored and the base datetimes are
    compared.  If both comparands are aware and have different :attr:`tzinfo`
    attributes, the comparands are first adjusted by subtracting their UTC
    offsets (obtained from ``self.utcoffset()``).
 
+   .. versionchanged:: 3.3
+
+   Equality comparisons between naive and aware :class:`datetime`
+   instances don't raise :exc:`TypeError`.
+
    .. note::
 
       In order to stop comparison from falling back to the default scheme of comparing
@@ -1316,7 +1324,10 @@
 
 * comparison of :class:`.time` to :class:`.time`, where *a* is considered less
   than *b* when *a* precedes *b* in time.  If one comparand is naive and the other
-  is aware, :exc:`TypeError` is raised.  If both comparands are aware, and have
+  is aware, :exc:`TypeError` is raised if an order comparison is attempted. For equality
+  comparisons, naive instances are never equal to aware instances.
+
+  If both comparands are aware, and have
   the same :attr:`tzinfo` attribute, the common :attr:`tzinfo` attribute is
   ignored and the base times are compared.  If both comparands are aware and
   have different :attr:`tzinfo` attributes, the comparands are first adjusted by
@@ -1326,6 +1337,11 @@
   different type, :exc:`TypeError` is raised unless the comparison is ``==`` or
   ``!=``.  The latter cases return :const:`False` or :const:`True`, respectively.
 
+  .. versionchanged:: 3.3
+
+  Equality comparisons between naive and aware :class:`time` instances
+  don't raise :exc:`TypeError`.
+
 * hash, use as dict key
 
 * efficient pickling
diff --git a/Lib/datetime.py b/Lib/datetime.py
--- a/Lib/datetime.py
+++ b/Lib/datetime.py
@@ -1065,13 +1065,13 @@
 
     def __eq__(self, other):
         if isinstance(other, time):
-            return self._cmp(other) == 0
+            return self._cmp(other, allow_mixed=True) == 0
         else:
             return False
 
     def __ne__(self, other):
         if isinstance(other, time):
-            return self._cmp(other) != 0
+            return self._cmp(other, allow_mixed=True) != 0
         else:
             return True
 
@@ -1099,7 +1099,7 @@
         else:
             _cmperror(self, other)
 
-    def _cmp(self, other):
+    def _cmp(self, other, allow_mixed=False):
         assert isinstance(other, time)
         mytz = self._tzinfo
         ottz = other._tzinfo
@@ -1118,7 +1118,10 @@
                        (other._hour, other._minute, other._second,
                         other._microsecond))
         if myoff is None or otoff is None:
-            raise TypeError("cannot compare naive and aware times")
+            if allow_mixed:
+                return 2 # arbitrary non-zero value
+            else:
+                raise TypeError("cannot compare naive and aware times")
         myhhmm = self._hour * 60 + self._minute - myoff//timedelta(minutes=1)
         othhmm = other._hour * 60 + other._minute - otoff//timedelta(minutes=1)
         return _cmp((myhhmm, self._second, self._microsecond),
@@ -1615,7 +1618,7 @@
 
     def __eq__(self, other):
         if isinstance(other, datetime):
-            return self._cmp(other) == 0
+            return self._cmp(other, allow_mixed=True) == 0
         elif not isinstance(other, date):
             return NotImplemented
         else:
@@ -1623,7 +1626,7 @@
 
     def __ne__(self, other):
         if isinstance(other, datetime):
-            return self._cmp(other) != 0
+            return self._cmp(other, allow_mixed=True) != 0
         elif not isinstance(other, date):
             return NotImplemented
         else:
@@ -1661,7 +1664,7 @@
         else:
             _cmperror(self, other)
 
-    def _cmp(self, other):
+    def _cmp(self, other, allow_mixed=False):
         assert isinstance(other, datetime)
         mytz = self._tzinfo
         ottz = other._tzinfo
@@ -1682,7 +1685,10 @@
                         other._hour, other._minute, other._second,
                         other._microsecond))
         if myoff is None or otoff is None:
-            raise TypeError("cannot compare naive and aware datetimes")
+            if allow_mixed:
+                return 2 # arbitrary non-zero value
+            else:
+                raise TypeError("cannot compare naive and aware datetimes")
         # XXX What follows could be done more efficiently...
         diff = self - other     # this will take offsets into account
         if diff.days < 0:
diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py
--- a/Lib/test/datetimetester.py
+++ b/Lib/test/datetimetester.py
@@ -2544,7 +2544,7 @@
         self.assertEqual(t1, t2)
         self.assertEqual(t1, t3)
         self.assertEqual(t2, t3)
-        self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
+        self.assertNotEqual(t4, t5) # mixed tz-aware & naive
         self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
         self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
 
@@ -2696,7 +2696,7 @@
         t2 = t2.replace(tzinfo=FixedOffset(None, ""))
         self.assertEqual(t1, t2)
         t2 = t2.replace(tzinfo=FixedOffset(0, ""))
-        self.assertRaises(TypeError, lambda: t1 == t2)
+        self.assertNotEqual(t1, t2)
 
         # In time w/ identical tzinfo objects, utcoffset is ignored.
         class Varies(tzinfo):
@@ -2801,16 +2801,16 @@
                            microsecond=1)
         self.assertTrue(t1 > t2)
 
-        # Make t2 naive and it should fail.
+        # Make t2 naive and it should differ.
         t2 = self.theclass.min
-        self.assertRaises(TypeError, lambda: t1 == t2)
+        self.assertNotEqual(t1, t2)
         self.assertEqual(t2, t2)
 
         # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
         class Naive(tzinfo):
             def utcoffset(self, dt): return None
         t2 = self.theclass(5, 6, 7, tzinfo=Naive())
-        self.assertRaises(TypeError, lambda: t1 == t2)
+        self.assertNotEqual(t1, t2)
         self.assertEqual(t2, t2)
 
         # OTOH, it's OK to compare two of these mixing the two ways of being
@@ -3327,7 +3327,7 @@
         t2 = t2.replace(tzinfo=FixedOffset(None, ""))
         self.assertEqual(t1, t2)
         t2 = t2.replace(tzinfo=FixedOffset(0, ""))
-        self.assertRaises(TypeError, lambda: t1 == t2)
+        self.assertNotEqual(t1, t2)
 
         # In datetime w/ identical tzinfo objects, utcoffset is ignored.
         class Varies(tzinfo):
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -24,9 +24,8 @@
 Library
 -------
 
-- Issue #14938: importlib.abc.SourceLoader.is_package() will not consider a
-  module whose name ends in '__init__' a package (e.g. importing pkg.__init__
-  directly should be considered a module, not a package).
+- Issue #15006: Allow equality comparison between naive and aware
+  time or datetime objects.
 
 - Issue #14982: Document that pkgutil's iteration functions require the
   non-standard iter_modules() method to be defined by an importer (something
diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c
--- a/Modules/_datetimemodule.c
+++ b/Modules/_datetimemodule.c
@@ -3707,6 +3707,14 @@
                    TIME_GET_MICROSECOND(other);
         result = diff_to_bool(diff, op);
     }
+    else if (op == Py_EQ) {
+        result = Py_False;
+        Py_INCREF(result);
+    }
+    else if (op == Py_NE) {
+        result = Py_True;
+        Py_INCREF(result);
+    }
     else {
         PyErr_SetString(PyExc_TypeError,
                         "can't compare offset-naive and "
@@ -4584,6 +4592,14 @@
         Py_DECREF(delta);
         result = diff_to_bool(diff, op);
     }
+    else if (op == Py_EQ) {
+        result = Py_False;
+        Py_INCREF(result);
+    }
+    else if (op == Py_NE) {
+        result = Py_True;
+        Py_INCREF(result);
+    }
     else {
         PyErr_SetString(PyExc_TypeError,
                         "can't compare offset-naive and "

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list