Skip to content

Commit c38a416

Browse files
jbrockmendelWillAyd
authored andcommitted
REF/BUG: DTA/TDA/PA comparison ops inconsistencies (#30637)
1 parent 7da951f commit c38a416

File tree

4 files changed

+39
-16
lines changed

4 files changed

+39
-16
lines changed

pandas/core/arrays/datetimes.py

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -140,18 +140,19 @@ def _dt_array_cmp(cls, op):
140140
@unpack_zerodim_and_defer(opname)
141141
def wrapper(self, other):
142142

143-
if isinstance(other, (datetime, np.datetime64, str)):
144-
if isinstance(other, (datetime, np.datetime64)):
145-
# GH#18435 strings get a pass from tzawareness compat
146-
self._assert_tzawareness_compat(other)
147-
143+
if isinstance(other, str):
148144
try:
149-
other = _to_M8(other, tz=self.tz)
145+
# GH#18435 strings get a pass from tzawareness compat
146+
other = self._scalar_from_string(other)
150147
except ValueError:
151148
# string that cannot be parsed to Timestamp
152149
return invalid_comparison(self, other, op)
153150

154-
result = op(self.asi8, other.view("i8"))
151+
if isinstance(other, (datetime, np.datetime64)):
152+
other = Timestamp(other)
153+
self._assert_tzawareness_compat(other)
154+
155+
result = op(self.asi8, other.value)
155156
if isna(other):
156157
result.fill(nat_result)
157158
elif lib.is_scalar(other) or np.ndim(other) == 0:
@@ -164,9 +165,7 @@ def wrapper(self, other):
164165
other = type(self)._from_sequence(other)
165166
except ValueError:
166167
other = np.array(other, dtype=np.object_)
167-
elif not isinstance(
168-
other, (np.ndarray, ABCIndexClass, ABCSeries, DatetimeArray)
169-
):
168+
elif not isinstance(other, (np.ndarray, DatetimeArray)):
170169
# Following Timestamp convention, __eq__ is all-False
171170
# and __ne__ is all True, others raise TypeError.
172171
return invalid_comparison(self, other, op)
@@ -185,8 +184,6 @@ def wrapper(self, other):
185184
return invalid_comparison(self, other, op)
186185
else:
187186
self._assert_tzawareness_compat(other)
188-
if isinstance(other, (ABCIndexClass, ABCSeries)):
189-
other = other.array
190187

191188
if (
192189
is_datetime64_dtype(other)

pandas/core/arrays/period.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
from pandas.core.arrays import datetimelike as dtl
4646
import pandas.core.common as com
4747
from pandas.core.ops.common import unpack_zerodim_and_defer
48+
from pandas.core.ops.invalid import invalid_comparison
4849

4950
from pandas.tseries import frequencies
5051
from pandas.tseries.offsets import DateOffset, Tick, _delta_to_tick
@@ -75,6 +76,18 @@ def wrapper(self, other):
7576
if is_list_like(other) and len(other) != len(self):
7677
raise ValueError("Lengths must match")
7778

79+
if isinstance(other, str):
80+
try:
81+
other = self._scalar_from_string(other)
82+
except ValueError:
83+
# string that can't be parsed as Period
84+
return invalid_comparison(self, other, op)
85+
elif isinstance(other, int):
86+
# TODO: sure we want to allow this? we dont for DTA/TDA
87+
# 2 tests rely on this
88+
other = Period(other, freq=self.freq)
89+
result = ordinal_op(other.ordinal)
90+
7891
if isinstance(other, Period):
7992
self._check_compatible_with(other)
8093

@@ -93,8 +106,7 @@ def wrapper(self, other):
93106
result = np.empty(len(self.asi8), dtype=bool)
94107
result.fill(nat_result)
95108
else:
96-
other = Period(other, freq=self.freq)
97-
result = ordinal_op(other.ordinal)
109+
return invalid_comparison(self, other, op)
98110

99111
if self._hasnans:
100112
result[self._isnan] = nat_result

pandas/core/arrays/timedeltas.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,13 +82,16 @@ def _td_array_cmp(cls, op):
8282
@unpack_zerodim_and_defer(opname)
8383
def wrapper(self, other):
8484

85-
if _is_convertible_to_td(other) or other is NaT:
85+
if isinstance(other, str):
8686
try:
87-
other = Timedelta(other)
87+
other = self._scalar_from_string(other)
8888
except ValueError:
8989
# failed to parse as timedelta
9090
return invalid_comparison(self, other, op)
9191

92+
if _is_convertible_to_td(other) or other is NaT:
93+
other = Timedelta(other)
94+
9295
result = op(self.view("i8"), other.value)
9396
if isna(other):
9497
result.fill(nat_result)

pandas/tests/arithmetic/test_period.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
from pandas.tseries.frequencies import to_offset
1919

20+
from .common import assert_invalid_comparison
21+
2022
# ------------------------------------------------------------------
2123
# Comparisons
2224

@@ -39,6 +41,15 @@ def test_compare_zerodim(self, box_with_array):
3941
expected = tm.box_expected(expected, xbox)
4042
tm.assert_equal(result, expected)
4143

44+
@pytest.mark.parametrize(
45+
"scalar", ["foo", pd.Timestamp.now(), pd.Timedelta(days=4)]
46+
)
47+
def test_compare_invalid_scalar(self, box_with_array, scalar):
48+
# comparison with scalar that cannot be interpreted as a Period
49+
pi = pd.period_range("2000", periods=4)
50+
parr = tm.box_expected(pi, box_with_array)
51+
assert_invalid_comparison(parr, scalar, box_with_array)
52+
4253

4354
class TestPeriodIndexComparisons:
4455
# TODO: parameterize over boxes

0 commit comments

Comments
 (0)