diff --git a/doc/source/whatsnew/v1.4.0.rst b/doc/source/whatsnew/v1.4.0.rst index 8b51fe2db7641..b83fd0504342c 100644 --- a/doc/source/whatsnew/v1.4.0.rst +++ b/doc/source/whatsnew/v1.4.0.rst @@ -544,6 +544,8 @@ Datetimelike - Bug in in calling ``np.isnan``, ``np.isfinite``, or ``np.isinf`` on a timezone-aware :class:`DatetimeIndex` incorrectly raising ``TypeError`` (:issue:`43917`) - Bug in constructing a :class:`Series` from datetime-like strings with mixed timezones incorrectly partially-inferring datetime values (:issue:`40111`) - Bug in addition with a :class:`Tick` object and a ``np.timedelta64`` object incorrectly raising instead of returning :class:`Timedelta` (:issue:`44474`) +- Bug in adding a ``np.timedelta64`` object to a :class:`BusinessDay` or :class:`CustomBusinessDay` object incorrectly raising (:issue:`44532`) +- Timedelta ^^^^^^^^^ diff --git a/pandas/_libs/tslibs/offsets.pyx b/pandas/_libs/tslibs/offsets.pyx index f689b8ce242e5..968a4d0198f09 100644 --- a/pandas/_libs/tslibs/offsets.pyx +++ b/pandas/_libs/tslibs/offsets.pyx @@ -353,6 +353,9 @@ cdef class BaseOffset: """ Base class for DateOffset methods that are not overridden by subclasses. """ + # ensure that reversed-ops with numpy scalars return NotImplemented + __array_priority__ = 1000 + _day_opt = None _attributes = tuple(["n", "normalize"]) _use_relativedelta = False @@ -434,6 +437,10 @@ cdef class BaseOffset: if not isinstance(self, BaseOffset): # cython semantics; this is __radd__ return other.__add__(self) + + elif util.is_array(other) and other.dtype == object: + return np.array([self + x for x in other]) + try: return self.apply(other) except ApplyTypeError: @@ -448,7 +455,8 @@ cdef class BaseOffset: elif not isinstance(self, BaseOffset): # cython semantics, this is __rsub__ return (-other).__add__(self) - else: # pragma: no cover + else: + # e.g. PeriodIndex return NotImplemented def __call__(self, other): @@ -767,8 +775,6 @@ cdef class SingleConstructorOffset(BaseOffset): # Tick Offsets cdef class Tick(SingleConstructorOffset): - # ensure that reversed-ops with numpy scalars return NotImplemented - __array_priority__ = 1000 _adjust_dst = False _prefix = "undefined" _attributes = tuple(["n", "normalize"]) diff --git a/pandas/tests/tseries/offsets/test_business_day.py b/pandas/tests/tseries/offsets/test_business_day.py index 92176515b6b6f..c7db42b6e9df1 100644 --- a/pandas/tests/tseries/offsets/test_business_day.py +++ b/pandas/tests/tseries/offsets/test_business_day.py @@ -7,7 +7,6 @@ timedelta, ) -import numpy as np import pytest from pandas._libs.tslibs.offsets import ( @@ -61,7 +60,6 @@ def test_with_offset(self): assert (self.d + offset) == datetime(2008, 1, 2, 2) - @pytest.mark.parametrize("reverse", [True, False]) @pytest.mark.parametrize( "td", [ @@ -71,20 +69,15 @@ def test_with_offset(self): ], ids=lambda x: type(x), ) - def test_with_offset_index(self, reverse, td, request): - if reverse and isinstance(td, np.timedelta64): - mark = pytest.mark.xfail( - reason="need __array_priority__, but that causes other errors" - ) - request.node.add_marker(mark) + def test_with_offset_index(self, td): dti = DatetimeIndex([self.d]) expected = DatetimeIndex([datetime(2008, 1, 2, 2)]) - if reverse: - result = dti + (td + self.offset) - else: - result = dti + (self.offset + td) + result = dti + (td + self.offset) + tm.assert_index_equal(result, expected) + + result = dti + (self.offset + td) tm.assert_index_equal(result, expected) def test_eq(self):