@@ -18,7 +18,8 @@ PyDateTime_IMPORT
18
18
cimport pandas._libs.tslibs.util as util
19
19
from pandas._libs.tslibs.util cimport (
20
20
is_timedelta64_object, is_datetime64_object, is_integer_object,
21
- is_float_object)
21
+ is_float_object, is_array
22
+ )
22
23
23
24
from pandas._libs.tslibs.c_timestamp cimport _Timestamp
24
25
@@ -606,7 +607,7 @@ def _binary_op_method_timedeltalike(op, name):
606
607
# We are implicitly requiring the canonical behavior to be
607
608
# defined by Timestamp methods.
608
609
609
- elif hasattr (other, ' dtype ' ):
610
+ elif is_array (other):
610
611
# nd-array like
611
612
if other.dtype.kind in [' m' , ' M' ]:
612
613
return op(self .to_timedelta64(), other)
@@ -1347,113 +1348,64 @@ class Timedelta(_Timedelta):
1347
1348
__rsub__ = _binary_op_method_timedeltalike(lambda x , y : y - x, ' __rsub__' )
1348
1349
1349
1350
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' )
1356
1353
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):
1364
1355
# ndarray-like
1365
1356
return other * self .to_timedelta64()
1366
1357
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
1375
1359
1376
1360
__rmul__ = __mul__
1377
1361
1378
1362
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)
1398
1369
1399
1370
elif is_integer_object(other) or is_float_object(other):
1400
1371
# integers or floats
1401
1372
return Timedelta(self .value / other, unit = ' ns' )
1402
1373
1403
- elif not _validate_ops_compat (other):
1404
- return NotImplemented
1374
+ elif is_array (other):
1375
+ return self .to_timedelta64() / other
1405
1376
1406
- other = Timedelta(other)
1407
- if other is NaT:
1408
- return np.nan
1409
- return self .value / float (other.value)
1377
+ return NotImplemented
1410
1378
1411
1379
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
1426
1386
1427
- elif hasattr (other, ' dtype ' ):
1387
+ elif is_array (other):
1428
1388
if other.dtype.kind == " O" :
1429
1389
# GH#31869
1430
1390
return np.array([x / self for x in other])
1431
1391
return other / self .to_timedelta64()
1432
1392
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
1441
1394
1442
1395
def __floordiv__ (self , other ):
1443
1396
# numpy does not implement floordiv for timedelta64 dtype, so we cannot
1444
1397
# 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
1451
1404
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' )
1455
1407
1456
- elif hasattr (other, ' dtype ' ):
1408
+ elif is_array (other):
1457
1409
if other.dtype.kind == ' m' :
1458
1410
# also timedelta-like
1459
1411
return _broadcast_floordiv_td64(self .value, other, _floordiv)
@@ -1465,50 +1417,27 @@ class Timedelta(_Timedelta):
1465
1417
1466
1418
raise TypeError (f' Invalid dtype {other.dtype} for __floordiv__' )
1467
1419
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
1478
1421
1479
1422
def __rfloordiv__ (self , other ):
1480
1423
# numpy does not implement floordiv for timedelta64 dtype, so we cannot
1481
1424
# 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
1492
1431
1493
- elif hasattr (other, ' dtype ' ):
1432
+ elif is_array (other):
1494
1433
if other.dtype.kind == ' m' :
1495
1434
# also timedelta-like
1496
1435
return _broadcast_floordiv_td64(self .value, other, _rfloordiv)
1497
1436
1498
1437
# Includes integer array // Timedelta, disallowed in GH#19761
1499
1438
raise TypeError (f' Invalid dtype {other.dtype} for __floordiv__' )
1500
1439
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
1512
1441
1513
1442
def __mod__ (self , other ):
1514
1443
# Naive implementation, room for optimization
@@ -1529,6 +1458,21 @@ class Timedelta(_Timedelta):
1529
1458
return div, other - div * self
1530
1459
1531
1460
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
+
1532
1476
cdef _floordiv(int64_t value, right):
1533
1477
return value // right
1534
1478
0 commit comments