Skip to content

REF: Make Period arith mirror PeriodArray arith #34278

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 46 additions & 26 deletions pandas/_libs/tslibs/period.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -1580,36 +1580,37 @@ cdef class _Period:
return PyObject_RichCompareBool(self.ordinal, other.ordinal, op)
elif other is NaT:
return _nat_scalar_rules[op]
return NotImplemented
return NotImplemented # TODO: ndarray[object]?

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

def _add_delta(self, other):
def _add_delta(self, other) -> "Period":
cdef:
int64_t nanos, offset_nanos

if (PyDelta_Check(other) or util.is_timedelta64_object(other) or
is_tick_object(other)):
offset = to_offset(self.freq.rule_code)
if is_tick_object(offset):
nanos = delta_to_nanoseconds(other)
offset_nanos = delta_to_nanoseconds(offset)
if nanos % offset_nanos == 0:
ordinal = self.ordinal + (nanos // offset_nanos)
return Period(ordinal=ordinal, freq=self.freq)
raise IncompatibleFrequency("Input cannot be converted to "
f"Period(freq={self.freqstr})")
elif is_offset_object(other):
if other.base == self.freq.base:
ordinal = self.ordinal + other.n
if is_tick_object(self.freq):
nanos = delta_to_nanoseconds(other)
offset_nanos = self.freq.base.nanos
if nanos % offset_nanos == 0:
ordinal = self.ordinal + (nanos // offset_nanos)
return Period(ordinal=ordinal, freq=self.freq)
msg = DIFFERENT_FREQ.format(cls=type(self).__name__,
own_freq=self.freqstr,
other_freq=other.freqstr)
raise IncompatibleFrequency(msg)
else: # pragma no cover
return NotImplemented
raise IncompatibleFrequency("Input cannot be converted to "
f"Period(freq={self.freqstr})")

def _add_offset(self, other) -> "Period":
# Non-Tick DateOffset other
cdef:
int64_t ordinal

if other.base == self.freq.base:
ordinal = self.ordinal + other.n
return Period(ordinal=ordinal, freq=self.freq)

msg = DIFFERENT_FREQ.format(cls=type(self).__name__,
own_freq=self.freqstr,
other_freq=other.freqstr)
raise IncompatibleFrequency(msg)

def __add__(self, other):
if not is_period_object(self):
Expand All @@ -1618,9 +1619,10 @@ cdef class _Period:
return NaT
return other.__add__(self)

if (PyDelta_Check(other) or util.is_timedelta64_object(other) or
is_offset_object(other)):
if is_any_tdlike_scalar(other):
return self._add_delta(other)
elif is_offset_object(other):
return self._add_offset(other)
elif other is NaT:
return NaT
elif util.is_integer_object(other):
Expand All @@ -1644,8 +1646,11 @@ cdef class _Period:
return NaT
return NotImplemented

elif (PyDelta_Check(other) or util.is_timedelta64_object(other) or
is_offset_object(other)):
elif is_any_tdlike_scalar(other):
neg_other = -other
return self + neg_other
elif is_offset_object(other):
# Non-Tick DateOffset
neg_other = -other
return self + neg_other
elif util.is_integer_object(other):
Expand Down Expand Up @@ -2516,3 +2521,18 @@ def validate_end_alias(how):
if how not in {'S', 'E'}:
raise ValueError('How must be one of S or E')
return how


cpdef is_any_tdlike_scalar(object obj):
"""
Cython equivalent for `isinstance(obj, (timedelta, np.timedelta64, Tick))`

Parameters
----------
obj : object

Returns
-------
bool
"""
return util.is_timedelta64_object(obj) or PyDelta_Check(obj) or is_tick_object(obj)
7 changes: 3 additions & 4 deletions pandas/_libs/tslibs/timedeltas.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ cdef convert_to_timedelta64(object ts, object unit):
"""
if checknull_with_nat(ts):
return np.timedelta64(NPY_NAT)
elif isinstance(ts, Timedelta):
elif isinstance(ts, _Timedelta):
# already in the proper format
ts = np.timedelta64(ts.value)
elif is_datetime64_object(ts):
Expand Down Expand Up @@ -1220,10 +1220,9 @@ class Timedelta(_Timedelta):

# GH 30543 if pd.Timedelta already passed, return it
# check that only value is passed
if (isinstance(value, Timedelta) and unit is None and
len(kwargs) == 0):
if isinstance(value, _Timedelta) and unit is None and len(kwargs) == 0:
return value
elif isinstance(value, Timedelta):
elif isinstance(value, _Timedelta):
value = value.value
elif isinstance(value, str):
if len(value) > 0 and value[0] == 'P':
Expand Down