diff --git a/doc/source/whatsnew/v0.23.0.txt b/doc/source/whatsnew/v0.23.0.txt index 14949267fc37d..9df7563d3b869 100644 --- a/doc/source/whatsnew/v0.23.0.txt +++ b/doc/source/whatsnew/v0.23.0.txt @@ -273,6 +273,7 @@ Other API Changes - The default ``Timedelta`` constructor now accepts an ``ISO 8601 Duration`` string as an argument (:issue:`19040`) - ``IntervalDtype`` now returns ``True`` when compared against ``'interval'`` regardless of subtype, and ``IntervalDtype.name`` now returns ``'interval'`` regardless of subtype (:issue:`18980`) - :func:`Series.to_csv` now accepts a ``compression`` argument that works in the same way as the ``compression`` argument in :func:`DataFrame.to_csv` (:issue:`18958`) +- Addition or subtraction of ``NaT`` from :class:`TimedeltaIndex` will return ``TimedeltaIndex`` instead of ``DatetimeIndex`` (:issue:`19124`) .. _whatsnew_0230.deprecations: diff --git a/pandas/core/indexes/timedeltas.py b/pandas/core/indexes/timedeltas.py index 12ca26cfe0266..866329b16c830 100644 --- a/pandas/core/indexes/timedeltas.py +++ b/pandas/core/indexes/timedeltas.py @@ -403,7 +403,8 @@ def _add_datelike(self, other): # adding a timedeltaindex to a datetimelike from pandas import Timestamp, DatetimeIndex if other is NaT: - result = self._nat_new(box=False) + # GH#19124 pd.NaT is treated like a timedelta + return self._nat_new() else: other = Timestamp(other) i8 = self.asi8 @@ -413,12 +414,13 @@ def _add_datelike(self, other): return DatetimeIndex(result, name=self.name, copy=False) def _sub_datelike(self, other): - from pandas import DatetimeIndex + # GH#19124 Timedelta - datetime is not in general well-defined. + # We make an exception for pd.NaT, which in this case quacks + # like a timedelta. if other is NaT: - result = self._nat_new(box=False) + return self._nat_new() else: raise TypeError("cannot subtract a datelike from a TimedeltaIndex") - return DatetimeIndex(result, name=self.name, copy=False) def _add_offset_array(self, other): # Array/Index of DateOffset objects diff --git a/pandas/tests/scalar/test_nat.py b/pandas/tests/scalar/test_nat.py index d0d204253e3f1..73e8c783ba882 100644 --- a/pandas/tests/scalar/test_nat.py +++ b/pandas/tests/scalar/test_nat.py @@ -302,14 +302,30 @@ def test_nat_arithmetic_index(): tm.assert_index_equal(left - right, exp) tm.assert_index_equal(right - left, exp) - # timedelta + # timedelta # GH#19124 tdi = TimedeltaIndex(['1 day', '2 day'], name='x') - exp = DatetimeIndex([NaT, NaT], name='x') - for (left, right) in [(NaT, tdi)]: - tm.assert_index_equal(left + right, exp) - tm.assert_index_equal(right + left, exp) - tm.assert_index_equal(left - right, exp) - tm.assert_index_equal(right - left, exp) + tdi_nat = TimedeltaIndex([NaT, NaT], name='x') + + tm.assert_index_equal(tdi + NaT, tdi_nat) + tm.assert_index_equal(NaT + tdi, tdi_nat) + tm.assert_index_equal(tdi - NaT, tdi_nat) + tm.assert_index_equal(NaT - tdi, tdi_nat) + + +@pytest.mark.parametrize('box, assert_func', [ + (TimedeltaIndex, tm.assert_index_equal), + pytest.param(Series, tm.assert_series_equal, + marks=pytest.mark.xfail(reason='NaT - Series returns NaT')) +]) +def test_nat_arithmetic_td64_vector(box, assert_func): + # GH#19124 + vec = box(['1 day', '2 day'], dtype='timedelta64[ns]') + box_nat = box([NaT, NaT], dtype='timedelta64[ns]') + + assert_func(vec + NaT, box_nat) + assert_func(NaT + vec, box_nat) + assert_func(vec - NaT, box_nat) + assert_func(NaT - vec, box_nat) def test_nat_pinned_docstrings():