Skip to content

Commit 080004c

Browse files
authored
BUG: Timestamp+- ndarray[td64] (#33296)
1 parent 82999c8 commit 080004c

File tree

3 files changed

+65
-23
lines changed

3 files changed

+65
-23
lines changed

doc/source/whatsnew/v1.1.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,7 @@ Datetimelike
306306
- :class:`Timestamp` raising confusing error message when year, month or day is missing (:issue:`31200`)
307307
- Bug in :class:`DatetimeIndex` constructor incorrectly accepting ``bool``-dtyped inputs (:issue:`32668`)
308308
- Bug in :meth:`DatetimeIndex.searchsorted` not accepting a ``list`` or :class:`Series` as its argument (:issue:`32762`)
309+
- Bug in :class:`Timestamp` arithmetic when adding or subtracting a ``np.ndarray`` with ``timedelta64`` dtype (:issue:`33296`)
309310

310311
Timedelta
311312
^^^^^^^^^

pandas/_libs/tslibs/c_timestamp.pyx

+14
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,13 @@ cdef class _Timestamp(datetime):
253253
elif is_array(other):
254254
if other.dtype.kind in ['i', 'u']:
255255
raise integer_op_not_supported(self)
256+
if other.dtype.kind == "m":
257+
if self.tz is None:
258+
return self.asm8 + other
259+
return np.asarray(
260+
[self + other[n] for n in range(len(other))],
261+
dtype=object,
262+
)
256263

257264
# index/series like
258265
elif hasattr(other, '_typ'):
@@ -275,6 +282,13 @@ cdef class _Timestamp(datetime):
275282
elif is_array(other):
276283
if other.dtype.kind in ['i', 'u']:
277284
raise integer_op_not_supported(self)
285+
if other.dtype.kind == "m":
286+
if self.tz is None:
287+
return self.asm8 - other
288+
return np.asarray(
289+
[self - other[n] for n in range(len(other))],
290+
dtype=object,
291+
)
278292

279293
typ = getattr(other, '_typ', None)
280294
if typ is not None:

pandas/tests/scalar/timestamp/test_arithmetic.py

+50-23
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from pandas.errors import OutOfBoundsDatetime
77

88
from pandas import Timedelta, Timestamp
9+
import pandas._testing as tm
910

1011
from pandas.tseries import offsets
1112
from pandas.tseries.frequencies import to_offset
@@ -177,29 +178,6 @@ def test_timestamp_add_timedelta64_unit(self, other, expected_difference):
177178
valdiff = result.value - ts.value
178179
assert valdiff == expected_difference
179180

180-
@pytest.mark.parametrize("ts", [Timestamp.now(), Timestamp.now("utc")])
181-
@pytest.mark.parametrize(
182-
"other",
183-
[
184-
1,
185-
np.int64(1),
186-
np.array([1, 2], dtype=np.int32),
187-
np.array([3, 4], dtype=np.uint64),
188-
],
189-
)
190-
def test_add_int_no_freq_raises(self, ts, other):
191-
msg = "Addition/subtraction of integers and integer-arrays"
192-
with pytest.raises(TypeError, match=msg):
193-
ts + other
194-
with pytest.raises(TypeError, match=msg):
195-
other + ts
196-
197-
with pytest.raises(TypeError, match=msg):
198-
ts - other
199-
msg = "unsupported operand type"
200-
with pytest.raises(TypeError, match=msg):
201-
other - ts
202-
203181
@pytest.mark.parametrize(
204182
"ts",
205183
[
@@ -229,3 +207,52 @@ def test_add_int_with_freq(self, ts, other):
229207
msg = "unsupported operand type"
230208
with pytest.raises(TypeError, match=msg):
231209
other - ts
210+
211+
@pytest.mark.parametrize("shape", [(6,), (2, 3,)])
212+
def test_addsub_m8ndarray(self, shape):
213+
# GH#33296
214+
ts = Timestamp("2020-04-04 15:45")
215+
other = np.arange(6).astype("m8[h]").reshape(shape)
216+
217+
result = ts + other
218+
219+
ex_stamps = [ts + Timedelta(hours=n) for n in range(6)]
220+
expected = np.array([x.asm8 for x in ex_stamps], dtype="M8[ns]").reshape(shape)
221+
tm.assert_numpy_array_equal(result, expected)
222+
223+
result = other + ts
224+
tm.assert_numpy_array_equal(result, expected)
225+
226+
result = ts - other
227+
ex_stamps = [ts - Timedelta(hours=n) for n in range(6)]
228+
expected = np.array([x.asm8 for x in ex_stamps], dtype="M8[ns]").reshape(shape)
229+
tm.assert_numpy_array_equal(result, expected)
230+
231+
msg = r"unsupported operand type\(s\) for -: 'numpy.ndarray' and 'Timestamp'"
232+
with pytest.raises(TypeError, match=msg):
233+
other - ts
234+
235+
@pytest.mark.parametrize("shape", [(6,), (2, 3,)])
236+
def test_addsub_m8ndarray_tzaware(self, shape):
237+
# GH#33296
238+
ts = Timestamp("2020-04-04 15:45", tz="US/Pacific")
239+
240+
other = np.arange(6).astype("m8[h]").reshape(shape)
241+
242+
result = ts + other
243+
244+
ex_stamps = [ts + Timedelta(hours=n) for n in range(6)]
245+
expected = np.array(ex_stamps).reshape(shape)
246+
tm.assert_numpy_array_equal(result, expected)
247+
248+
result = other + ts
249+
tm.assert_numpy_array_equal(result, expected)
250+
251+
result = ts - other
252+
ex_stamps = [ts - Timedelta(hours=n) for n in range(6)]
253+
expected = np.array(ex_stamps).reshape(shape)
254+
tm.assert_numpy_array_equal(result, expected)
255+
256+
msg = r"unsupported operand type\(s\) for -: 'numpy.ndarray' and 'Timestamp'"
257+
with pytest.raises(TypeError, match=msg):
258+
other - ts

0 commit comments

Comments
 (0)