Skip to content

Commit a193df1

Browse files
AlexandreDecanjreback
authored andcommitted
BUG: GH24011 - Rich comparisons of Timestamps now return NotImplemented (#24021)
1 parent 1a30601 commit a193df1

File tree

3 files changed

+34
-15
lines changed

3 files changed

+34
-15
lines changed

doc/source/whatsnew/v0.25.0.rst

+2
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ Backwards incompatible API changes
4444

4545
.. _whatsnew_0250.api_breaking.utc_offset_indexing:
4646

47+
4748
Indexing with date strings with UTC offsets
4849
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4950

@@ -215,6 +216,7 @@ Other API Changes
215216
- :class:`DatetimeTZDtype` will now standardize pytz timezones to a common timezone instance (:issue:`24713`)
216217
- ``Timestamp`` and ``Timedelta`` scalars now implement the :meth:`to_numpy` method as aliases to :meth:`Timestamp.to_datetime64` and :meth:`Timedelta.to_timedelta64`, respectively. (:issue:`24653`)
217218
- :meth:`Timestamp.strptime` will now rise a ``NotImplementedError`` (:issue:`25016`)
219+
- Comparing :class:`Timestamp` with unsupported objects now returns :py:obj:`NotImplemented` instead of raising ``TypeError``. This implies that unsupported rich comparisons are delegated to the other object, and are now consistent with Python 3 behavior for ``datetime`` objects (:issue:`24011`)
218220
- Bug in :meth:`DatetimeIndex.snap` which didn't preserving the ``name`` of the input :class:`Index` (:issue:`25575`)
219221

220222
.. _whatsnew_0250.deprecations:

pandas/_libs/tslibs/timestamps.pyx

+2-15
Original file line numberDiff line numberDiff line change
@@ -227,26 +227,13 @@ cdef class _Timestamp(datetime):
227227
if is_datetime64_object(other):
228228
other = Timestamp(other)
229229
else:
230-
if op == Py_EQ:
231-
return False
232-
elif op == Py_NE:
233-
return True
234-
235-
# only allow ==, != ops
236-
raise TypeError('Cannot compare type %r with type %r' %
237-
(type(self).__name__,
238-
type(other).__name__))
230+
return NotImplemented
239231
elif is_array(other):
240232
# avoid recursion error GH#15183
241233
return PyObject_RichCompare(np.array([self]), other, op)
242234
return PyObject_RichCompare(other, self, reverse_ops[op])
243235
else:
244-
if op == Py_EQ:
245-
return False
246-
elif op == Py_NE:
247-
return True
248-
raise TypeError('Cannot compare type %r with type %r' %
249-
(type(self).__name__, type(other).__name__))
236+
return NotImplemented
250237

251238
self._assert_tzawareness_compat(other)
252239
return cmp_scalar(self.value, ots.value, op)

pandas/tests/scalar/timestamp/test_comparisons.py

+30
Original file line numberDiff line numberDiff line change
@@ -156,3 +156,33 @@ def test_timestamp_compare_with_early_datetime(self):
156156
assert stamp >= datetime(1600, 1, 1)
157157
assert stamp < datetime(2700, 1, 1)
158158
assert stamp <= datetime(2700, 1, 1)
159+
160+
161+
def test_rich_comparison_with_unsupported_type():
162+
# Comparisons with unsupported objects should return NotImplemented
163+
# (it previously raised TypeError, see #24011)
164+
165+
class Inf(object):
166+
def __lt__(self, o):
167+
return False
168+
169+
def __le__(self, o):
170+
return isinstance(o, Inf)
171+
172+
def __gt__(self, o):
173+
return not isinstance(o, Inf)
174+
175+
def __ge__(self, o):
176+
return True
177+
178+
def __eq__(self, o):
179+
return isinstance(o, Inf)
180+
181+
inf = Inf()
182+
timestamp = Timestamp('2018-11-30')
183+
184+
for left, right in [(inf, timestamp), (timestamp, inf)]:
185+
assert left > right or left < right
186+
assert left >= right or left <= right
187+
assert not (left == right)
188+
assert left != right

0 commit comments

Comments
 (0)