@@ -18,7 +18,8 @@ PyDateTime_IMPORT
1818cimport pandas._libs.tslibs.util as util
1919from pandas._libs.tslibs.util cimport (
2020 is_timedelta64_object, is_datetime64_object, is_integer_object,
21- is_float_object)
21+ is_float_object, is_array
22+ )
2223
2324from pandas._libs.tslibs.c_timestamp cimport _Timestamp
2425
@@ -606,7 +607,7 @@ def _binary_op_method_timedeltalike(op, name):
606607 # We are implicitly requiring the canonical behavior to be
607608 # defined by Timestamp methods.
608609
609- elif hasattr (other, ' dtype ' ):
610+ elif is_array (other):
610611 # nd-array like
611612 if other.dtype.kind in [' m' , ' M' ]:
612613 return op(self .to_timedelta64(), other)
@@ -1347,113 +1348,64 @@ class Timedelta(_Timedelta):
13471348 __rsub__ = _binary_op_method_timedeltalike(lambda x , y : y - x, ' __rsub__' )
13481349
13491350 def __mul__ (self , other ):
1350- if hasattr (other, ' _typ' ):
1351- # Series, DataFrame, ...
1352- if other._typ == ' dateoffset' and hasattr (other, ' delta' ):
1353- # Tick offset; this op will raise TypeError
1354- return other.delta * self
1355- return NotImplemented
1351+ if is_integer_object(other) or is_float_object(other):
1352+ return Timedelta(other * self .value, unit = ' ns' )
13561353
1357- elif util.is_nan(other):
1358- # i.e. np.nan, but also catch np.float64("NaN") which would
1359- # otherwise get caught by the hasattr(other, "dtype") branch
1360- # incorrectly return a np.timedelta64 object.
1361- return NaT
1362-
1363- elif hasattr (other, ' dtype' ):
1354+ elif is_array(other):
13641355 # ndarray-like
13651356 return other * self .to_timedelta64()
13661357
1367- elif other is NaT:
1368- raise TypeError (' Cannot multiply Timedelta with NaT' )
1369-
1370- elif not (is_integer_object(other) or is_float_object(other)):
1371- # only integers and floats allowed
1372- return NotImplemented
1373-
1374- return Timedelta(other * self .value, unit = ' ns' )
1358+ return NotImplemented
13751359
13761360 __rmul__ = __mul__
13771361
13781362 def __truediv__ (self , other ):
1379- if hasattr (other, ' _typ' ):
1380- # Series, DataFrame, ...
1381- if other._typ == ' dateoffset' and hasattr (other, ' delta' ):
1382- # Tick offset
1383- return self / other.delta
1384- return NotImplemented
1385-
1386- elif is_timedelta64_object(other):
1387- # convert to Timedelta below
1388- pass
1389-
1390- elif util.is_nan(other):
1391- # i.e. np.nan, but also catch np.float64("NaN") which would
1392- # otherwise get caught by the hasattr(other, "dtype") branch
1393- # incorrectly return a np.timedelta64 object.
1394- return NaT
1395-
1396- elif hasattr (other, ' dtype' ):
1397- return self .to_timedelta64() / other
1363+ if _should_cast_to_timedelta(other):
1364+ # We interpret NaT as timedelta64("NaT")
1365+ other = Timedelta(other)
1366+ if other is NaT:
1367+ return np.nan
1368+ return self .value / float (other.value)
13981369
13991370 elif is_integer_object(other) or is_float_object(other):
14001371 # integers or floats
14011372 return Timedelta(self .value / other, unit = ' ns' )
14021373
1403- elif not _validate_ops_compat (other):
1404- return NotImplemented
1374+ elif is_array (other):
1375+ return self .to_timedelta64() / other
14051376
1406- other = Timedelta(other)
1407- if other is NaT:
1408- return np.nan
1409- return self .value / float (other.value)
1377+ return NotImplemented
14101378
14111379 def __rtruediv__ (self , other ):
1412- if hasattr (other, ' _typ' ):
1413- # Series, DataFrame, ...
1414- if other._typ == ' dateoffset' and hasattr (other, ' delta' ):
1415- # Tick offset
1416- return other.delta / self
1417- return NotImplemented
1418-
1419- elif is_timedelta64_object(other):
1420- # convert to Timedelta below
1421- pass
1422-
1423- elif util.is_nan(other):
1424- # i.e. np.nan or np.float64("NaN")
1425- raise TypeError (" Cannot divide float by Timedelta" )
1380+ if _should_cast_to_timedelta(other):
1381+ # We interpret NaT as timedelta64("NaT")
1382+ other = Timedelta(other)
1383+ if other is NaT:
1384+ return np.nan
1385+ return float (other.value) / self .value
14261386
1427- elif hasattr (other, ' dtype ' ):
1387+ elif is_array (other):
14281388 if other.dtype.kind == " O" :
14291389 # GH#31869
14301390 return np.array([x / self for x in other])
14311391 return other / self .to_timedelta64()
14321392
1433- elif not _validate_ops_compat(other):
1434- return NotImplemented
1435-
1436- other = Timedelta(other)
1437- if other is NaT:
1438- # In this context we treat NaT as timedelta-like
1439- return np.nan
1440- return float (other.value) / self .value
1393+ return NotImplemented
14411394
14421395 def __floordiv__ (self , other ):
14431396 # numpy does not implement floordiv for timedelta64 dtype, so we cannot
14441397 # just defer
1445- if hasattr (other, ' _typ ' ):
1446- # Series, DataFrame, ...
1447- if other._typ == ' dateoffset ' and hasattr (other, ' delta ' ):
1448- # Tick offset
1449- return self // other.delta
1450- return NotImplemented
1398+ if _should_cast_to_timedelta (other):
1399+ # We interpret NaT as timedelta64("NaT")
1400+ other = Timedelta (other)
1401+ if other is NaT:
1402+ return np.nan
1403+ return self .value // other.value
14511404
1452- elif is_timedelta64_object(other):
1453- # convert to Timedelta below
1454- pass
1405+ elif is_integer_object(other) or is_float_object(other):
1406+ return Timedelta(self .value // other, unit = ' ns' )
14551407
1456- elif hasattr (other, ' dtype ' ):
1408+ elif is_array (other):
14571409 if other.dtype.kind == ' m' :
14581410 # also timedelta-like
14591411 return _broadcast_floordiv_td64(self .value, other, _floordiv)
@@ -1465,50 +1417,27 @@ class Timedelta(_Timedelta):
14651417
14661418 raise TypeError (f' Invalid dtype {other.dtype} for __floordiv__' )
14671419
1468- elif is_integer_object(other) or is_float_object(other):
1469- return Timedelta(self .value // other, unit = ' ns' )
1470-
1471- elif not _validate_ops_compat(other):
1472- return NotImplemented
1473-
1474- other = Timedelta(other)
1475- if other is NaT:
1476- return np.nan
1477- return self .value // other.value
1420+ return NotImplemented
14781421
14791422 def __rfloordiv__ (self , other ):
14801423 # numpy does not implement floordiv for timedelta64 dtype, so we cannot
14811424 # just defer
1482- if hasattr (other, ' _typ' ):
1483- # Series, DataFrame, ...
1484- if other._typ == ' dateoffset' and hasattr (other, ' delta' ):
1485- # Tick offset
1486- return other.delta // self
1487- return NotImplemented
1488-
1489- elif is_timedelta64_object(other):
1490- # convert to Timedelta below
1491- pass
1425+ if _should_cast_to_timedelta(other):
1426+ # We interpret NaT as timedelta64("NaT")
1427+ other = Timedelta(other)
1428+ if other is NaT:
1429+ return np.nan
1430+ return other.value // self .value
14921431
1493- elif hasattr (other, ' dtype ' ):
1432+ elif is_array (other):
14941433 if other.dtype.kind == ' m' :
14951434 # also timedelta-like
14961435 return _broadcast_floordiv_td64(self .value, other, _rfloordiv)
14971436
14981437 # Includes integer array // Timedelta, disallowed in GH#19761
14991438 raise TypeError (f' Invalid dtype {other.dtype} for __floordiv__' )
15001439
1501- elif is_float_object(other) and util.is_nan(other):
1502- # i.e. np.nan
1503- return NotImplemented
1504-
1505- elif not _validate_ops_compat(other):
1506- return NotImplemented
1507-
1508- other = Timedelta(other)
1509- if other is NaT:
1510- return np.nan
1511- return other.value // self .value
1440+ return NotImplemented
15121441
15131442 def __mod__ (self , other ):
15141443 # Naive implementation, room for optimization
@@ -1529,6 +1458,21 @@ class Timedelta(_Timedelta):
15291458 return div, other - div * self
15301459
15311460
1461+ cdef bint is_any_td_scalar(object obj):
1462+ return (
1463+ PyDelta_Check(obj) or is_timedelta64_object(obj) or isinstance (obj, Tick)
1464+ )
1465+
1466+
1467+ cdef bint _should_cast_to_timedelta(object obj):
1468+ """
1469+ Should we treat this object as a Timedelta for the purpose of a binary op
1470+ """
1471+ return (
1472+ is_any_td_scalar(obj) or obj is None or obj is NaT or isinstance (obj, str )
1473+ )
1474+
1475+
15321476cdef _floordiv(int64_t value, right):
15331477 return value // right
15341478
0 commit comments