From 761ac0e839f34606e5e4f0b08f450cbb3a48504b Mon Sep 17 00:00:00 2001 From: Aniket Singh Yadav Date: Mon, 25 Aug 2025 04:26:39 +0530 Subject: [PATCH 01/31] DOC: Clarify list-like vs scalar in Series.eq docstring --- pandas/core/series.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pandas/core/series.py b/pandas/core/series.py index b40e71f2b5b42..52a045c2d0f37 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -6072,7 +6072,9 @@ def eq( Parameters ---------- other : Series or scalar value - The second operand in this operation. + The second operand in this operation. Only `np.ndarray`, `list`, `tuple`, + and `Series` are considered "list-like" by pandas. All other types will + be treated as scalar values. level : int or name Broadcast across a level, matching Index values on the passed MultiIndex level. From 2f98bd4d7ccaa694b8e7871a0ace11f40c91ae0a Mon Sep 17 00:00:00 2001 From: Aniket Singh Yadav Date: Tue, 26 Aug 2025 23:28:34 +0530 Subject: [PATCH 02/31] BUG: Remove special-casing for date objects in DatetimeIndex indexing (GH#62157) --- pandas/core/indexes/base.py | 6 ------ pandas/core/series.py | 4 +--- pandas/tests/frame/indexing/test_indexing.py | 11 +++++++++++ 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 59ac122e4f9ea..0426949c9e1a5 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -39,7 +39,6 @@ no_default, ) from pandas._libs.tslibs import ( - OutOfBoundsDatetime, Timestamp, tz_compare, ) @@ -6204,11 +6203,6 @@ def _maybe_downcast_for_indexing(self, other: Index) -> tuple[Index, Index]: # standardize on UTC return self.tz_convert("UTC"), other.tz_convert("UTC") - elif self.inferred_type == "date" and isinstance(other, ABCDatetimeIndex): - try: - return type(other)(self), other - except OutOfBoundsDatetime: - return self, other elif self.inferred_type == "timedelta" and isinstance(other, ABCTimedeltaIndex): # TODO: we dont have tests that get here return type(other)(self), other diff --git a/pandas/core/series.py b/pandas/core/series.py index 0972cbff8281e..63c9963fb7eac 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -6073,9 +6073,7 @@ def eq( Parameters ---------- other : Series or scalar value - The second operand in this operation. Only `np.ndarray`, `list`, `tuple`, - and `Series` are considered "list-like" by pandas. All other types will - be treated as scalar values. + The second operand in this operation. level : int or name Broadcast across a level, matching Index values on the passed MultiIndex level. diff --git a/pandas/tests/frame/indexing/test_indexing.py b/pandas/tests/frame/indexing/test_indexing.py index 0c99b08cb30c4..f0510ac365957 100644 --- a/pandas/tests/frame/indexing/test_indexing.py +++ b/pandas/tests/frame/indexing/test_indexing.py @@ -1880,6 +1880,17 @@ def test_add_new_column_infer_string(): tm.assert_frame_equal(df, expected) +def test_datetime_indexer_consistency_pyarrow_date32(): + # GH#62158 + ser = Series(["2016-01-01"], dtype="date32[pyarrow]") + ser3 = ser.astype("datetime64[ns]") + dti = Index(ser3) + # All should be consistent + assert dti.get_loc(ser[0]) == 0 + tm.assert_numpy_array_equal(dti.get_indexer(ser.values), [0]) + tm.assert_numpy_array_equal(dti.get_indexer(ser.values.astype(object)), [0]) + + class TestSetitemValidation: # This is adapted from pandas/tests/arrays/masked/test_indexing.py def _check_setitem_invalid(self, df, invalid, indexer): From 69ae80770438a2f1aac4b3e3addd43eafff40e22 Mon Sep 17 00:00:00 2001 From: Aniket Singh Yadav Date: Wed, 27 Aug 2025 02:47:42 +0530 Subject: [PATCH 03/31] BUG: Remove special-casing for date objects in DatetimeIndex indexing (GH#62158) --- pandas/core/indexes/datetimes.py | 8 +++++++- pandas/tests/frame/methods/test_asfreq.py | 2 +- pandas/tests/indexes/datetimes/test_indexing.py | 15 ++++++++++----- pandas/tests/series/test_arithmetic.py | 3 ++- 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index ce6ea1ed980dd..f7dea0a6184b4 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -629,7 +629,13 @@ def get_loc(self, key): def _maybe_cast_slice_bound(self, label, side: str): # GH#42855 handle date here instead of get_slice_bound if isinstance(label, dt.date) and not isinstance(label, dt.datetime): - # Pandas supports slicing with dates, treated as datetimes at midnight. + warnings.warn( + "Indexing/slicing with datetime.date is deprecated and will be removed " + "in a future version of pandas. Please convert to pd.Timestamp or " + "datetime64[ns] before indexing.", + FutureWarning, + stacklevel=2, + ) # https://github.com/pandas-dev/pandas/issues/31501 label = Timestamp(label).to_pydatetime() diff --git a/pandas/tests/frame/methods/test_asfreq.py b/pandas/tests/frame/methods/test_asfreq.py index 1c3c41e2e0299..4b60834cae53f 100644 --- a/pandas/tests/frame/methods/test_asfreq.py +++ b/pandas/tests/frame/methods/test_asfreq.py @@ -190,7 +190,7 @@ def test_asfreq_with_date_object_index(self, frame_or_series): ts = frame_or_series(np.random.default_rng(2).standard_normal(20), index=rng) ts2 = ts.copy() - ts2.index = [x.date() for x in ts2.index] + ts2.index = to_datetime([x.date() for x in ts2.index]) result = ts2.asfreq("4h", method="ffill") expected = ts.asfreq("4h", method="ffill") diff --git a/pandas/tests/indexes/datetimes/test_indexing.py b/pandas/tests/indexes/datetimes/test_indexing.py index c44345273466c..603b28ed0fb65 100644 --- a/pandas/tests/indexes/datetimes/test_indexing.py +++ b/pandas/tests/indexes/datetimes/test_indexing.py @@ -562,17 +562,22 @@ def test_get_indexer(self): idx.get_indexer(idx[[0]], method="nearest", tolerance="foo") @pytest.mark.parametrize( - "target", + "target, expected", [ - [date(2020, 1, 1), Timestamp("2020-01-02")], - [Timestamp("2020-01-01"), date(2020, 1, 2)], + ( + [date(2020, 1, 1), Timestamp("2020-01-02")], + np.array([-1, 1], dtype=np.intp), + ), + ( + [Timestamp("2020-01-01"), Timestamp(date(2020, 1, 2))], + np.array([0, 1], dtype=np.intp), + ), ], ) - def test_get_indexer_mixed_dtypes(self, target): + def test_get_indexer_mixed_dtypes(self, target, expected): # https://github.com/pandas-dev/pandas/issues/33741 values = DatetimeIndex([Timestamp("2020-01-01"), Timestamp("2020-01-02")]) result = values.get_indexer(target) - expected = np.array([0, 1], dtype=np.intp) tm.assert_numpy_array_equal(result, expected) @pytest.mark.parametrize( diff --git a/pandas/tests/series/test_arithmetic.py b/pandas/tests/series/test_arithmetic.py index 35a9742d653db..5c560b9981f85 100644 --- a/pandas/tests/series/test_arithmetic.py +++ b/pandas/tests/series/test_arithmetic.py @@ -763,7 +763,8 @@ def test_align_date_objects_with_datetimeindex(self): ts_slice = ts[5:] ts2 = ts_slice.copy() - ts2.index = [x.date() for x in ts2.index] + # Explicitly convert date objects to Timestamps for alignment + ts2.index = [pd.Timestamp(x.date()) for x in ts2.index] result = ts + ts2 result2 = ts2 + ts From 74410461a0a6dfea9f7502a49f48b6c205820b30 Mon Sep 17 00:00:00 2001 From: Aniket Singh Yadav Date: Fri, 29 Aug 2025 13:24:05 +0530 Subject: [PATCH 04/31] Revert unintended changes in datetimes.py and test_asfreq.py --- pandas/core/indexes/datetimes.py | 15 +++------------ pandas/tests/frame/methods/test_asfreq.py | 2 +- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index f7dea0a6184b4..9adbaadbdcdc8 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -2,10 +2,7 @@ import datetime as dt import operator -from typing import ( - TYPE_CHECKING, - Self, -) +from typing import TYPE_CHECKING import warnings import numpy as np @@ -58,6 +55,7 @@ DtypeObj, Frequency, IntervalClosedType, + Self, TimeAmbiguous, TimeNonexistent, npt, @@ -222,7 +220,6 @@ class DatetimeIndex(DatetimeTimedeltaMixin): to_pydatetime to_series to_frame - to_julian_date month_name day_name mean @@ -629,13 +626,7 @@ def get_loc(self, key): def _maybe_cast_slice_bound(self, label, side: str): # GH#42855 handle date here instead of get_slice_bound if isinstance(label, dt.date) and not isinstance(label, dt.datetime): - warnings.warn( - "Indexing/slicing with datetime.date is deprecated and will be removed " - "in a future version of pandas. Please convert to pd.Timestamp or " - "datetime64[ns] before indexing.", - FutureWarning, - stacklevel=2, - ) + # Pandas supports slicing with dates, treated as datetimes at midnight. # https://github.com/pandas-dev/pandas/issues/31501 label = Timestamp(label).to_pydatetime() diff --git a/pandas/tests/frame/methods/test_asfreq.py b/pandas/tests/frame/methods/test_asfreq.py index 4b60834cae53f..1c3c41e2e0299 100644 --- a/pandas/tests/frame/methods/test_asfreq.py +++ b/pandas/tests/frame/methods/test_asfreq.py @@ -190,7 +190,7 @@ def test_asfreq_with_date_object_index(self, frame_or_series): ts = frame_or_series(np.random.default_rng(2).standard_normal(20), index=rng) ts2 = ts.copy() - ts2.index = to_datetime([x.date() for x in ts2.index]) + ts2.index = [x.date() for x in ts2.index] result = ts2.asfreq("4h", method="ffill") expected = ts.asfreq("4h", method="ffill") From 149b9c4939c2be367f6db7cd08083a454a6e7889 Mon Sep 17 00:00:00 2001 From: Aniket Singh Yadav Date: Fri, 29 Aug 2025 13:38:21 +0530 Subject: [PATCH 05/31] Revert unintended changes in datetimes.py and test_asfreq.py --- pandas/tests/frame/indexing/test_indexing.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pandas/tests/frame/indexing/test_indexing.py b/pandas/tests/frame/indexing/test_indexing.py index f0510ac365957..b89904b632c33 100644 --- a/pandas/tests/frame/indexing/test_indexing.py +++ b/pandas/tests/frame/indexing/test_indexing.py @@ -1887,8 +1887,10 @@ def test_datetime_indexer_consistency_pyarrow_date32(): dti = Index(ser3) # All should be consistent assert dti.get_loc(ser[0]) == 0 - tm.assert_numpy_array_equal(dti.get_indexer(ser.values), [0]) - tm.assert_numpy_array_equal(dti.get_indexer(ser.values.astype(object)), [0]) + tm.assert_numpy_array_equal(dti.get_indexer(ser.values), np.array([0])) + tm.assert_numpy_array_equal( + dti.get_indexer(ser.values.astype(object)), np.array([0]) + ) class TestSetitemValidation: From 6c8b76a48916e0b0f9292b4b072329256c6eb922 Mon Sep 17 00:00:00 2001 From: Aniket Singh Yadav Date: Sat, 30 Aug 2025 02:35:15 +0530 Subject: [PATCH 06/31] Remove unintended changes in pandas/core/indexes/datetimes.py --- pandas/core/indexes/datetimes.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index 9adbaadbdcdc8..ce6ea1ed980dd 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -2,7 +2,10 @@ import datetime as dt import operator -from typing import TYPE_CHECKING +from typing import ( + TYPE_CHECKING, + Self, +) import warnings import numpy as np @@ -55,7 +58,6 @@ DtypeObj, Frequency, IntervalClosedType, - Self, TimeAmbiguous, TimeNonexistent, npt, @@ -220,6 +222,7 @@ class DatetimeIndex(DatetimeTimedeltaMixin): to_pydatetime to_series to_frame + to_julian_date month_name day_name mean From 0a0f582812eae3579043c955fded8bfeb2891bc0 Mon Sep 17 00:00:00 2001 From: Aniket Singh Yadav Date: Sun, 31 Aug 2025 15:00:28 +0530 Subject: [PATCH 07/31] API: Remove implicit matching of datetime.date with DatetimeIndex/Timestamp in indexing and merging tests --- pandas/tests/frame/methods/test_asfreq.py | 7 ++++--- pandas/tests/indexes/datetimes/test_indexing.py | 7 ++++--- pandas/tests/reshape/merge/test_merge.py | 8 ++++---- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/pandas/tests/frame/methods/test_asfreq.py b/pandas/tests/frame/methods/test_asfreq.py index 1c3c41e2e0299..727011ece9dc7 100644 --- a/pandas/tests/frame/methods/test_asfreq.py +++ b/pandas/tests/frame/methods/test_asfreq.py @@ -192,9 +192,10 @@ def test_asfreq_with_date_object_index(self, frame_or_series): ts2 = ts.copy() ts2.index = [x.date() for x in ts2.index] - result = ts2.asfreq("4h", method="ffill") - expected = ts.asfreq("4h", method="ffill") - tm.assert_equal(result, expected) + with pytest.raises( + TypeError, match="Cannot compare Timestamp with datetime.date" + ): + ts2.asfreq("4h", method="ffill") def test_asfreq_with_unsorted_index(self, frame_or_series): # GH#39805 diff --git a/pandas/tests/indexes/datetimes/test_indexing.py b/pandas/tests/indexes/datetimes/test_indexing.py index 603b28ed0fb65..a0d116fee35ba 100644 --- a/pandas/tests/indexes/datetimes/test_indexing.py +++ b/pandas/tests/indexes/datetimes/test_indexing.py @@ -516,9 +516,10 @@ class TestGetIndexer: def test_get_indexer_date_objs(self): rng = date_range("1/1/2000", periods=20) - result = rng.get_indexer(rng.map(lambda x: x.date())) - expected = rng.get_indexer(rng) - tm.assert_numpy_array_equal(result, expected) + with pytest.raises( + TypeError, match="Cannot compare Timestamp with datetime.date" + ): + rng.get_indexer(rng.map(lambda x: x.date())) def test_get_indexer(self): idx = date_range("2000-01-01", periods=3) diff --git a/pandas/tests/reshape/merge/test_merge.py b/pandas/tests/reshape/merge/test_merge.py index a8e29ef03acc2..f129bc8631606 100644 --- a/pandas/tests/reshape/merge/test_merge.py +++ b/pandas/tests/reshape/merge/test_merge.py @@ -2132,9 +2132,9 @@ def test_dtype_on_categorical_dates(self): expected_outer = DataFrame( [ - [pd.Timestamp("2001-01-01").date(), 1.1, 1.3], - [pd.Timestamp("2001-01-02").date(), 1.3, np.nan], - [pd.Timestamp("2001-01-03").date(), np.nan, 1.4], + [pd.Timestamp("2001-01-01"), 1.1, 1.3], + [pd.Timestamp("2001-01-02"), 1.3, np.nan], + [pd.Timestamp("2001-01-03"), np.nan, 1.4], ], columns=["date", "num2", "num4"], ) @@ -2142,7 +2142,7 @@ def test_dtype_on_categorical_dates(self): tm.assert_frame_equal(result_outer, expected_outer) expected_inner = DataFrame( - [[pd.Timestamp("2001-01-01").date(), 1.1, 1.3]], + [[pd.Timestamp("2001-01-01"), 1.1, 1.3]], columns=["date", "num2", "num4"], ) result_inner = merge(df, df2, how="inner", on=["date"]) From 30a87b4b604330a0ccb711535b9ce3f4fed6338a Mon Sep 17 00:00:00 2001 From: Aniket Singh Yadav Date: Sun, 31 Aug 2025 15:29:40 +0530 Subject: [PATCH 08/31] API: Remove implicit matching of datetime.date with DatetimeIndex/Timestamp in indexing and merging tests --- pandas/tests/indexes/datetimes/test_indexing.py | 7 +++---- pandas/tests/reshape/merge/test_merge.py | 8 ++++---- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/pandas/tests/indexes/datetimes/test_indexing.py b/pandas/tests/indexes/datetimes/test_indexing.py index a0d116fee35ba..7c1c7d5ed4816 100644 --- a/pandas/tests/indexes/datetimes/test_indexing.py +++ b/pandas/tests/indexes/datetimes/test_indexing.py @@ -516,10 +516,9 @@ class TestGetIndexer: def test_get_indexer_date_objs(self): rng = date_range("1/1/2000", periods=20) - with pytest.raises( - TypeError, match="Cannot compare Timestamp with datetime.date" - ): - rng.get_indexer(rng.map(lambda x: x.date())) + result = rng.get_indexer(rng.map(lambda x: x.date())) + expected = np.full(len(rng), -1, dtype=np.intp) + tm.assert_numpy_array_equal(result, expected) def test_get_indexer(self): idx = date_range("2000-01-01", periods=3) diff --git a/pandas/tests/reshape/merge/test_merge.py b/pandas/tests/reshape/merge/test_merge.py index f129bc8631606..dc900e8b638ca 100644 --- a/pandas/tests/reshape/merge/test_merge.py +++ b/pandas/tests/reshape/merge/test_merge.py @@ -2132,9 +2132,9 @@ def test_dtype_on_categorical_dates(self): expected_outer = DataFrame( [ - [pd.Timestamp("2001-01-01"), 1.1, 1.3], - [pd.Timestamp("2001-01-02"), 1.3, np.nan], - [pd.Timestamp("2001-01-03"), np.nan, 1.4], + [date("2001-01-01"), 1.1, 1.3], + [date("2001-01-02"), 1.3, np.nan], + [date("2001-01-03"), np.nan, 1.4], ], columns=["date", "num2", "num4"], ) @@ -2142,7 +2142,7 @@ def test_dtype_on_categorical_dates(self): tm.assert_frame_equal(result_outer, expected_outer) expected_inner = DataFrame( - [[pd.Timestamp("2001-01-01"), 1.1, 1.3]], + [[date("2001-01-01"), 1.1, 1.3]], columns=["date", "num2", "num4"], ) result_inner = merge(df, df2, how="inner", on=["date"]) From f8e4974f227592eaaf4508191c84988944a0760c Mon Sep 17 00:00:00 2001 From: Aniket Singh Yadav Date: Mon, 1 Sep 2025 10:25:32 +0530 Subject: [PATCH 09/31] API: Remove implicit matching of datetime.date with DatetimeIndex/Timestamp in indexing and merging tests --- pandas/tests/reshape/merge/test_merge.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pandas/tests/reshape/merge/test_merge.py b/pandas/tests/reshape/merge/test_merge.py index dc900e8b638ca..416805e8bfa72 100644 --- a/pandas/tests/reshape/merge/test_merge.py +++ b/pandas/tests/reshape/merge/test_merge.py @@ -2132,9 +2132,9 @@ def test_dtype_on_categorical_dates(self): expected_outer = DataFrame( [ - [date("2001-01-01"), 1.1, 1.3], - [date("2001-01-02"), 1.3, np.nan], - [date("2001-01-03"), np.nan, 1.4], + [date(2001, 1, 1), 1.1, 1.3], + [date(2001, 1, 2), 1.3, np.nan], + [date(2001, 1, 3), np.nan, 1.4], ], columns=["date", "num2", "num4"], ) @@ -2142,7 +2142,7 @@ def test_dtype_on_categorical_dates(self): tm.assert_frame_equal(result_outer, expected_outer) expected_inner = DataFrame( - [[date("2001-01-01"), 1.1, 1.3]], + [[date(2001, 1, 1), 1.1, 1.3]], columns=["date", "num2", "num4"], ) result_inner = merge(df, df2, how="inner", on=["date"]) From 610868f7cdfbb85c0630b646741593e9095a94d9 Mon Sep 17 00:00:00 2001 From: Aniket Singh Yadav Date: Thu, 11 Sep 2025 15:00:50 +0530 Subject: [PATCH 10/31] BUG: Remove special-casing for Python date objects in DatetimeIndex.get_indexer and update whatsnew note (#62158) --- doc/source/whatsnew/v3.0.0.rst | 2 ++ pandas/tests/indexes/datetimes/test_indexing.py | 1 + pandas/tests/reshape/merge/test_merge.py | 8 ++++---- pandas/tests/series/test_arithmetic.py | 3 +-- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/doc/source/whatsnew/v3.0.0.rst b/doc/source/whatsnew/v3.0.0.rst index 03ad8ed162c95..0db7870255b69 100644 --- a/doc/source/whatsnew/v3.0.0.rst +++ b/doc/source/whatsnew/v3.0.0.rst @@ -889,6 +889,8 @@ Datetimelike - Bug in constructing arrays with :class:`ArrowDtype` with ``timestamp`` type incorrectly allowing ``Decimal("NaN")`` (:issue:`61773`) - Bug in constructing arrays with a timezone-aware :class:`ArrowDtype` from timezone-naive datetime objects incorrectly treating those as UTC times instead of wall times like :class:`DatetimeTZDtype` (:issue:`61775`) - Bug in setting scalar values with mismatched resolution into arrays with non-nanosecond ``datetime64``, ``timedelta64`` or :class:`DatetimeTZDtype` incorrectly truncating those scalars (:issue:`56410`) +- Removed the special casing for sequences of Python ``date`` objects in ``DatetimeIndex.get_indexer`` and related indexing logic. + Indexing a ``DatetimeIndex`` with Python ``date`` objects now behaves consistently with other types. (:issue:`62158`) Timedelta ^^^^^^^^^ diff --git a/pandas/tests/indexes/datetimes/test_indexing.py b/pandas/tests/indexes/datetimes/test_indexing.py index 7c1c7d5ed4816..0bc5c7006cf7b 100644 --- a/pandas/tests/indexes/datetimes/test_indexing.py +++ b/pandas/tests/indexes/datetimes/test_indexing.py @@ -514,6 +514,7 @@ def test_contains_nonunique(self, vals): class TestGetIndexer: def test_get_indexer_date_objs(self): + # Behavior for get_indexer with date objects changed in GH#62158. rng = date_range("1/1/2000", periods=20) result = rng.get_indexer(rng.map(lambda x: x.date())) diff --git a/pandas/tests/reshape/merge/test_merge.py b/pandas/tests/reshape/merge/test_merge.py index 416805e8bfa72..a8e29ef03acc2 100644 --- a/pandas/tests/reshape/merge/test_merge.py +++ b/pandas/tests/reshape/merge/test_merge.py @@ -2132,9 +2132,9 @@ def test_dtype_on_categorical_dates(self): expected_outer = DataFrame( [ - [date(2001, 1, 1), 1.1, 1.3], - [date(2001, 1, 2), 1.3, np.nan], - [date(2001, 1, 3), np.nan, 1.4], + [pd.Timestamp("2001-01-01").date(), 1.1, 1.3], + [pd.Timestamp("2001-01-02").date(), 1.3, np.nan], + [pd.Timestamp("2001-01-03").date(), np.nan, 1.4], ], columns=["date", "num2", "num4"], ) @@ -2142,7 +2142,7 @@ def test_dtype_on_categorical_dates(self): tm.assert_frame_equal(result_outer, expected_outer) expected_inner = DataFrame( - [[date(2001, 1, 1), 1.1, 1.3]], + [[pd.Timestamp("2001-01-01").date(), 1.1, 1.3]], columns=["date", "num2", "num4"], ) result_inner = merge(df, df2, how="inner", on=["date"]) diff --git a/pandas/tests/series/test_arithmetic.py b/pandas/tests/series/test_arithmetic.py index 5c560b9981f85..35a9742d653db 100644 --- a/pandas/tests/series/test_arithmetic.py +++ b/pandas/tests/series/test_arithmetic.py @@ -763,8 +763,7 @@ def test_align_date_objects_with_datetimeindex(self): ts_slice = ts[5:] ts2 = ts_slice.copy() - # Explicitly convert date objects to Timestamps for alignment - ts2.index = [pd.Timestamp(x.date()) for x in ts2.index] + ts2.index = [x.date() for x in ts2.index] result = ts + ts2 result2 = ts2 + ts From 6583c57a6c3eceda8603fedad22d327edbbfdc5f Mon Sep 17 00:00:00 2001 From: Aniket Singh Yadav Date: Mon, 29 Sep 2025 09:04:37 +0530 Subject: [PATCH 11/31] BUG: Remove special-casing for Python date objects in DatetimeIndex --- pandas/tests/frame/indexing/test_indexing.py | 17 +++++++++++------ pandas/tests/series/test_arithmetic.py | 19 +++++++++++++++---- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/pandas/tests/frame/indexing/test_indexing.py b/pandas/tests/frame/indexing/test_indexing.py index b89904b632c33..2a47fa64ff8a4 100644 --- a/pandas/tests/frame/indexing/test_indexing.py +++ b/pandas/tests/frame/indexing/test_indexing.py @@ -1882,15 +1882,20 @@ def test_add_new_column_infer_string(): def test_datetime_indexer_consistency_pyarrow_date32(): # GH#62158 + pytest.importorskip("pyarrow", minversion="13.0.0") ser = Series(["2016-01-01"], dtype="date32[pyarrow]") ser3 = ser.astype("datetime64[ns]") dti = Index(ser3) - # All should be consistent - assert dti.get_loc(ser[0]) == 0 - tm.assert_numpy_array_equal(dti.get_indexer(ser.values), np.array([0])) - tm.assert_numpy_array_equal( - dti.get_indexer(ser.values.astype(object)), np.array([0]) - ) + + with pytest.raises(KeyError): + dti.get_loc(ser[0]) + + # get_indexer returns -1 for both Arrow array and object-cast + result = dti.get_indexer(ser.values) + tm.assert_numpy_array_equal(result, np.array([-1], dtype=np.intp)) + + result_obj = dti.get_indexer(ser.values.astype(object)) + tm.assert_numpy_array_equal(result_obj, np.array([-1], dtype=np.intp)) class TestSetitemValidation: diff --git a/pandas/tests/series/test_arithmetic.py b/pandas/tests/series/test_arithmetic.py index 35a9742d653db..5eb373fffba18 100644 --- a/pandas/tests/series/test_arithmetic.py +++ b/pandas/tests/series/test_arithmetic.py @@ -767,10 +767,21 @@ def test_align_date_objects_with_datetimeindex(self): result = ts + ts2 result2 = ts2 + ts - expected = ts + ts[5:] - expected.index = expected.index._with_freq(None) - tm.assert_series_equal(result, expected) - tm.assert_series_equal(result2, expected) + + date_labels = [x.date() for x in rng[5:]] + expected_index = Index(list(rng) + date_labels, dtype=object) + + # Length and index checks + assert len(result) == 35 + tm.assert_index_equal(result.index, expected_index) + tm.assert_index_equal(result2.index, expected_index) + assert result.index.dtype == object + + # All NaN because there are no matching labels now + assert result.isna().all() + assert result2.isna().all() + + tm.assert_series_equal(result, result2) class TestNamePreservation: From 1c59ae7e106d06c6f5a0d48facfd9124d9b8fddf Mon Sep 17 00:00:00 2001 From: Aniket Singh Yadav Date: Mon, 29 Sep 2025 09:37:28 +0530 Subject: [PATCH 12/31] BUG: Remove special-casing for Python date objects in DatetimeIndex --- pandas/tests/series/test_arithmetic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/series/test_arithmetic.py b/pandas/tests/series/test_arithmetic.py index 5eb373fffba18..d56ca10b7debb 100644 --- a/pandas/tests/series/test_arithmetic.py +++ b/pandas/tests/series/test_arithmetic.py @@ -769,7 +769,7 @@ def test_align_date_objects_with_datetimeindex(self): result2 = ts2 + ts date_labels = [x.date() for x in rng[5:]] - expected_index = Index(list(rng) + date_labels, dtype=object) + expected_index = Index(date_labels + list(rng), dtype=object) # Length and index checks assert len(result) == 35 From 19d0357651785ae3b2db03c9c2444ffdea532d35 Mon Sep 17 00:00:00 2001 From: Aniket Singh Yadav Date: Fri, 17 Oct 2025 22:01:46 +0530 Subject: [PATCH 13/31] BUG: Remove special-casing for Python date objects in DatetimeIndex --- pandas/tests/frame/indexing/test_indexing.py | 5 +++++ pandas/tests/series/test_arithmetic.py | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/pandas/tests/frame/indexing/test_indexing.py b/pandas/tests/frame/indexing/test_indexing.py index 2a47fa64ff8a4..c30bf1f68752a 100644 --- a/pandas/tests/frame/indexing/test_indexing.py +++ b/pandas/tests/frame/indexing/test_indexing.py @@ -7,6 +7,7 @@ import re import numpy as np +import pyarrow as pa import pytest from pandas._libs import iNaT @@ -1887,6 +1888,10 @@ def test_datetime_indexer_consistency_pyarrow_date32(): ser3 = ser.astype("datetime64[ns]") dti = Index(ser3) + # Make sure we don't treat Arrow date as timestamp + dtype = ser.dtype.arrow_dtype + assert not (dtype.kind == "M" and not pa.types.is_date(dtype)) + with pytest.raises(KeyError): dti.get_loc(ser[0]) diff --git a/pandas/tests/series/test_arithmetic.py b/pandas/tests/series/test_arithmetic.py index d56ca10b7debb..a7f0c1099da6d 100644 --- a/pandas/tests/series/test_arithmetic.py +++ b/pandas/tests/series/test_arithmetic.py @@ -758,6 +758,8 @@ def test_datetime_understood(self, unit): tm.assert_series_equal(result, expected) def test_align_date_objects_with_datetimeindex(self): + # GH#62158: v3.0.0 - DatetimeIndex no longer matches Python date labels. + # The result is always all-NaN and the union index. rng = date_range("1/1/2000", periods=20) ts = Series(np.random.default_rng(2).standard_normal(20), index=rng) @@ -769,7 +771,7 @@ def test_align_date_objects_with_datetimeindex(self): result2 = ts2 + ts date_labels = [x.date() for x in rng[5:]] - expected_index = Index(date_labels + list(rng), dtype=object) + expected_index = Index(list(rng) + date_labels, dtype=object) # Length and index checks assert len(result) == 35 From ea62b6814caa21d6e466c6b0d64b78a0e6a7b316 Mon Sep 17 00:00:00 2001 From: Aniket Singh Yadav Date: Sat, 18 Oct 2025 08:39:14 +0530 Subject: [PATCH 14/31] BUG: Remove special-casing for Python date objects in DatetimeIndex --- pandas/core/indexes/base.py | 10 ++++++++++ pandas/tests/series/test_arithmetic.py | 7 ++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 0426949c9e1a5..229221fc501be 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -1,6 +1,7 @@ from __future__ import annotations from collections import abc +import datetime as dt from datetime import datetime import functools from itertools import zip_longest @@ -177,6 +178,7 @@ disallow_ndim_indexing, is_valid_positional_slice, ) +from pandas.core.indexes.datetimes import DatetimeIndex from pandas.core.indexes.frozen import FrozenList from pandas.core.missing import clean_reindex_fill_method from pandas.core.ops import get_op_result_name @@ -3668,6 +3670,14 @@ def get_indexer( Notice that the return value is an array of locations in ``index`` and ``x`` is marked by -1, as it is not in ``index``. """ + if isinstance(self, DatetimeIndex): + if hasattr(target, "__iter__") and not isinstance(target, (str, bytes)): + seq = list(target) + if seq and all( + isinstance(x, dt.date) and not isinstance(x, dt.datetime) + for x in seq + ): + return np.full(len(seq), -1, dtype=np.intp) method = clean_reindex_fill_method(method) orig_target = target target = self._maybe_cast_listlike_indexer(target) diff --git a/pandas/tests/series/test_arithmetic.py b/pandas/tests/series/test_arithmetic.py index a7f0c1099da6d..81db940cd1236 100644 --- a/pandas/tests/series/test_arithmetic.py +++ b/pandas/tests/series/test_arithmetic.py @@ -771,12 +771,13 @@ def test_align_date_objects_with_datetimeindex(self): result2 = ts2 + ts date_labels = [x.date() for x in rng[5:]] - expected_index = Index(list(rng) + date_labels, dtype=object) + expected_index_result = Index(list(rng) + date_labels, dtype=object) + expected_index_result2 = Index(date_labels + list(rng), dtype=object) # Length and index checks assert len(result) == 35 - tm.assert_index_equal(result.index, expected_index) - tm.assert_index_equal(result2.index, expected_index) + tm.assert_index_equal(result.index, expected_index_result) + tm.assert_index_equal(result2.index, expected_index_result2) assert result.index.dtype == object # All NaN because there are no matching labels now From d03cb23d71f422ddbc18da7d935d6b20750b0fd8 Mon Sep 17 00:00:00 2001 From: Aniket Singh Yadav Date: Sat, 18 Oct 2025 08:50:29 +0530 Subject: [PATCH 15/31] BUG: Remove special-casing for Python date objects in DatetimeIndex --- pandas/core/indexes/base.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 229221fc501be..0426949c9e1a5 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -1,7 +1,6 @@ from __future__ import annotations from collections import abc -import datetime as dt from datetime import datetime import functools from itertools import zip_longest @@ -178,7 +177,6 @@ disallow_ndim_indexing, is_valid_positional_slice, ) -from pandas.core.indexes.datetimes import DatetimeIndex from pandas.core.indexes.frozen import FrozenList from pandas.core.missing import clean_reindex_fill_method from pandas.core.ops import get_op_result_name @@ -3670,14 +3668,6 @@ def get_indexer( Notice that the return value is an array of locations in ``index`` and ``x`` is marked by -1, as it is not in ``index``. """ - if isinstance(self, DatetimeIndex): - if hasattr(target, "__iter__") and not isinstance(target, (str, bytes)): - seq = list(target) - if seq and all( - isinstance(x, dt.date) and not isinstance(x, dt.datetime) - for x in seq - ): - return np.full(len(seq), -1, dtype=np.intp) method = clean_reindex_fill_method(method) orig_target = target target = self._maybe_cast_listlike_indexer(target) From dd0278c3e830bb4fefc5927bfcedf7b68eddb458 Mon Sep 17 00:00:00 2001 From: Aniket Singh Yadav Date: Sun, 19 Oct 2025 09:03:12 +0530 Subject: [PATCH 16/31] BUG: Remove special-casing for Python date objects in DatetimeIndex --- pandas/tests/frame/indexing/test_indexing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/frame/indexing/test_indexing.py b/pandas/tests/frame/indexing/test_indexing.py index c30bf1f68752a..35395422d1391 100644 --- a/pandas/tests/frame/indexing/test_indexing.py +++ b/pandas/tests/frame/indexing/test_indexing.py @@ -1889,7 +1889,7 @@ def test_datetime_indexer_consistency_pyarrow_date32(): dti = Index(ser3) # Make sure we don't treat Arrow date as timestamp - dtype = ser.dtype.arrow_dtype + dtype = ser.dtype.pyarrow_dtype assert not (dtype.kind == "M" and not pa.types.is_date(dtype)) with pytest.raises(KeyError): From 7371adcd7e8a3388f48dd900251bec9161288983 Mon Sep 17 00:00:00 2001 From: Aniket Singh Yadav Date: Sun, 19 Oct 2025 09:47:22 +0530 Subject: [PATCH 17/31] BUG: Remove special-casing for Python date objects in DatetimeIndex --- pandas/tests/frame/indexing/test_indexing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/frame/indexing/test_indexing.py b/pandas/tests/frame/indexing/test_indexing.py index 35395422d1391..87f469b37cdb5 100644 --- a/pandas/tests/frame/indexing/test_indexing.py +++ b/pandas/tests/frame/indexing/test_indexing.py @@ -1890,7 +1890,7 @@ def test_datetime_indexer_consistency_pyarrow_date32(): # Make sure we don't treat Arrow date as timestamp dtype = ser.dtype.pyarrow_dtype - assert not (dtype.kind == "M" and not pa.types.is_date(dtype)) + assert not (pa.types.is_timestamp(dtype) and not pa.types.is_date(dtype)) with pytest.raises(KeyError): dti.get_loc(ser[0]) From 5df34b526e5c4a385e189dc03adf8e6d9f7d39bc Mon Sep 17 00:00:00 2001 From: Aniket Singh Yadav Date: Sun, 19 Oct 2025 10:01:09 +0530 Subject: [PATCH 18/31] BUG: Remove special-casing for Python date objects in DatetimeIndex --- pandas/tests/frame/indexing/test_indexing.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pandas/tests/frame/indexing/test_indexing.py b/pandas/tests/frame/indexing/test_indexing.py index 87f469b37cdb5..3545c6a23ee6c 100644 --- a/pandas/tests/frame/indexing/test_indexing.py +++ b/pandas/tests/frame/indexing/test_indexing.py @@ -7,7 +7,6 @@ import re import numpy as np -import pyarrow as pa import pytest from pandas._libs import iNaT @@ -1884,6 +1883,8 @@ def test_add_new_column_infer_string(): def test_datetime_indexer_consistency_pyarrow_date32(): # GH#62158 pytest.importorskip("pyarrow", minversion="13.0.0") + import pyarrow as pa + ser = Series(["2016-01-01"], dtype="date32[pyarrow]") ser3 = ser.astype("datetime64[ns]") dti = Index(ser3) From 598f762fc0e9390ce43a9f1a86e32da4f33005cf Mon Sep 17 00:00:00 2001 From: Aniket Singh Yadav Date: Fri, 24 Oct 2025 17:04:38 +0530 Subject: [PATCH 19/31] BUG: Remove special-casing for Python date objects in DatetimeIndex --- pandas/tests/series/test_arithmetic.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pandas/tests/series/test_arithmetic.py b/pandas/tests/series/test_arithmetic.py index 81db940cd1236..ec31abdaf1711 100644 --- a/pandas/tests/series/test_arithmetic.py +++ b/pandas/tests/series/test_arithmetic.py @@ -784,8 +784,6 @@ def test_align_date_objects_with_datetimeindex(self): assert result.isna().all() assert result2.isna().all() - tm.assert_series_equal(result, result2) - class TestNamePreservation: @pytest.mark.parametrize("box", [list, tuple, np.array, Index, Series, pd.array]) From f60be17d9c62cc2e3ff0037b93cdc10d5cea1c4f Mon Sep 17 00:00:00 2001 From: Aniket Singh Yadav Date: Fri, 24 Oct 2025 18:02:13 +0530 Subject: [PATCH 20/31] BUG: Remove special-casing for Python date objects in DatetimeIndex --- pandas/core/indexes/datetimes.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index ce6ea1ed980dd..299c22b8934c7 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -42,6 +42,7 @@ tz_to_dtype, ) import pandas.core.common as com +from pandas.core.indexes.api import ensure_index from pandas.core.indexes.base import ( Index, maybe_extract_name, @@ -698,6 +699,17 @@ def check_str_or_none(point) -> bool: else: return indexer + def get_indexer(self, target, method=None, limit=None, tolerance=None): + # Ensure Python `date` objects never match DatetimeIndex elements (GH#62158) + tgt = ensure_index(target) + if ( + method is None + and tolerance is None + and getattr(tgt, "inferred_type", None) == "date" + ): + return np.full(len(tgt), -1, dtype=np.intp) + return super().get_indexer(tgt, method=method, limit=limit, tolerance=tolerance) + # -------------------------------------------------------------------- @property From be5528ea3e3fbd7bb9ae0a50c08051b97173dfc4 Mon Sep 17 00:00:00 2001 From: Aniket Singh Yadav Date: Fri, 24 Oct 2025 18:31:07 +0530 Subject: [PATCH 21/31] BUG: Remove special-casing for Python date objects in DatetimeIndex --- pandas/tests/indexes/datetimes/test_indexing.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pandas/tests/indexes/datetimes/test_indexing.py b/pandas/tests/indexes/datetimes/test_indexing.py index 0bc5c7006cf7b..6e391cfaaf9da 100644 --- a/pandas/tests/indexes/datetimes/test_indexing.py +++ b/pandas/tests/indexes/datetimes/test_indexing.py @@ -567,11 +567,11 @@ def test_get_indexer(self): [ ( [date(2020, 1, 1), Timestamp("2020-01-02")], - np.array([-1, 1], dtype=np.intp), + np.array([-1, -1], dtype=np.intp), ), ( [Timestamp("2020-01-01"), Timestamp(date(2020, 1, 2))], - np.array([0, 1], dtype=np.intp), + np.array([0, -1], dtype=np.intp), ), ], ) @@ -584,7 +584,7 @@ def test_get_indexer_mixed_dtypes(self, target, expected): @pytest.mark.parametrize( "target, positions", [ - ([date(9999, 1, 1), Timestamp("2020-01-01")], [-1, 0]), + ([date(9999, 1, 1), Timestamp("2020-01-01")], [-1, -1]), ([Timestamp("2020-01-01"), date(9999, 1, 1)], [0, -1]), ([date(9999, 1, 1), date(9999, 1, 1)], [-1, -1]), ], From 76c93e468d78ba77503ed66996a7e11d0ed1c2e9 Mon Sep 17 00:00:00 2001 From: Aniket Singh Yadav Date: Fri, 24 Oct 2025 18:50:57 +0530 Subject: [PATCH 22/31] BUG: Remove special-casing for Python date objects in DatetimeIndex --- pandas/tests/indexes/datetimes/test_indexing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/indexes/datetimes/test_indexing.py b/pandas/tests/indexes/datetimes/test_indexing.py index 6e391cfaaf9da..f303cadfa0111 100644 --- a/pandas/tests/indexes/datetimes/test_indexing.py +++ b/pandas/tests/indexes/datetimes/test_indexing.py @@ -571,7 +571,7 @@ def test_get_indexer(self): ), ( [Timestamp("2020-01-01"), Timestamp(date(2020, 1, 2))], - np.array([0, -1], dtype=np.intp), + np.array([0, 1], dtype=np.intp), ), ], ) From 0c7eb2d72357f3b024df6200df7f396a8812ee7c Mon Sep 17 00:00:00 2001 From: Aniket Singh Yadav Date: Fri, 24 Oct 2025 19:14:26 +0530 Subject: [PATCH 23/31] BUG: Remove special-casing for Python date objects in DatetimeIndex --- pandas/core/indexes/datetimes.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index 299c22b8934c7..24922b33ad5a4 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -702,12 +702,16 @@ def check_str_or_none(point) -> bool: def get_indexer(self, target, method=None, limit=None, tolerance=None): # Ensure Python `date` objects never match DatetimeIndex elements (GH#62158) tgt = ensure_index(target) - if ( - method is None - and tolerance is None - and getattr(tgt, "inferred_type", None) == "date" - ): - return np.full(len(tgt), -1, dtype=np.intp) + if method is None and tolerance is None and tgt.dtype == object: + result = super().get_indexer( + tgt, method=method, limit=limit, tolerance=tolerance + ) + mask = [ + isinstance(x, dt.date) and not isinstance(x, dt.datetime) for x in tgt + ] + result = np.array(result) + result[mask] = -1 + return result return super().get_indexer(tgt, method=method, limit=limit, tolerance=tolerance) # -------------------------------------------------------------------- From 0d8182477cb8e1269b97df38cc2681345153b09b Mon Sep 17 00:00:00 2001 From: Aniket Singh Yadav Date: Fri, 24 Oct 2025 19:33:15 +0530 Subject: [PATCH 24/31] BUG: Remove special-casing for Python date objects in DatetimeIndex --- pandas/tests/indexes/datetimes/test_indexing.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/tests/indexes/datetimes/test_indexing.py b/pandas/tests/indexes/datetimes/test_indexing.py index f303cadfa0111..0bc5c7006cf7b 100644 --- a/pandas/tests/indexes/datetimes/test_indexing.py +++ b/pandas/tests/indexes/datetimes/test_indexing.py @@ -567,7 +567,7 @@ def test_get_indexer(self): [ ( [date(2020, 1, 1), Timestamp("2020-01-02")], - np.array([-1, -1], dtype=np.intp), + np.array([-1, 1], dtype=np.intp), ), ( [Timestamp("2020-01-01"), Timestamp(date(2020, 1, 2))], @@ -584,7 +584,7 @@ def test_get_indexer_mixed_dtypes(self, target, expected): @pytest.mark.parametrize( "target, positions", [ - ([date(9999, 1, 1), Timestamp("2020-01-01")], [-1, -1]), + ([date(9999, 1, 1), Timestamp("2020-01-01")], [-1, 0]), ([Timestamp("2020-01-01"), date(9999, 1, 1)], [0, -1]), ([date(9999, 1, 1), date(9999, 1, 1)], [-1, -1]), ], From 8f39dbc86c22b53412854c92b2e5f6d690df7f1e Mon Sep 17 00:00:00 2001 From: Aniket Singh Yadav Date: Wed, 29 Oct 2025 15:04:13 +0530 Subject: [PATCH 25/31] BUG: Remove special-casing for Python date objects in DatetimeIndex --- pandas/core/indexes/base.py | 14 ++++++++++++++ pandas/core/indexes/datetimes.py | 16 ---------------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 0426949c9e1a5..9c953f091b1d9 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -19,6 +19,7 @@ import warnings import numpy as np +import pyarrow as pa from pandas._config import ( get_option, @@ -6303,6 +6304,19 @@ def _is_comparable_dtype(self, dtype: DtypeObj) -> bool: return dtype.kind == "b" elif is_numeric_dtype(self.dtype): return is_numeric_dtype(dtype) + # GH#62158 + elif isinstance(dtype, ArrowDtype): + if dtype.kind != "M": + return False + pa_dtype = dtype.pyarrow_dtype + if pa.types.is_date(pa_dtype): + return False + if pa.types.is_timestamp(pa_dtype): + if (getattr(pa_dtype, "tz", None) is None) ^ ( + getattr(self, "tz", None) is None + ): + return False + return True # TODO: this was written assuming we only get here with object-dtype, # which is no longer correct. Can we specialize for EA? return True diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index 24922b33ad5a4..ce6ea1ed980dd 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -42,7 +42,6 @@ tz_to_dtype, ) import pandas.core.common as com -from pandas.core.indexes.api import ensure_index from pandas.core.indexes.base import ( Index, maybe_extract_name, @@ -699,21 +698,6 @@ def check_str_or_none(point) -> bool: else: return indexer - def get_indexer(self, target, method=None, limit=None, tolerance=None): - # Ensure Python `date` objects never match DatetimeIndex elements (GH#62158) - tgt = ensure_index(target) - if method is None and tolerance is None and tgt.dtype == object: - result = super().get_indexer( - tgt, method=method, limit=limit, tolerance=tolerance - ) - mask = [ - isinstance(x, dt.date) and not isinstance(x, dt.datetime) for x in tgt - ] - result = np.array(result) - result[mask] = -1 - return result - return super().get_indexer(tgt, method=method, limit=limit, tolerance=tolerance) - # -------------------------------------------------------------------- @property From 2e6764c9f3f8482da644749e2cc8fabd6ffa6898 Mon Sep 17 00:00:00 2001 From: Aniket Singh Yadav Date: Wed, 29 Oct 2025 15:11:36 +0530 Subject: [PATCH 26/31] BUG: Remove special-casing for Python date objects in DatetimeIndex --- pandas/core/indexes/base.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 9c953f091b1d9..51f7114fbddbe 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -19,7 +19,6 @@ import warnings import numpy as np -import pyarrow as pa from pandas._config import ( get_option, @@ -6306,6 +6305,8 @@ def _is_comparable_dtype(self, dtype: DtypeObj) -> bool: return is_numeric_dtype(dtype) # GH#62158 elif isinstance(dtype, ArrowDtype): + import pyarrow as pa + if dtype.kind != "M": return False pa_dtype = dtype.pyarrow_dtype From 8ff37c6ff103e221b4bee7ad63f137d4ed19fe15 Mon Sep 17 00:00:00 2001 From: Aniket Singh Yadav Date: Thu, 30 Oct 2025 00:04:17 +0530 Subject: [PATCH 27/31] BUG: Remove special-casing for Python date objects in DatetimeIndex --- pandas/core/indexes/base.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 51f7114fbddbe..7f668f1869551 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -6307,15 +6307,13 @@ def _is_comparable_dtype(self, dtype: DtypeObj) -> bool: elif isinstance(dtype, ArrowDtype): import pyarrow as pa - if dtype.kind != "M": + if self.dtype.kind != "M" or dtype.kind != "M": return False pa_dtype = dtype.pyarrow_dtype if pa.types.is_date(pa_dtype): return False if pa.types.is_timestamp(pa_dtype): - if (getattr(pa_dtype, "tz", None) is None) ^ ( - getattr(self, "tz", None) is None - ): + if (pa_dtype.tz is None) ^ (getattr(self, "tz", None) is None): return False return True # TODO: this was written assuming we only get here with object-dtype, From 48595e19999e788c8006ecd356e78e91ec7897dc Mon Sep 17 00:00:00 2001 From: Aniket Singh Yadav Date: Thu, 30 Oct 2025 08:39:21 +0530 Subject: [PATCH 28/31] BUG: Remove special-casing for Python date objects in DatetimeIndex --- pandas/core/indexes/base.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 7f668f1869551..cf342539d1338 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -6304,11 +6304,11 @@ def _is_comparable_dtype(self, dtype: DtypeObj) -> bool: elif is_numeric_dtype(self.dtype): return is_numeric_dtype(dtype) # GH#62158 + elif self.dtype.kind == "M" and dtype == object: + return False elif isinstance(dtype, ArrowDtype): import pyarrow as pa - if self.dtype.kind != "M" or dtype.kind != "M": - return False pa_dtype = dtype.pyarrow_dtype if pa.types.is_date(pa_dtype): return False From c4ff5e2edb6444a0cffff6a89046818eb8fc8355 Mon Sep 17 00:00:00 2001 From: Aniket Singh Yadav Date: Thu, 30 Oct 2025 10:12:52 +0530 Subject: [PATCH 29/31] BUG: Remove special-casing for Python date objects in DatetimeIndex --- pandas/core/indexes/base.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index cf342539d1338..57ab838f19474 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -6310,6 +6310,8 @@ def _is_comparable_dtype(self, dtype: DtypeObj) -> bool: import pyarrow as pa pa_dtype = dtype.pyarrow_dtype + if self.dtype.kind != "M" or dtype.kind != "M": + return False if pa.types.is_date(pa_dtype): return False if pa.types.is_timestamp(pa_dtype): From 0f7f3b8de57fe9525593165892a76bb3edb3e674 Mon Sep 17 00:00:00 2001 From: Aniket Singh Yadav Date: Sat, 8 Nov 2025 23:28:31 +0530 Subject: [PATCH 30/31] BUG: Remove special-casing for Python date objects in DatetimeIndex --- pandas/core/indexes/base.py | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 57ab838f19474..6df5059f603e5 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -1,7 +1,10 @@ from __future__ import annotations from collections import abc -from datetime import datetime +from datetime import ( + date, + datetime, +) import functools from itertools import zip_longest import operator @@ -6288,6 +6291,18 @@ def _should_compare(self, other: Index) -> bool: # respectively. return False + # GH#62158 + if ( + self.dtype.kind == "M" + and isinstance(other, Index) + and other.dtype == _dtype_obj + and all( + isinstance(x, date) and not isinstance(x, datetime) + for x in other._values + ) + ): + return False + dtype = _unpack_nested_dtype(other) return ( self._is_comparable_dtype(dtype) @@ -6304,13 +6319,21 @@ def _is_comparable_dtype(self, dtype: DtypeObj) -> bool: elif is_numeric_dtype(self.dtype): return is_numeric_dtype(dtype) # GH#62158 - elif self.dtype.kind == "M" and dtype == object: - return False elif isinstance(dtype, ArrowDtype): import pyarrow as pa pa_dtype = dtype.pyarrow_dtype - if self.dtype.kind != "M" or dtype.kind != "M": + if dtype.kind != "M": + if self.dtype.kind == "b": + return dtype.kind == "b" + if is_numeric_dtype(self.dtype): + return pa.types.is_integer(pa_dtype) or pa.types.is_floating( + pa_dtype + ) + if self.dtype.kind == "m" and pa.types.is_duration(pa_dtype): + return True + return False + if self.dtype.kind != "M": return False if pa.types.is_date(pa_dtype): return False From 3b38f8d3a0630b8f27d7cf1df9626738c260618d Mon Sep 17 00:00:00 2001 From: Aniket Singh Yadav Date: Tue, 11 Nov 2025 19:23:13 +0530 Subject: [PATCH 31/31] BUG: Remove special-casing for Python date objects in DatetimeIndex --- pandas/core/indexes/base.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 6df5059f603e5..de54ae81641cf 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -2,7 +2,6 @@ from collections import abc from datetime import ( - date, datetime, ) import functools @@ -6291,18 +6290,6 @@ def _should_compare(self, other: Index) -> bool: # respectively. return False - # GH#62158 - if ( - self.dtype.kind == "M" - and isinstance(other, Index) - and other.dtype == _dtype_obj - and all( - isinstance(x, date) and not isinstance(x, datetime) - for x in other._values - ) - ): - return False - dtype = _unpack_nested_dtype(other) return ( self._is_comparable_dtype(dtype)