Skip to content

Commit 8d0e1fe

Browse files
authored
REF: Make Period arith mirror PeriodArray arith (#34278)
1 parent 25a2fab commit 8d0e1fe

File tree

2 files changed

+49
-30
lines changed

2 files changed

+49
-30
lines changed

pandas/_libs/tslibs/period.pyx

+46-26
Original file line numberDiff line numberDiff line change
@@ -1580,36 +1580,37 @@ cdef class _Period:
15801580
return PyObject_RichCompareBool(self.ordinal, other.ordinal, op)
15811581
elif other is NaT:
15821582
return _nat_scalar_rules[op]
1583-
return NotImplemented
1583+
return NotImplemented # TODO: ndarray[object]?
15841584

15851585
def __hash__(self):
15861586
return hash((self.ordinal, self.freqstr))
15871587

1588-
def _add_delta(self, other):
1588+
def _add_delta(self, other) -> "Period":
15891589
cdef:
15901590
int64_t nanos, offset_nanos
15911591

1592-
if (PyDelta_Check(other) or util.is_timedelta64_object(other) or
1593-
is_tick_object(other)):
1594-
offset = to_offset(self.freq.rule_code)
1595-
if is_tick_object(offset):
1596-
nanos = delta_to_nanoseconds(other)
1597-
offset_nanos = delta_to_nanoseconds(offset)
1598-
if nanos % offset_nanos == 0:
1599-
ordinal = self.ordinal + (nanos // offset_nanos)
1600-
return Period(ordinal=ordinal, freq=self.freq)
1601-
raise IncompatibleFrequency("Input cannot be converted to "
1602-
f"Period(freq={self.freqstr})")
1603-
elif is_offset_object(other):
1604-
if other.base == self.freq.base:
1605-
ordinal = self.ordinal + other.n
1592+
if is_tick_object(self.freq):
1593+
nanos = delta_to_nanoseconds(other)
1594+
offset_nanos = self.freq.base.nanos
1595+
if nanos % offset_nanos == 0:
1596+
ordinal = self.ordinal + (nanos // offset_nanos)
16061597
return Period(ordinal=ordinal, freq=self.freq)
1607-
msg = DIFFERENT_FREQ.format(cls=type(self).__name__,
1608-
own_freq=self.freqstr,
1609-
other_freq=other.freqstr)
1610-
raise IncompatibleFrequency(msg)
1611-
else: # pragma no cover
1612-
return NotImplemented
1598+
raise IncompatibleFrequency("Input cannot be converted to "
1599+
f"Period(freq={self.freqstr})")
1600+
1601+
def _add_offset(self, other) -> "Period":
1602+
# Non-Tick DateOffset other
1603+
cdef:
1604+
int64_t ordinal
1605+
1606+
if other.base == self.freq.base:
1607+
ordinal = self.ordinal + other.n
1608+
return Period(ordinal=ordinal, freq=self.freq)
1609+
1610+
msg = DIFFERENT_FREQ.format(cls=type(self).__name__,
1611+
own_freq=self.freqstr,
1612+
other_freq=other.freqstr)
1613+
raise IncompatibleFrequency(msg)
16131614

16141615
def __add__(self, other):
16151616
if not is_period_object(self):
@@ -1618,9 +1619,10 @@ cdef class _Period:
16181619
return NaT
16191620
return other.__add__(self)
16201621

1621-
if (PyDelta_Check(other) or util.is_timedelta64_object(other) or
1622-
is_offset_object(other)):
1622+
if is_any_tdlike_scalar(other):
16231623
return self._add_delta(other)
1624+
elif is_offset_object(other):
1625+
return self._add_offset(other)
16241626
elif other is NaT:
16251627
return NaT
16261628
elif util.is_integer_object(other):
@@ -1644,8 +1646,11 @@ cdef class _Period:
16441646
return NaT
16451647
return NotImplemented
16461648

1647-
elif (PyDelta_Check(other) or util.is_timedelta64_object(other) or
1648-
is_offset_object(other)):
1649+
elif is_any_tdlike_scalar(other):
1650+
neg_other = -other
1651+
return self + neg_other
1652+
elif is_offset_object(other):
1653+
# Non-Tick DateOffset
16491654
neg_other = -other
16501655
return self + neg_other
16511656
elif util.is_integer_object(other):
@@ -2516,3 +2521,18 @@ def validate_end_alias(how):
25162521
if how not in {'S', 'E'}:
25172522
raise ValueError('How must be one of S or E')
25182523
return how
2524+
2525+
2526+
cpdef is_any_tdlike_scalar(object obj):
2527+
"""
2528+
Cython equivalent for `isinstance(obj, (timedelta, np.timedelta64, Tick))`
2529+
2530+
Parameters
2531+
----------
2532+
obj : object
2533+
2534+
Returns
2535+
-------
2536+
bool
2537+
"""
2538+
return util.is_timedelta64_object(obj) or PyDelta_Check(obj) or is_tick_object(obj)

pandas/_libs/tslibs/timedeltas.pyx

+3-4
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ cdef convert_to_timedelta64(object ts, object unit):
176176
"""
177177
if checknull_with_nat(ts):
178178
return np.timedelta64(NPY_NAT)
179-
elif isinstance(ts, Timedelta):
179+
elif isinstance(ts, _Timedelta):
180180
# already in the proper format
181181
ts = np.timedelta64(ts.value)
182182
elif is_datetime64_object(ts):
@@ -1149,10 +1149,9 @@ class Timedelta(_Timedelta):
11491149

11501150
# GH 30543 if pd.Timedelta already passed, return it
11511151
# check that only value is passed
1152-
if (isinstance(value, Timedelta) and unit is None and
1153-
len(kwargs) == 0):
1152+
if isinstance(value, _Timedelta) and unit is None and len(kwargs) == 0:
11541153
return value
1155-
elif isinstance(value, Timedelta):
1154+
elif isinstance(value, _Timedelta):
11561155
value = value.value
11571156
elif isinstance(value, str):
11581157
if len(value) > 0 and value[0] == 'P':

0 commit comments

Comments
 (0)