From 528ca53b7455b3307cb0e3f097ae951191521ba8 Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Thu, 16 Sep 2004 01:30:50 +0000 Subject: SF bug #1028306: date-datetime comparison Treat comparing a date to a datetime like a mixed-type comparison. --- Lib/test/test_datetime.py | 43 +++++++++++++++++++++++++++++++++++++++++++ Misc/NEWS | 10 ++++++++++ Modules/datetimemodule.c | 12 +++++++++++- 3 files changed, 64 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_datetime.py b/Lib/test/test_datetime.py index 2a6aca2..ab7bd71 100644 --- a/Lib/test/test_datetime.py +++ b/Lib/test/test_datetime.py @@ -3151,6 +3151,48 @@ class TestTimezoneConversions(unittest.TestCase): fstart += HOUR +############################################################################# +# oddballs + +class Oddballs(unittest.TestCase): + + def test_bug_1028306(self): + # Trying to compare a date to a datetime should act like a mixed- + # type comparison, despite that datetime is a subclass of date. + as_date = date.today() + as_datetime = datetime.combine(as_date, time()) + self.assert_(as_date != as_datetime) + self.assert_(as_datetime != as_date) + self.assert_(not as_date == as_datetime) + self.assert_(not as_datetime == as_date) + self.assertRaises(TypeError, lambda: as_date < as_datetime) + self.assertRaises(TypeError, lambda: as_datetime < as_date) + self.assertRaises(TypeError, lambda: as_date <= as_datetime) + self.assertRaises(TypeError, lambda: as_datetime <= as_date) + self.assertRaises(TypeError, lambda: as_date > as_datetime) + self.assertRaises(TypeError, lambda: as_datetime > as_date) + self.assertRaises(TypeError, lambda: as_date >= as_datetime) + self.assertRaises(TypeError, lambda: as_datetime >= as_date) + + # Neverthelss, comparison should work with the base-class (date) + # projection if use of a date method is forced. + self.assert_(as_date.__eq__(as_datetime)) + different_day = (as_date.day + 1) % 20 + 1 + self.assert_(not as_date.__eq__(as_datetime.replace(day= + different_day))) + + # And date should compare with other subclasses of date. If a + # subclass wants to stop this, it's up to the subclass to do so. + date_sc = SubclassDate(as_date.year, as_date.month, as_date.day) + self.assertEqual(as_date, date_sc) + self.assertEqual(date_sc, as_date) + + # Ditto for datetimes. + datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month, + as_date.day, 0, 0, 0) + self.assertEqual(as_datetime, datetime_sc) + self.assertEqual(datetime_sc, as_datetime) + def test_suite(): allsuites = [unittest.makeSuite(klass, 'test') for klass in (TestModule, @@ -3163,6 +3205,7 @@ def test_suite(): TestTimeTZ, TestDateTimeTZ, TestTimezoneConversions, + Oddballs, ) ] return unittest.TestSuite(allsuites) diff --git a/Misc/NEWS b/Misc/NEWS index 1ade365..fcc52d7 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -22,6 +22,16 @@ Extension modules Library ------- +- SF bug #1028306: Trying to compare a ``datetime.date`` to a + ``datetime.datetime`` mistakenly compared only the year, month and day. + Now it acts like a mixed-type comparison: ``False`` for ``==``, + ``True`` for ``!=``, and raises ``TypeError`` for other comparison + operators. Because datetime is a subclass of date, comparing only the + base class (date) members can still be done, if that's desired, by + forcing using of the approprate date method; e.g., + ``a_date.__eq__(a_datetime)`` is true if and only if the year, month + and day members of ``a_date`` and ``a_datetime`` are equal. + - bdist_rpm now supports command line options --force-arch, {pre,post}-install, {pre,post}-uninstall, and {prep,build,install,clean,verify}-script. diff --git a/Modules/datetimemodule.c b/Modules/datetimemodule.c index ee7387c..6312e21 100644 --- a/Modules/datetimemodule.c +++ b/Modules/datetimemodule.c @@ -4075,7 +4075,17 @@ datetime_richcompare(PyDateTime_DateTime *self, PyObject *other, int op) int offset1, offset2; if (! PyDateTime_Check(other)) { - if (PyObject_HasAttrString(other, "timetuple")) { + /* If other has a "timetuple" attr, that's an advertised + * hook for other classes to ask to get comparison control. + * However, date instances have a timetuple attr, and we + * don't want to allow that comparison. Because datetime + * is a subclass of date, when mixing date and datetime + * in a comparison, Python gives datetime the first shot + * (it's the more specific subtype). So we can stop that + * combination here reliably. + */ + if (PyObject_HasAttrString(other, "timetuple") && + ! PyDate_Check(other)) { /* A hook for other kinds of datetime objects. */ Py_INCREF(Py_NotImplemented); return Py_NotImplemented; -- cgit v0.12