diff --git a/doc/source/whatsnew/v1.0.0.rst b/doc/source/whatsnew/v1.0.0.rst index 4671170fa79ae..c6c5562af3951 100644 --- a/doc/source/whatsnew/v1.0.0.rst +++ b/doc/source/whatsnew/v1.0.0.rst @@ -716,7 +716,7 @@ Datetimelike - Bug in :class:`DatetimeIndex` addition when adding a non-optimized :class:`DateOffset` incorrectly dropping timezone information (:issue:`30336`) - Bug in :meth:`DataFrame.drop` where attempting to drop non-existent values from a DatetimeIndex would yield a confusing error message (:issue:`30399`) - Bug in :meth:`DataFrame.append` would remove the timezone-awareness of new data (:issue:`30238`) - +- Bug in :class:`DatetimeArray`, :class:`TimedeltaArray`, and :class:`PeriodArray` where inplace addition and subtraction did not actually operate inplace (:issue:`24115`) Timedelta ^^^^^^^^^ diff --git a/pandas/core/arrays/datetimelike.py b/pandas/core/arrays/datetimelike.py index ff21a66928294..66f0ad2500c54 100644 --- a/pandas/core/arrays/datetimelike.py +++ b/pandas/core/arrays/datetimelike.py @@ -1311,14 +1311,23 @@ def __rsub__(self, other): return -(self - other) - # FIXME: DTA/TDA/PA inplace methods should actually be inplace, GH#24115 def __iadd__(self, other): # type: ignore - # alias for __add__ - return self.__add__(other) + result = self + other + self[:] = result[:] + + if not is_period_dtype(self): + # restore freq, which is invalidated by setitem + self._freq = result._freq + return self def __isub__(self, other): # type: ignore - # alias for __sub__ - return self.__sub__(other) + result = self - other + self[:] = result[:] + + if not is_period_dtype(self): + # restore freq, which is invalidated by setitem + self._freq = result._freq + return self # -------------------------------------------------------------- # Comparison Methods diff --git a/pandas/tests/arrays/test_datetimelike.py b/pandas/tests/arrays/test_datetimelike.py index 84b6d45b78fe8..e9c64d04ec860 100644 --- a/pandas/tests/arrays/test_datetimelike.py +++ b/pandas/tests/arrays/test_datetimelike.py @@ -225,6 +225,19 @@ def test_setitem_raises(self): with pytest.raises(TypeError, match="'value' should be a.* 'object'"): arr[0] = object() + def test_inplace_arithmetic(self): + # GH#24115 check that iadd and isub are actually in-place + data = np.arange(10, dtype="i8") * 24 * 3600 * 10 ** 9 + arr = self.array_cls(data, freq="D") + + expected = arr + pd.Timedelta(days=1) + arr += pd.Timedelta(days=1) + tm.assert_equal(arr, expected) + + expected = arr - pd.Timedelta(days=1) + arr -= pd.Timedelta(days=1) + tm.assert_equal(arr, expected) + class TestDatetimeArray(SharedTests): index_cls = pd.DatetimeIndex