Skip to content

Commit 9c36c83

Browse files
authored
PERF: use is_tick_offset to check for Tick (#34032)
1 parent 8872766 commit 9c36c83

File tree

4 files changed

+30
-31
lines changed

4 files changed

+30
-31
lines changed

pandas/_libs/tslibs/c_timestamp.pyx

+14-8
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ from pandas._libs.tslibs.timezones cimport (
4040
get_timezone, is_utc, tz_compare)
4141
from pandas._libs.tslibs.timezones import UTC
4242
from pandas._libs.tslibs.tzconversion cimport tz_convert_single
43+
from pandas._libs.tslibs.offsets cimport is_tick_object
4344

4445

4546
class NullFrequencyError(ValueError):
@@ -247,13 +248,9 @@ cdef class _Timestamp(datetime):
247248
elif is_integer_object(other):
248249
raise integer_op_not_supported(self)
249250

250-
elif PyDelta_Check(other) or hasattr(other, 'delta'):
251-
# delta --> offsets.Tick
251+
elif PyDelta_Check(other):
252252
# logic copied from delta_to_nanoseconds to prevent circular import
253-
if hasattr(other, 'nanos'):
254-
# Tick
255-
nanos = other.nanos
256-
elif hasattr(other, 'delta'):
253+
if hasattr(other, 'delta'):
257254
# pd.Timedelta
258255
nanos = other.value
259256
elif PyDelta_Check(other):
@@ -264,6 +261,16 @@ cdef class _Timestamp(datetime):
264261
result = type(self)(self.value + nanos, tz=self.tzinfo, freq=self.freq)
265262
return result
266263

264+
elif is_tick_object(other):
265+
try:
266+
nanos = other.nanos
267+
except OverflowError:
268+
raise OverflowError(
269+
f"the add operation between {other} and {self} will overflow"
270+
)
271+
result = type(self)(self.value + nanos, tz=self.tzinfo, freq=self.freq)
272+
return result
273+
267274
elif is_array(other):
268275
if other.dtype.kind in ['i', 'u']:
269276
raise integer_op_not_supported(self)
@@ -280,8 +287,7 @@ cdef class _Timestamp(datetime):
280287
def __sub__(self, other):
281288

282289
if (is_timedelta64_object(other) or is_integer_object(other) or
283-
PyDelta_Check(other) or hasattr(other, 'delta')):
284-
# `delta` attribute is for offsets.Tick or offsets.Week obj
290+
PyDelta_Check(other) or is_tick_object(other)):
285291
neg_other = -other
286292
return self + neg_other
287293

pandas/_libs/tslibs/nattype.pyx

+2-4
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,7 @@ cdef class _NaT(datetime):
147147
return c_NaT
148148
elif util.is_datetime64_object(other) or util.is_timedelta64_object(other):
149149
return c_NaT
150-
elif hasattr(other, "delta"):
151-
# Timedelta, offsets.Tick, offsets.Week
150+
elif util.is_offset_object(other):
152151
return c_NaT
153152

154153
elif util.is_integer_object(other) or util.is_period_object(other):
@@ -184,8 +183,7 @@ cdef class _NaT(datetime):
184183
return c_NaT
185184
elif util.is_datetime64_object(other) or util.is_timedelta64_object(other):
186185
return c_NaT
187-
elif hasattr(other, "delta"):
188-
# offsets.Tick, offsets.Week
186+
elif util.is_offset_object(other):
189187
return c_NaT
190188

191189
elif util.is_integer_object(other) or util.is_period_object(other):

pandas/_libs/tslibs/period.pyx

+3-4
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,7 @@ from pandas._libs.tslibs.resolution import Resolution
5757
from pandas._libs.tslibs.nattype import nat_strings
5858
from pandas._libs.tslibs.nattype cimport (
5959
_nat_scalar_rules, NPY_NAT, is_null_datetimelike, c_NaT as NaT)
60-
from pandas._libs.tslibs.offsets cimport to_offset
61-
from pandas._libs.tslibs.offsets import _Tick
60+
from pandas._libs.tslibs.offsets cimport to_offset, is_tick_object
6261
from pandas._libs.tslibs.tzconversion cimport tz_convert_utc_to_tzlocal
6362

6463

@@ -1588,9 +1587,9 @@ cdef class _Period:
15881587
int64_t nanos, offset_nanos
15891588

15901589
if (PyDelta_Check(other) or util.is_timedelta64_object(other) or
1591-
isinstance(other, _Tick)):
1590+
is_tick_object(other)):
15921591
offset = to_offset(self.freq.rule_code)
1593-
if isinstance(offset, _Tick):
1592+
if is_tick_object(offset):
15941593
nanos = delta_to_nanoseconds(other)
15951594
offset_nanos = delta_to_nanoseconds(offset)
15961595
if nanos % offset_nanos == 0:

pandas/_libs/tslibs/timedeltas.pyx

+11-15
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,7 @@ from pandas._libs.tslibs.np_datetime cimport (
3131
from pandas._libs.tslibs.nattype import nat_strings
3232
from pandas._libs.tslibs.nattype cimport (
3333
checknull_with_nat, NPY_NAT, c_NaT as NaT)
34-
from pandas._libs.tslibs.offsets cimport to_offset
35-
from pandas._libs.tslibs.offsets import _Tick as Tick
34+
from pandas._libs.tslibs.offsets cimport to_offset, is_tick_object
3635

3736
# ----------------------------------------------------------------------
3837
# Constants
@@ -140,10 +139,10 @@ def ints_to_pytimedelta(const int64_t[:] arr, box=False):
140139
# ----------------------------------------------------------------------
141140

142141
cpdef int64_t delta_to_nanoseconds(delta) except? -1:
143-
if hasattr(delta, 'nanos'):
142+
if is_tick_object(delta):
144143
return delta.nanos
145-
if hasattr(delta, 'delta'):
146-
delta = delta.delta
144+
if isinstance(delta, _Timedelta):
145+
delta = delta.value
147146
if is_timedelta64_object(delta):
148147
return delta.astype("timedelta64[ns]").item()
149148
if is_integer_object(delta):
@@ -204,8 +203,8 @@ cdef convert_to_timedelta64(object ts, object unit):
204203
else:
205204
ts = parse_timedelta_string(ts)
206205
ts = np.timedelta64(ts)
207-
elif hasattr(ts, 'delta'):
208-
ts = np.timedelta64(delta_to_nanoseconds(ts), 'ns')
206+
elif is_tick_object(ts):
207+
ts = np.timedelta64(ts.nanos, 'ns')
209208

210209
if PyDelta_Check(ts):
211210
ts = np.timedelta64(delta_to_nanoseconds(ts), 'ns')
@@ -562,12 +561,10 @@ cdef bint _validate_ops_compat(other):
562561
# return True if we are compat with operating
563562
if checknull_with_nat(other):
564563
return True
565-
elif PyDelta_Check(other) or is_timedelta64_object(other):
564+
elif is_any_td_scalar(other):
566565
return True
567566
elif isinstance(other, str):
568567
return True
569-
elif hasattr(other, 'delta'):
570-
return True
571568
return False
572569

573570

@@ -779,8 +776,7 @@ cdef class _Timedelta(timedelta):
779776

780777
if isinstance(other, _Timedelta):
781778
ots = other
782-
elif (is_timedelta64_object(other) or PyDelta_Check(other)
783-
or isinstance(other, Tick)):
779+
elif is_any_td_scalar(other):
784780
ots = Timedelta(other)
785781
# TODO: watch out for overflows
786782

@@ -1249,8 +1245,8 @@ class Timedelta(_Timedelta):
12491245
if unit is not None:
12501246
value = value.astype(f'timedelta64[{unit}]')
12511247
value = value.astype('timedelta64[ns]')
1252-
elif hasattr(value, 'delta'):
1253-
value = np.timedelta64(delta_to_nanoseconds(value.delta), 'ns')
1248+
elif is_tick_object(value):
1249+
value = np.timedelta64(value.nanos, 'ns')
12541250
elif is_integer_object(value) or is_float_object(value):
12551251
# unit=None is de-facto 'ns'
12561252
unit = parse_timedelta_unit(unit)
@@ -1460,7 +1456,7 @@ class Timedelta(_Timedelta):
14601456

14611457
cdef bint is_any_td_scalar(object obj):
14621458
return (
1463-
PyDelta_Check(obj) or is_timedelta64_object(obj) or isinstance(obj, Tick)
1459+
PyDelta_Check(obj) or is_timedelta64_object(obj) or is_tick_object(obj)
14641460
)
14651461

14661462

0 commit comments

Comments
 (0)