Skip to content

Commit 856df95

Browse files
committed
BUG: Invalid Timedelta op may raise ValueError
1 parent 2f7fdd0 commit 856df95

File tree

5 files changed

+50
-7
lines changed

5 files changed

+50
-7
lines changed

doc/source/whatsnew/v0.19.0.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,7 @@ Bug Fixes
543543
- Bug in ``.to_html``, ``.to_latex`` and ``.to_string`` silently ignore custom datetime formatter passed through the ``formatters`` key word (:issue:`10690`)
544544

545545
- Bug in ``pd.to_numeric`` when ``errors='coerce'`` and input contains non-hashable objects (:issue:`13324`)
546-
546+
- Bug in invalid ``Timedelta`` arithmetic and comparison may raise ``ValueError`` rather than ``TypeError`` (:issue:`13624`)
547547

548548
- Bug in ``Categorical.remove_unused_categories()`` changes ``.codes`` dtype to platform int (:issue:`13261`)
549549
- Bug in ``groupby`` with ``as_index=False`` returns all NaN's when grouping on multiple columns including a categorical one (:issue:`13204`)

pandas/tseries/tdi.py

+7-3
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,20 @@ def _td_index_cmp(opname, nat_result=False):
3535
"""
3636

3737
def wrapper(self, other):
38+
msg = "cannot compare a TimedeltaIndex with type {0}"
3839
func = getattr(super(TimedeltaIndex, self), opname)
3940
if _is_convertible_to_td(other) or other is tslib.NaT:
40-
other = _to_m8(other)
41+
try:
42+
other = _to_m8(other)
43+
except ValueError:
44+
# failed to parse as timedelta
45+
raise TypeError(msg.format(type(other)))
4146
result = func(other)
4247
if com.isnull(other):
4348
result.fill(nat_result)
4449
else:
4550
if not com.is_list_like(other):
46-
raise TypeError("cannot compare a TimedeltaIndex with type "
47-
"{0}".format(type(other)))
51+
raise TypeError(msg.format(type(other)))
4852

4953
other = TimedeltaIndex(other).values
5054
result = func(other)

pandas/tseries/tests/test_timedeltas.py

+32
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,21 @@ class Other:
472472
self.assertTrue(td.__mul__(other) is NotImplemented)
473473
self.assertTrue(td.__floordiv__(td) is NotImplemented)
474474

475+
def test_ops_error_str(self):
476+
# GH 13624
477+
td = Timedelta('1 day')
478+
479+
for l, r in [(td, 'a'), ('a', td)]:
480+
481+
with tm.assertRaises(TypeError):
482+
l + r
483+
484+
with tm.assertRaises(TypeError):
485+
l > r
486+
487+
self.assertFalse(l == r)
488+
self.assertTrue(l != r)
489+
475490
def test_fields(self):
476491
def check(value):
477492
# that we are int/long like
@@ -1432,6 +1447,23 @@ def test_comparisons_nat(self):
14321447
expected = np.array([True, True, True, True, True, False])
14331448
self.assert_numpy_array_equal(result, expected)
14341449

1450+
def test_ops_error_str(self):
1451+
# GH 13624
1452+
tdi = TimedeltaIndex(['1 day', '2 days'])
1453+
1454+
for l, r in [(tdi, 'a'), ('a', tdi)]:
1455+
with tm.assertRaises(TypeError):
1456+
l + r
1457+
1458+
with tm.assertRaises(TypeError):
1459+
l > r
1460+
1461+
with tm.assertRaises(TypeError):
1462+
l == r
1463+
1464+
with tm.assertRaises(TypeError):
1465+
l != r
1466+
14351467
def test_map(self):
14361468

14371469
rng = timedelta_range('1 day', periods=10)

pandas/tseries/timedeltas.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,8 @@ def _convert_listlike(arg, box, unit, name=None):
7474
value = arg.astype('timedelta64[{0}]'.format(
7575
unit)).astype('timedelta64[ns]', copy=False)
7676
else:
77-
value = tslib.array_to_timedelta64(
78-
_ensure_object(arg), unit=unit, errors=errors)
77+
value = tslib.array_to_timedelta64(_ensure_object(arg),
78+
unit=unit, errors=errors)
7979
value = value.astype('timedelta64[ns]', copy=False)
8080

8181
if box:

pandas/tslib.pyx

+8-1
Original file line numberDiff line numberDiff line change
@@ -2912,10 +2912,17 @@ class Timedelta(_Timedelta):
29122912
if not self._validate_ops_compat(other):
29132913
return NotImplemented
29142914

2915-
other = Timedelta(other)
29162915
if other is NaT:
29172916
return NaT
2917+
2918+
try:
2919+
other = Timedelta(other)
2920+
except ValueError:
2921+
# failed to parse as timedelta
2922+
return NotImplemented
2923+
29182924
return Timedelta(op(self.value, other.value), unit='ns')
2925+
29192926
f.__name__ = name
29202927
return f
29212928

0 commit comments

Comments
 (0)