Skip to content

Commit 6fd5cc3

Browse files
authored
BUG: Timestamp == date match stdlib (#36131)
1 parent 13da932 commit 6fd5cc3

File tree

5 files changed

+61
-1
lines changed

5 files changed

+61
-1
lines changed

doc/source/whatsnew/v1.3.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ Deprecations
156156
- Deprecated allowing subclass-specific keyword arguments in the :class:`Index` constructor, use the specific subclass directly instead (:issue:`14093`,:issue:`21311`,:issue:`22315`,:issue:`26974`)
157157
- Deprecated ``astype`` of datetimelike (``timedelta64[ns]``, ``datetime64[ns]``, ``Datetime64TZDtype``, ``PeriodDtype``) to integer dtypes, use ``values.view(...)`` instead (:issue:`38544`)
158158
- Deprecated keyword ``try_cast`` in :meth:`Series.where`, :meth:`Series.mask`, :meth:`DataFrame.where`, :meth:`DataFrame.mask`; cast results manually if desired (:issue:`38836`)
159+
- Deprecated comparison of :class:`Timestamp` object with ``datetime.date`` objects. Instead of e.g. ``ts <= mydate`` use ``ts <= pd.Timestamp(mydate)`` or ``ts.date() <= mydate`` (:issue:`36131`)
159160
-
160161

161162
.. ---------------------------------------------------------------------------

pandas/_libs/tslibs/timestamps.pyx

+15
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ from numpy cimport int8_t, int64_t, ndarray, uint8_t
1616
cnp.import_array()
1717

1818
from cpython.datetime cimport ( # alias bc `tzinfo` is a kwarg below
19+
PyDate_Check,
1920
PyDateTime_Check,
2021
PyDateTime_IMPORT,
2122
PyDelta_Check,
@@ -281,6 +282,20 @@ cdef class _Timestamp(ABCTimestamp):
281282
return np.zeros(other.shape, dtype=np.bool_)
282283
return NotImplemented
283284

285+
elif PyDate_Check(other):
286+
# returning NotImplemented defers to the `date` implementation
287+
# which incorrectly drops tz and normalizes to midnight
288+
# before comparing
289+
# We follow the stdlib datetime behavior of never being equal
290+
warnings.warn(
291+
"Comparison of Timestamp with datetime.date is deprecated in "
292+
"order to match the standard library behavior. "
293+
"In a future version these will be considered non-comparable."
294+
"Use 'ts == pd.Timestamp(date)' or 'ts.date() == date' instead.",
295+
FutureWarning,
296+
stacklevel=1,
297+
)
298+
return NotImplemented
284299
else:
285300
return NotImplemented
286301

pandas/tests/frame/indexing/test_indexing.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -1429,7 +1429,10 @@ def test_loc_setitem_datetime_coercion(self):
14291429
assert Timestamp("2008-08-08") == df.loc[0, "c"]
14301430
assert Timestamp("2008-08-08") == df.loc[1, "c"]
14311431
df.loc[2, "c"] = date(2005, 5, 5)
1432-
assert Timestamp("2005-05-05") == df.loc[2, "c"]
1432+
with tm.assert_produces_warning(FutureWarning):
1433+
# Comparing Timestamp to date obj is deprecated
1434+
assert Timestamp("2005-05-05") == df.loc[2, "c"]
1435+
assert Timestamp("2005-05-05").date() == df.loc[2, "c"]
14331436

14341437
def test_loc_setitem_datetimelike_with_inference(self):
14351438
# GH 7592

pandas/tests/indexes/datetimes/test_indexing.py

+5
Original file line numberDiff line numberDiff line change
@@ -613,8 +613,13 @@ def test_get_indexer_mixed_dtypes(self, target):
613613
([date(9999, 1, 1), date(9999, 1, 1)], [-1, -1]),
614614
],
615615
)
616+
# FIXME: these warnings are flaky GH#36131
617+
@pytest.mark.filterwarnings(
618+
"ignore:Comparison of Timestamp with datetime.date:FutureWarning"
619+
)
616620
def test_get_indexer_out_of_bounds_date(self, target, positions):
617621
values = DatetimeIndex([Timestamp("2020-01-01"), Timestamp("2020-01-02")])
622+
618623
result = values.get_indexer(target)
619624
expected = np.array(positions, dtype=np.intp)
620625
tm.assert_numpy_array_equal(result, expected)

pandas/tests/scalar/timestamp/test_comparisons.py

+36
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,42 @@ def test_compare_invalid(self):
142142
assert val != np.float64(1)
143143
assert val != np.int64(1)
144144

145+
@pytest.mark.parametrize("tz", [None, "US/Pacific"])
146+
def test_compare_date(self, tz):
147+
# GH#36131 comparing Timestamp with date object is deprecated
148+
ts = Timestamp.now(tz)
149+
dt = ts.to_pydatetime().date()
150+
# These are incorrectly considered as equal because they
151+
# dispatch to the date comparisons which truncates ts
152+
153+
for left, right in [(ts, dt), (dt, ts)]:
154+
with tm.assert_produces_warning(FutureWarning):
155+
assert left == right
156+
with tm.assert_produces_warning(FutureWarning):
157+
assert not left != right
158+
with tm.assert_produces_warning(FutureWarning):
159+
assert not left < right
160+
with tm.assert_produces_warning(FutureWarning):
161+
assert left <= right
162+
with tm.assert_produces_warning(FutureWarning):
163+
assert not left > right
164+
with tm.assert_produces_warning(FutureWarning):
165+
assert left >= right
166+
167+
# Once the deprecation is enforced, the following assertions
168+
# can be enabled:
169+
# assert not left == right
170+
# assert left != right
171+
#
172+
# with pytest.raises(TypeError):
173+
# left < right
174+
# with pytest.raises(TypeError):
175+
# left <= right
176+
# with pytest.raises(TypeError):
177+
# left > right
178+
# with pytest.raises(TypeError):
179+
# left >= right
180+
145181
def test_cant_compare_tz_naive_w_aware(self, utc_fixture):
146182
# see GH#1404
147183
a = Timestamp("3/12/2012")

0 commit comments

Comments
 (0)