1
1
import collections
2
+ import operator
2
3
import warnings
3
4
4
5
cimport cython
@@ -215,20 +216,16 @@ cpdef int64_t delta_to_nanoseconds(delta) except? -1:
215
216
return get_timedelta64_value(ensure_td64ns(delta))
216
217
217
218
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 )
227
225
228
226
raise TypeError (type (delta))
229
227
230
228
231
- @ cython.overflowcheck (True )
232
229
cdef object ensure_td64ns(object ts):
233
230
"""
234
231
Overflow-safe implementation of td64.astype("m8[ns]")
@@ -247,25 +244,14 @@ cdef object ensure_td64ns(object ts):
247
244
str unitstr
248
245
249
246
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
265
249
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)
267
253
268
- return ts
254
+ return np.timedelta64(td64_value, " ns " )
269
255
270
256
271
257
cdef convert_to_timedelta64(object ts, str unit):
@@ -686,13 +672,27 @@ def _op_unary_method(func, name):
686
672
return f
687
673
688
674
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 :
690
676
"""
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.
693
679
"""
694
680
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)
696
696
except OverflowError as ex:
697
697
msg = f" outside allowed range [{TIMEDELTA_MIN_NS}ns, {TIMEDELTA_MAX_NS}ns]"
698
698
raise OutOfBoundsTimedelta(msg) from ex
@@ -742,7 +742,7 @@ def _binary_op_method_timedeltalike(op, name):
742
742
if self ._reso != other._reso:
743
743
raise NotImplementedError
744
744
745
- result = calculate (op, self .value, other.value)
745
+ result = calc_int_int (op, self .value, other.value)
746
746
if result == NPY_NAT:
747
747
return NaT
748
748
return _timedelta_from_value_and_reso(result, self ._reso)
@@ -1601,19 +1601,18 @@ class Timedelta(_Timedelta):
1601
1601
__rsub__ = _binary_op_method_timedeltalike(lambda x , y : y - x, ' __rsub__' )
1602
1602
1603
1603
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):
1615
1608
# ndarray-like
1616
1609
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)
1617
1616
1618
1617
return NotImplemented
1619
1618
0 commit comments