From 87c158eec2b515a362dc58ea1c0b155380c35870 Mon Sep 17 00:00:00 2001 From: Antriksh006 Date: Tue, 23 Dec 2025 03:44:57 +0530 Subject: [PATCH 1/7] comparing time series with index of different units --- pandas/core/indexes/datetimelike.py | 16 +++++++++++++++- pandas/tests/indexes/datetimes/test_setops.py | 2 +- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/pandas/core/indexes/datetimelike.py b/pandas/core/indexes/datetimelike.py index fd061666c1f00..b5698b8159d39 100644 --- a/pandas/core/indexes/datetimelike.py +++ b/pandas/core/indexes/datetimelike.py @@ -245,8 +245,22 @@ def equals(self, other: Any) -> bool: if not isinstance(other, Index): return False - elif other.dtype.kind in "iufc": + + if len(self) != len(other): + return False + + self_unit = getattr(self, "unit", None) + other_unit = getattr(other, "unit", None) + + if self_unit and other_unit and self_unit != other_unit: + try: + # Match the units and check ity + return self._values.equals(other._values.as_unit(self_unit)) + except (ValueError, TypeError, AttributeError): + return False + if other.dtype.kind in "iufc": return False + elif not isinstance(other, type(self)): should_try = False inferable = self._data._infer_matches diff --git a/pandas/tests/indexes/datetimes/test_setops.py b/pandas/tests/indexes/datetimes/test_setops.py index 397aab80531f0..19d537639311f 100644 --- a/pandas/tests/indexes/datetimes/test_setops.py +++ b/pandas/tests/indexes/datetimes/test_setops.py @@ -246,7 +246,7 @@ def test_intersection_same_timezone_different_units(self): # Test intersection result = idx1.intersection(idx2) expected = date_range("2000-01-01", periods=3, tz=tz).as_unit("ns") - tm.assert_index_equal(result, expected) + tm.assert_index_equal(result, expected,exact=False) def test_symmetric_difference_same_timezone_different_units(self): # GH 60080 - fix timezone being changed to UTC when units differ From 9d21e7c58b0f6d4e79fea1df397de64bb1d4a6db Mon Sep 17 00:00:00 2001 From: Antriksh006 Date: Tue, 23 Dec 2025 13:01:30 +0530 Subject: [PATCH 2/7] . --- pandas/core/indexes/datetimelike.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/indexes/datetimelike.py b/pandas/core/indexes/datetimelike.py index b5698b8159d39..98fb22588a5ef 100644 --- a/pandas/core/indexes/datetimelike.py +++ b/pandas/core/indexes/datetimelike.py @@ -246,7 +246,7 @@ def equals(self, other: Any) -> bool: if not isinstance(other, Index): return False - if len(self) != len(other): + if len(self)!=len(other): return False self_unit = getattr(self, "unit", None) From cc2668bc37f490455791815388292477d22f9815 Mon Sep 17 00:00:00 2001 From: Antriksh006 Date: Tue, 23 Dec 2025 13:55:50 +0530 Subject: [PATCH 3/7] . --- pandas/core/indexes/datetimelike.py | 4 ++-- pandas/tests/indexes/datetimes/test_setops.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pandas/core/indexes/datetimelike.py b/pandas/core/indexes/datetimelike.py index 98fb22588a5ef..23870d31746ca 100644 --- a/pandas/core/indexes/datetimelike.py +++ b/pandas/core/indexes/datetimelike.py @@ -246,7 +246,7 @@ def equals(self, other: Any) -> bool: if not isinstance(other, Index): return False - if len(self)!=len(other): + if len(self) != len(other): return False self_unit = getattr(self, "unit", None) @@ -260,7 +260,7 @@ def equals(self, other: Any) -> bool: return False if other.dtype.kind in "iufc": return False - + elif not isinstance(other, type(self)): should_try = False inferable = self._data._infer_matches diff --git a/pandas/tests/indexes/datetimes/test_setops.py b/pandas/tests/indexes/datetimes/test_setops.py index 19d537639311f..32dbacccbd251 100644 --- a/pandas/tests/indexes/datetimes/test_setops.py +++ b/pandas/tests/indexes/datetimes/test_setops.py @@ -246,7 +246,7 @@ def test_intersection_same_timezone_different_units(self): # Test intersection result = idx1.intersection(idx2) expected = date_range("2000-01-01", periods=3, tz=tz).as_unit("ns") - tm.assert_index_equal(result, expected,exact=False) + tm.assert_index_equal(result, expected, exact=False) def test_symmetric_difference_same_timezone_different_units(self): # GH 60080 - fix timezone being changed to UTC when units differ From fd19e02949b8ddce3d06f8e05f00c05c329482a2 Mon Sep 17 00:00:00 2001 From: Antriksh006 Date: Tue, 23 Dec 2025 20:01:41 +0530 Subject: [PATCH 4/7] fix Datetimelike.equals unit mismatch and typing errors --- pandas/core/indexes/datetimelike.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/pandas/core/indexes/datetimelike.py b/pandas/core/indexes/datetimelike.py index 23870d31746ca..5a48401c6d4a0 100644 --- a/pandas/core/indexes/datetimelike.py +++ b/pandas/core/indexes/datetimelike.py @@ -252,12 +252,14 @@ def equals(self, other: Any) -> bool: self_unit = getattr(self, "unit", None) other_unit = getattr(other, "unit", None) - if self_unit and other_unit and self_unit != other_unit: - try: - # Match the units and check ity - return self._values.equals(other._values.as_unit(self_unit)) - except (ValueError, TypeError, AttributeError): - return False + if self_unit is not None and other_unit is not None and self_unit != other_unit: + if getattr(self.dtype, "tz", None) == getattr(other.dtype, "tz", None): + try: + other_values = other._values + if hasattr(other_values, "as_unit") and hasattr(self._values, "equals"): + return self._values.equals(other_values.as_unit(self_unit)) + except (ValueError, TypeError, AttributeError): + return False if other.dtype.kind in "iufc": return False @@ -281,7 +283,6 @@ def equals(self, other: Any) -> bool: return False if self.dtype != other.dtype: - # have different timezone return False return np.array_equal(self.asi8, other.asi8) From 7566471bd4c710ac0dfa3fd19ef28fd6ef9ca1c1 Mon Sep 17 00:00:00 2001 From: Antriksh006 Date: Tue, 23 Dec 2025 20:05:12 +0530 Subject: [PATCH 5/7] . --- pandas/core/indexes/datetimelike.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pandas/core/indexes/datetimelike.py b/pandas/core/indexes/datetimelike.py index 5a48401c6d4a0..aedfe130d5123 100644 --- a/pandas/core/indexes/datetimelike.py +++ b/pandas/core/indexes/datetimelike.py @@ -256,7 +256,9 @@ def equals(self, other: Any) -> bool: if getattr(self.dtype, "tz", None) == getattr(other.dtype, "tz", None): try: other_values = other._values - if hasattr(other_values, "as_unit") and hasattr(self._values, "equals"): + if hasattr(other_values, "as_unit") and hasattr( + self._values, "equals" + ): return self._values.equals(other_values.as_unit(self_unit)) except (ValueError, TypeError, AttributeError): return False From cb1e31f4cb8f1deb9fca90cf3b99614f20a8af06 Mon Sep 17 00:00:00 2001 From: Antriksh006 Date: Wed, 24 Dec 2025 22:15:07 +0530 Subject: [PATCH 6/7] added some tests --- .../indexes/datetimelike_/test_equals.py | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/pandas/tests/indexes/datetimelike_/test_equals.py b/pandas/tests/indexes/datetimelike_/test_equals.py index df9182b2dd1c2..f7976f83d7a78 100644 --- a/pandas/tests/indexes/datetimelike_/test_equals.py +++ b/pandas/tests/indexes/datetimelike_/test_equals.py @@ -135,6 +135,19 @@ def test_equals2(self): assert not idx.equals(oob2) assert not idx2.equals(oob2) assert not idx3.equals(oob2) + + def test_equals_different_units(self): + # GH#63459 + idx1 = date_range("2013-01-01", periods=5).as_unit("ns") + idx2 = idx1.as_unit("us") + + assert idx1.equals(idx2) is True + assert idx2.equals(idx1) is True + + # Test with mismatching values but same units to ensure we don't + # get false positives + idx3 = date_range("2013-01-02", periods=5).as_unit("us") + assert not idx1.equals(idx3) @pytest.mark.parametrize("freq", ["B", "C"]) def test_not_equals_bday(self, freq): @@ -183,3 +196,16 @@ def test_equals2(self): assert (oob3 == oob).all() assert not idx.equals(oob3) assert not idx2.equals(oob3) + + def test_equals_different_units(self): + # GH#63459 + idx1 = timedelta_range("1 day", periods=10).as_unit("ns") + idx2 = idx1.as_unit("us") + + assert idx1.equals(idx2) is True + assert idx2.equals(idx1) is True + + # Ensure that NaT equality still holds across units + idx3 = TimedeltaIndex(["1 days", "NaT"]).as_unit("ns") + idx4 = TimedeltaIndex(["1 days", "NaT"]).as_unit("us") + assert idx3.equals(idx4) is True From 7459fd029d157d9d009fe5d2276679b87323810c Mon Sep 17 00:00:00 2001 From: Antriksh006 Date: Wed, 24 Dec 2025 22:21:32 +0530 Subject: [PATCH 7/7] . --- pandas/tests/indexes/datetimelike_/test_equals.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pandas/tests/indexes/datetimelike_/test_equals.py b/pandas/tests/indexes/datetimelike_/test_equals.py index f7976f83d7a78..18ed45b8662c3 100644 --- a/pandas/tests/indexes/datetimelike_/test_equals.py +++ b/pandas/tests/indexes/datetimelike_/test_equals.py @@ -135,16 +135,16 @@ def test_equals2(self): assert not idx.equals(oob2) assert not idx2.equals(oob2) assert not idx3.equals(oob2) - + def test_equals_different_units(self): # GH#63459 idx1 = date_range("2013-01-01", periods=5).as_unit("ns") idx2 = idx1.as_unit("us") - + assert idx1.equals(idx2) is True assert idx2.equals(idx1) is True - - # Test with mismatching values but same units to ensure we don't + + # Test with mismatching values but same units to ensure we don't # get false positives idx3 = date_range("2013-01-02", periods=5).as_unit("us") assert not idx1.equals(idx3) @@ -196,12 +196,12 @@ def test_equals2(self): assert (oob3 == oob).all() assert not idx.equals(oob3) assert not idx2.equals(oob3) - + def test_equals_different_units(self): # GH#63459 idx1 = timedelta_range("1 day", periods=10).as_unit("ns") idx2 = idx1.as_unit("us") - + assert idx1.equals(idx2) is True assert idx2.equals(idx1) is True