diff --git a/doc/source/whatsnew/v3.0.0.rst b/doc/source/whatsnew/v3.0.0.rst index 68c5556c4bcbd..50dc08456c7c7 100644 --- a/doc/source/whatsnew/v3.0.0.rst +++ b/doc/source/whatsnew/v3.0.0.rst @@ -758,6 +758,10 @@ Datetimelike - Bug in :meth:`to_datetime` reports incorrect index in case of any failure scenario. (:issue:`58298`) - Bug in :meth:`to_datetime` with ``format="ISO8601"`` and ``utc=True`` where naive timestamps incorrectly inherited timezone offset from previous timestamps in a series. (:issue:`61389`) - Bug in :meth:`to_datetime` wrongly converts when ``arg`` is a ``np.datetime64`` object with unit of ``ps``. (:issue:`60341`) +- Bug in subtracting a :class:`Timestamp` from a :class:`DatetimeIndex` with + ``freq="D"`` dropping the frequency and causing subsequent + :meth:`TimedeltaIndex.shift` calls to raise ``NullFrequencyError`` + (:issue:`62094`) - Bug in constructing arrays with :class:`ArrowDtype` with ``timestamp`` type incorrectly allowing ``Decimal("NaN")`` (:issue:`61773`) - Bug in constructing arrays with a timezone-aware :class:`ArrowDtype` from timezone-naive datetime objects incorrectly treating those as UTC times instead of wall times like :class:`DatetimeTZDtype` (:issue:`61775`) - Bug in setting scalar values with mismatched resolution into arrays with non-nanosecond ``datetime64``, ``timedelta64`` or :class:`DatetimeTZDtype` incorrectly truncating those scalars (:issue:`56410`) diff --git a/pandas/core/arrays/datetimelike.py b/pandas/core/arrays/datetimelike.py index 7e57b40e42430..b175c7b9d18c5 100644 --- a/pandas/core/arrays/datetimelike.py +++ b/pandas/core/arrays/datetimelike.py @@ -1089,6 +1089,18 @@ def _get_arithmetic_result_freq(self, other) -> BaseOffset | None: # e.g. TestTimedelta64ArithmeticUnsorted::test_timedelta # Day is unambiguously 24h return self.freq + elif ( + lib.is_np_dtype(self.dtype, "M") + and isinstance(self.freq, Day) + and isinstance(other, Timestamp) + ): + self = cast("DatetimeArray", self) + if (self.tz is None or timezones.is_utc(self.tz)) and ( + other.tz is None or timezones.is_utc(other.tz) + ): + # e.g. issue gh-62094: subtracting a Timestamp from a DTI + # with Day freq retains that freq + return self.freq return None diff --git a/pandas/tests/indexes/timedeltas/methods/test_shift.py b/pandas/tests/indexes/timedeltas/methods/test_shift.py index 9bbf06dc51a0c..14529c6b1e06d 100644 --- a/pandas/tests/indexes/timedeltas/methods/test_shift.py +++ b/pandas/tests/indexes/timedeltas/methods/test_shift.py @@ -74,3 +74,10 @@ def test_shift_no_freq(self): tdi = TimedeltaIndex(["1 days 01:00:00", "2 days 01:00:00"], freq=None) with pytest.raises(NullFrequencyError, match="Cannot shift with no freq"): tdi.shift(2) + + def test_shift_after_dti_sub_timestamp(self): + dti = pd.date_range("1/1/2021", "1/5/2021") + tdi = dti - pd.Timestamp("1/3/2019") + result = tdi.shift(1) + expected = tdi + pd.Timedelta(days=1) + tm.assert_index_equal(result, expected)