diff --git a/pandas/_libs/tslibs/offsets.pyx b/pandas/_libs/tslibs/offsets.pyx index 0deaf082dd1c7..77b60d0c22322 100644 --- a/pandas/_libs/tslibs/offsets.pyx +++ b/pandas/_libs/tslibs/offsets.pyx @@ -40,6 +40,7 @@ from pandas._libs.tslibs.ccalendar cimport DAY_NANOS, get_days_in_month, dayofwe from pandas._libs.tslibs.conversion cimport ( convert_datetime_to_tsobject, localize_pydatetime, + normalize_i8_timestamps, ) from pandas._libs.tslibs.nattype cimport NPY_NAT, c_NaT as NaT from pandas._libs.tslibs.np_datetime cimport ( @@ -79,21 +80,14 @@ cdef bint _is_normalized(datetime dt): def apply_index_wraps(func): # Note: normally we would use `@functools.wraps(func)`, but this does # not play nicely with cython class methods - def wrapper(self, other): - - is_index = not util.is_array(other._data) - - # operate on DatetimeArray - arr = other._data if is_index else other - - result = func(self, arr) + def wrapper(self, other) -> np.ndarray: + # other is a DatetimeArray - if is_index: - # Wrap DatetimeArray result back to DatetimeIndex - result = type(other)._simple_new(result, name=other.name) + result = func(self, other) + result = np.asarray(result) if self.normalize: - result = result.to_period('D').to_timestamp() + result = normalize_i8_timestamps(result.view("i8"), None) return result # do @functools.wraps(func) manually since it doesn't work on cdef funcs @@ -1889,7 +1883,7 @@ cdef class YearOffset(SingleConstructorOffset): shifted = shift_quarters( dtindex.asi8, self.n, self.month, self._day_opt, modby=12 ) - return type(dtindex)._simple_new(shifted, dtype=dtindex.dtype) + return shifted cdef class BYearEnd(YearOffset): @@ -2033,7 +2027,7 @@ cdef class QuarterOffset(SingleConstructorOffset): shifted = shift_quarters( dtindex.asi8, self.n, self.startingMonth, self._day_opt ) - return type(dtindex)._simple_new(shifted, dtype=dtindex.dtype) + return shifted cdef class BQuarterEnd(QuarterOffset): @@ -2139,7 +2133,7 @@ cdef class MonthOffset(SingleConstructorOffset): @apply_index_wraps def apply_index(self, dtindex): shifted = shift_months(dtindex.asi8, self.n, self._day_opt) - return type(dtindex)._simple_new(shifted, dtype=dtindex.dtype) + return shifted cpdef __setstate__(self, state): state.pop("_use_relativedelta", False) @@ -2503,8 +2497,6 @@ cdef class Week(SingleConstructorOffset): @apply_index_wraps def apply_index(self, dtindex): if self.weekday is None: - # integer addition on PeriodIndex is deprecated, - # so we use _time_shift directly td = timedelta(days=7 * self.n) td64 = np.timedelta64(td, "ns") return dtindex + td64 diff --git a/pandas/core/arrays/datetimes.py b/pandas/core/arrays/datetimes.py index 7bbe60c4fdcd1..053aeb6d81be4 100644 --- a/pandas/core/arrays/datetimes.py +++ b/pandas/core/arrays/datetimes.py @@ -685,7 +685,9 @@ def _add_offset(self, offset): values = self.tz_localize(None) else: values = self - result = offset.apply_index(values).tz_localize(self.tz) + result = offset.apply_index(values) + result = DatetimeArray._simple_new(result) + result = result.tz_localize(self.tz) except NotImplementedError: warnings.warn( diff --git a/pandas/tests/tseries/offsets/test_offsets.py b/pandas/tests/tseries/offsets/test_offsets.py index 86cc7ff753660..e3a89d9ed57a6 100644 --- a/pandas/tests/tseries/offsets/test_offsets.py +++ b/pandas/tests/tseries/offsets/test_offsets.py @@ -3524,7 +3524,7 @@ def test_offset_whole_year(self): with tm.assert_produces_warning(None): # GH#22535 check that we don't get a FutureWarning from adding # an integer array to PeriodIndex - result = SemiMonthEnd().apply_index(s) + result = SemiMonthEnd() + s exp = DatetimeIndex(dates[1:]) tm.assert_index_equal(result, exp) @@ -3672,7 +3672,7 @@ def test_apply_index(self, case): with tm.assert_produces_warning(None): # GH#22535 check that we don't get a FutureWarning from adding # an integer array to PeriodIndex - result = offset.apply_index(s) + result = offset + s exp = DatetimeIndex(cases.values()) tm.assert_index_equal(result, exp) @@ -3783,7 +3783,7 @@ def test_offset_whole_year(self): with tm.assert_produces_warning(None): # GH#22535 check that we don't get a FutureWarning from adding # an integer array to PeriodIndex - result = SemiMonthBegin().apply_index(s) + result = SemiMonthBegin() + s exp = DatetimeIndex(dates[1:]) tm.assert_index_equal(result, exp) @@ -3936,7 +3936,7 @@ def test_apply_index(self, case): with tm.assert_produces_warning(None): # GH#22535 check that we don't get a FutureWarning from adding # an integer array to PeriodIndex - result = offset.apply_index(s) + result = offset + s exp = DatetimeIndex(cases.values()) tm.assert_index_equal(result, exp) diff --git a/pandas/tests/tseries/offsets/test_yqm_offsets.py b/pandas/tests/tseries/offsets/test_yqm_offsets.py index 13cab9be46d37..9921355bdf2ee 100644 --- a/pandas/tests/tseries/offsets/test_yqm_offsets.py +++ b/pandas/tests/tseries/offsets/test_yqm_offsets.py @@ -65,8 +65,6 @@ def test_apply_index(cls, n): res = rng + offset assert res.freq is None # not retained - res_v2 = offset.apply_index(rng) - assert (res == res_v2).all() assert res[0] == rng[0] + offset assert res[-1] == rng[-1] + offset res2 = ser + offset