Skip to content

Commit cacf82f

Browse files
fix raise of TypeError when subtracting timedelta array
closes pandas-dev#21980
1 parent 716efd3 commit cacf82f

File tree

3 files changed

+125
-5
lines changed

3 files changed

+125
-5
lines changed

doc/source/whatsnew/v0.24.0.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -441,7 +441,7 @@ Datetimelike
441441
Timedelta
442442
^^^^^^^^^
443443

444-
-
444+
- Fixed bug where subtracting :class:`Timedelta` from an object-dtyped array would raise ``TypeError`` (:issue:`21980`)
445445
-
446446
-
447447

pandas/_libs/tslibs/timedeltas.pyx

+6-4
Original file line numberDiff line numberDiff line change
@@ -542,10 +542,12 @@ def _binary_op_method_timedeltalike(op, name):
542542

543543
elif hasattr(other, 'dtype'):
544544
# nd-array like
545-
if other.dtype.kind not in ['m', 'M']:
546-
# raise rathering than letting numpy return wrong answer
545+
if other.dtype.kind in ['m', 'M']:
546+
return op(self.to_timedelta64(), other)
547+
elif other.dtype.kind == 'O':
548+
return np.array([op(self, x) for x in other])
549+
else:
547550
return NotImplemented
548-
return op(self.to_timedelta64(), other)
549551

550552
elif not _validate_ops_compat(other):
551553
return NotImplemented
@@ -929,7 +931,7 @@ cdef class _Timedelta(timedelta):
929931
def nanoseconds(self):
930932
"""
931933
Return the number of nanoseconds (n), where 0 <= n < 1 microsecond.
932-
934+
933935
Returns
934936
-------
935937
int

pandas/tests/scalar/timedelta/test_arithmetic.py

+118
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,92 @@ def test_td_rsub_numeric_raises(self):
200200
with pytest.raises(TypeError):
201201
2.0 - td
202202

203+
def test_td_sub_timedeltalike_object_dtype_array(self):
204+
# GH 21980
205+
arr = np.array([Timestamp('20130101 9:01'),
206+
Timestamp('20121230 9:02')])
207+
exp = np.array([Timestamp('20121231 9:01'),
208+
Timestamp('20121229 9:02')])
209+
res = arr - pd.Timedelta('1D')
210+
tm.assert_numpy_array_equal(res, exp)
211+
212+
def test_td_sub_mixed_timedeltalike_object_dtype_array(self):
213+
# GH 21980
214+
now = pd.Timestamp.now()
215+
arr = np.array([now,
216+
pd.Timedelta('1D')])
217+
exp = np.array([now - pd.Timedelta('1D'),
218+
pd.Timedelta('0D')])
219+
res = arr - pd.Timedelta('1D')
220+
tm.assert_numpy_array_equal(res, exp)
221+
222+
def test_td_sub_mixed_most_timedeltalike_object_dtype_array(self):
223+
# GH 21980
224+
now = pd.Timestamp.now()
225+
arr = np.array([now,
226+
pd.Timedelta('1D'),
227+
np.timedelta64(2, 'h')])
228+
exp = np.array([now - pd.Timedelta('1D'),
229+
pd.Timedelta('0D'),
230+
np.timedelta64(2, 'h') - pd.Timedelta('1D')])
231+
res = arr - pd.Timedelta('1D')
232+
tm.assert_numpy_array_equal(res, exp)
233+
234+
def test_td_rsub_timedeltalike_object_dtype_array(self):
235+
# GH 21980
236+
arr = np.array([Timestamp('20130101 9:01'),
237+
Timestamp('20121230 9:02')])
238+
with pytest.raises(TypeError):
239+
# an timedelta - timestamp doesnt make sense
240+
pd.Timedelta('1D') - arr
241+
242+
def test_td_rsub_mixed_timedeltalike_object_dtype_array(self):
243+
# GH 21980
244+
now = pd.Timestamp.now()
245+
arr = np.array([now,
246+
pd.Timedelta('1D')])
247+
with pytest.raises(TypeError):
248+
# an timedelta - timestamp doesnt make sense
249+
pd.Timedelta('1D') - arr
250+
251+
def test_td_add_timedeltalike_object_dtype_array(self):
252+
# GH 21980
253+
arr = np.array([Timestamp('20130101 9:01'),
254+
Timestamp('20121230 9:02')])
255+
exp = np.array([Timestamp('20130102 9:01'),
256+
Timestamp('20121231 9:02')])
257+
res = arr + pd.Timedelta('1D')
258+
tm.assert_numpy_array_equal(res, exp)
259+
260+
def test_td_add_mixed_timedeltalike_object_dtype_array(self):
261+
# GH 21980
262+
now = pd.Timestamp.now()
263+
arr = np.array([now,
264+
pd.Timedelta('1D')])
265+
exp = np.array([now + pd.Timedelta('1D'),
266+
pd.Timedelta('2D')])
267+
res = arr + pd.Timedelta('1D')
268+
tm.assert_numpy_array_equal(res, exp)
269+
270+
def test_td_radd_timedeltalike_object_dtype_array(self):
271+
# GH 21980
272+
arr = np.array([Timestamp('20130101 9:01'),
273+
Timestamp('20121230 9:02')])
274+
exp = np.array([Timestamp('20130102 9:01'),
275+
Timestamp('20121231 9:02')])
276+
res = pd.Timedelta('1D') + arr
277+
tm.assert_numpy_array_equal(res, exp)
278+
279+
def test_td_radd_mixed_timedeltalike_object_dtype_array(self):
280+
# GH 21980
281+
now = pd.Timestamp.now()
282+
arr = np.array([now,
283+
pd.Timedelta('1D')])
284+
exp = np.array([now + pd.Timedelta('1D'),
285+
pd.Timedelta('2D')])
286+
res = pd.Timedelta('1D') + arr
287+
tm.assert_numpy_array_equal(res, exp)
288+
203289

204290
class TestTimedeltaMultiplicationDivision(object):
205291
"""
@@ -616,3 +702,35 @@ def test_rdivmod_invalid(self):
616702

617703
with pytest.raises(TypeError):
618704
divmod(np.array([22, 24]), td)
705+
706+
def test_td_div_timedelta_timedeltalike_array(self):
707+
# GH 21980
708+
arr = np.array([Timestamp('20130101 9:01'),
709+
Timestamp('20121230 9:02')])
710+
711+
with pytest.raises(TypeError):
712+
arr / pd.Timedelta('1D')
713+
714+
def test_td_rdiv_timedelta_mixed_timedeltalike_array(self):
715+
# GH 21980
716+
arr = np.array([pd.Timestamp.now(),
717+
pd.Timedelta('1D')])
718+
719+
with pytest.raises(TypeError):
720+
pd.Timedelta('1D') / arr
721+
722+
def test_td_mult_timedelta_mixed_timedeltalike_array(self):
723+
# GH 21980
724+
arr = np.array([pd.Timestamp.now(),
725+
pd.Timedelta('1D')])
726+
727+
with pytest.raises(TypeError):
728+
pd.Timedelta('1D') * arr
729+
730+
def test_td_rmult_timedelta_mixed_timedeltalike_array(self):
731+
# GH 21980
732+
arr = np.array([pd.Timestamp.now(),
733+
pd.Timedelta('1D')])
734+
735+
with pytest.raises(TypeError):
736+
arr * pd.Timedelta('1D')

0 commit comments

Comments
 (0)