11import collections
2+ import operator
23import warnings
34
45cimport cython
@@ -215,20 +216,16 @@ cpdef int64_t delta_to_nanoseconds(delta) except? -1:
215216 return get_timedelta64_value(ensure_td64ns(delta))
216217
217218 if PyDelta_Check(delta):
218- try :
219- return (
220- delta.days * 24 * 3600 * 1 _000_000
221- + delta.seconds * 1 _000_000
222- + delta.microseconds
223- ) * 1000
224- except OverflowError as err:
225- msg = f" {delta} outside allowed range [{TIMEDELTA_MIN_NS}ns, {TIMEDELTA_MAX_NS}ns]"
226- raise OutOfBoundsTimedelta(msg) from err
219+ microseconds = (
220+ delta.days * 24 * 3600 * 1 _000_000
221+ + delta.seconds * 1 _000_000
222+ + delta.microseconds
223+ )
224+ return calc_int_int(operator.mul, microseconds, 1000 )
227225
228226 raise TypeError (type (delta))
229227
230228
231- @ cython.overflowcheck (True )
232229cdef object ensure_td64ns(object ts):
233230 """
234231 Overflow-safe implementation of td64.astype("m8[ns]")
@@ -247,25 +244,14 @@ cdef object ensure_td64ns(object ts):
247244 str unitstr
248245
249246 td64_unit = get_datetime64_unit(ts)
250- if (
251- td64_unit != NPY_DATETIMEUNIT.NPY_FR_ns
252- and td64_unit != NPY_DATETIMEUNIT.NPY_FR_GENERIC
253- ):
254- unitstr = npy_unit_to_abbrev(td64_unit)
255-
256- td64_value = get_timedelta64_value(ts)
257-
258- mult = precision_from_unit(unitstr)[0 ]
259- try :
260- # NB: cython#1381 this cannot be *=
261- td64_value = td64_value * mult
262- except OverflowError as err:
263- msg = f" {ts} outside allowed range [{TIMEDELTA_MIN_NS}ns, {TIMEDELTA_MAX_NS}ns]"
264- raise OutOfBoundsTimedelta(msg) from err
247+ if td64_unit in (NPY_DATETIMEUNIT.NPY_FR_ns, NPY_DATETIMEUNIT.NPY_FR_GENERIC):
248+ return ts
265249
266- return np.timedelta64(td64_value, " ns" )
250+ unitstr = npy_unit_to_abbrev(td64_unit)
251+ mult = precision_from_unit(unitstr)[0 ]
252+ td64_value = calc_int_int(operator.mul, get_timedelta64_value(ts), mult)
267253
268- return ts
254+ return np.timedelta64(td64_value, " ns " )
269255
270256
271257cdef convert_to_timedelta64(object ts, str unit):
@@ -686,13 +672,27 @@ def _op_unary_method(func, name):
686672 return f
687673
688674
689- cpdef int64_t calculate (object op, object a, object b) except ? - 1 :
675+ cpdef int64_t calc_int_int (object op, object a, object b) except ? - 1 :
690676 """
691- Calculate op(a, b), raising if either operand or the resulting value cannot be
692- safely cast to an int64_t.
677+ Calculate op(a, b), raising if either operand or the result cannot be safely cast
678+ to an int64_t.
693679 """
694680 try :
695- return ops.calculate(op, a, b)
681+ return ops.calc_int_int(op, a, b)
682+ except OverflowError as ex:
683+ msg = f" outside allowed range [{TIMEDELTA_MIN_NS}ns, {TIMEDELTA_MAX_NS}ns]"
684+ raise OutOfBoundsTimedelta(msg) from ex
685+
686+
687+ cpdef int64_t calc_int_float(object op, object a, object b) except ? - 1 :
688+ """
689+ Calculate op(int, double), raising if any of the following aren't safe conversions:
690+ - a to int64_t
691+ - b to double
692+ - result to int64_t
693+ """
694+ try :
695+ return ops.calc_int_float(op, a, b)
696696 except OverflowError as ex:
697697 msg = f" outside allowed range [{TIMEDELTA_MIN_NS}ns, {TIMEDELTA_MAX_NS}ns]"
698698 raise OutOfBoundsTimedelta(msg) from ex
@@ -742,7 +742,7 @@ def _binary_op_method_timedeltalike(op, name):
742742 if self ._reso != other._reso:
743743 raise NotImplementedError
744744
745- result = calculate (op, self .value, other.value)
745+ result = calc_int_int (op, self .value, other.value)
746746 if result == NPY_NAT:
747747 return NaT
748748 return _timedelta_from_value_and_reso(result, self ._reso)
@@ -1601,19 +1601,18 @@ class Timedelta(_Timedelta):
16011601 __rsub__ = _binary_op_method_timedeltalike(lambda x , y : y - x, ' __rsub__' )
16021602
16031603 def __mul__ (self , other ):
1604- if is_integer_object(other) or is_float_object(other):
1605- if util.is_nan(other):
1606- # np.nan * timedelta -> np.timedelta64("NaT"), in this case NaT
1607- return NaT
1608-
1609- return _timedelta_from_value_and_reso(
1610- < int64_t> (other * self .value),
1611- reso = self ._reso,
1612- )
1613-
1614- elif is_array(other):
1604+ if util.is_nan(other):
1605+ # np.nan * timedelta -> np.timedelta64("NaT"), in this case NaT
1606+ return NaT
1607+ if is_array(other):
16151608 # ndarray-like
16161609 return other * self .to_timedelta64()
1610+ if is_integer_object(other):
1611+ value = calc_int_int(operator.mul, self .value, other)
1612+ return _timedelta_from_value_and_reso(value, self ._reso)
1613+ if is_float_object(other):
1614+ value = calc_int_float(operator.mul, self .value, other)
1615+ return _timedelta_from_value_and_reso(value, self ._reso)
16171616
16181617 return NotImplemented
16191618
0 commit comments