diff --git a/pandas/tests/arithmetic/test_datetime64.py b/pandas/tests/arithmetic/test_datetime64.py index c055b3e62a368..afce374aebe05 100644 --- a/pandas/tests/arithmetic/test_datetime64.py +++ b/pandas/tests/arithmetic/test_datetime64.py @@ -1890,13 +1890,10 @@ def test_dti_addsub_int(self, tz_naive_fixture, one): with pytest.raises(TypeError, match=msg): rng + one - with pytest.raises(TypeError, match=msg): rng += one - with pytest.raises(TypeError, match=msg): rng - one - with pytest.raises(TypeError, match=msg): rng -= one @@ -1910,13 +1907,8 @@ def test_dti_add_intarray_tick(self, int_holder, freq): dti = pd.date_range("2016-01-01", periods=2, freq=freq) other = int_holder([4, -1]) - msg = "Addition/subtraction of integers" - - with pytest.raises(TypeError, match=msg): - dti + other - - with pytest.raises(TypeError, match=msg): - other + dti + msg = "Addition/subtraction of integers|cannot subtract DatetimeArray from" + assert_invalid_addsub_type(dti, other, msg) @pytest.mark.parametrize("freq", ["W", "M", "MS", "Q"]) @pytest.mark.parametrize("int_holder", [np.array, pd.Index]) @@ -1925,29 +1917,18 @@ def test_dti_add_intarray_non_tick(self, int_holder, freq): dti = pd.date_range("2016-01-01", periods=2, freq=freq) other = int_holder([4, -1]) - msg = "Addition/subtraction of integers" - - with pytest.raises(TypeError, match=msg): - dti + other - - with pytest.raises(TypeError, match=msg): - other + dti + msg = "Addition/subtraction of integers|cannot subtract DatetimeArray from" + assert_invalid_addsub_type(dti, other, msg) @pytest.mark.parametrize("int_holder", [np.array, pd.Index]) def test_dti_add_intarray_no_freq(self, int_holder): # GH#19959 dti = pd.DatetimeIndex(["2016-01-01", "NaT", "2017-04-05 06:07:08"]) other = int_holder([9, 4, -1]) - tmsg = "cannot subtract DatetimeArray from" - msg = "Addition/subtraction of integers" - with pytest.raises(TypeError, match=msg): - dti + other - with pytest.raises(TypeError, match=msg): - other + dti - with pytest.raises(TypeError, match=msg): - dti - other - with pytest.raises(TypeError, match=tmsg): - other - dti + msg = "|".join( + ["cannot subtract DatetimeArray from", "Addition/subtraction of integers"] + ) + assert_invalid_addsub_type(dti, other, msg) # ------------------------------------------------------------- # Binary operations DatetimeIndex and TimedeltaIndex/array diff --git a/pandas/tests/arithmetic/test_timedelta64.py b/pandas/tests/arithmetic/test_timedelta64.py index cafbbc9aef6f7..cc337f8fdd7ce 100644 --- a/pandas/tests/arithmetic/test_timedelta64.py +++ b/pandas/tests/arithmetic/test_timedelta64.py @@ -18,7 +18,11 @@ Timestamp, timedelta_range, ) -from pandas.tests.arithmetic.common import assert_invalid_comparison, get_upcast_box +from pandas.tests.arithmetic.common import ( + assert_invalid_addsub_type, + assert_invalid_comparison, + get_upcast_box, +) import pandas.util.testing as tm # ------------------------------------------------------------------ @@ -470,6 +474,62 @@ def test_tda_add_sub_index(self): expected = tdi - tdi tm.assert_index_equal(result, expected) + # ------------------------------------------------------------- + # Binary operations TimedeltaIndex and timedelta-like + + def test_tdi_iadd_timedeltalike(self, two_hours): + # only test adding/sub offsets as + is now numeric + rng = timedelta_range("1 days", "10 days") + expected = timedelta_range("1 days 02:00:00", "10 days 02:00:00", freq="D") + rng += two_hours + tm.assert_index_equal(rng, expected) + + def test_tdi_isub_timedeltalike(self, two_hours): + # only test adding/sub offsets as - is now numeric + rng = timedelta_range("1 days", "10 days") + expected = timedelta_range("0 days 22:00:00", "9 days 22:00:00") + rng -= two_hours + tm.assert_index_equal(rng, expected) + + # ------------------------------------------------------------- + + def test_tdi_ops_attributes(self): + rng = timedelta_range("2 days", periods=5, freq="2D", name="x") + + result = rng + 1 * rng.freq + exp = timedelta_range("4 days", periods=5, freq="2D", name="x") + tm.assert_index_equal(result, exp) + assert result.freq == "2D" + + result = rng - 2 * rng.freq + exp = timedelta_range("-2 days", periods=5, freq="2D", name="x") + tm.assert_index_equal(result, exp) + assert result.freq == "2D" + + result = rng * 2 + exp = timedelta_range("4 days", periods=5, freq="4D", name="x") + tm.assert_index_equal(result, exp) + assert result.freq == "4D" + + result = rng / 2 + exp = timedelta_range("1 days", periods=5, freq="D", name="x") + tm.assert_index_equal(result, exp) + assert result.freq == "D" + + result = -rng + exp = timedelta_range("-2 days", periods=5, freq="-2D", name="x") + tm.assert_index_equal(result, exp) + assert result.freq == "-2D" + + rng = pd.timedelta_range("-2 days", periods=5, freq="D", name="x") + + result = abs(rng) + exp = TimedeltaIndex( + ["2 days", "1 days", "0 days", "1 days", "2 days"], name="x" + ) + tm.assert_index_equal(result, exp) + assert result.freq is None + class TestAddSubNaTMasking: # TODO: parametrize over boxes @@ -541,22 +601,6 @@ def test_tdi_add_overflow(self): class TestTimedeltaArraylikeAddSubOps: # Tests for timedelta64[ns] __add__, __sub__, __radd__, __rsub__ - # TODO: moved from frame tests; needs parametrization/de-duplication - def test_td64_df_add_int_frame(self): - # GH#22696 Check that we don't dispatch to numpy implementation, - # which treats int64 as m8[ns] - tdi = pd.timedelta_range("1", periods=3) - df = tdi.to_frame() - other = pd.DataFrame([1, 2, 3], index=tdi) # indexed like `df` - with pytest.raises(TypeError): - df + other - with pytest.raises(TypeError): - other + df - with pytest.raises(TypeError): - df - other - with pytest.raises(TypeError): - other - df - # TODO: moved from tests.indexes.timedeltas.test_arithmetic; needs # parametrization+de-duplication def test_timedelta_ops_with_missing_values(self): @@ -811,51 +855,6 @@ def test_timedelta64_ops_nat(self): tm.assert_series_equal(timedelta_series / 2.0, Series([NaT, Timedelta("0.5s")])) tm.assert_series_equal(timedelta_series / np.nan, nat_series_dtype_timedelta) - # ------------------------------------------------------------- - # Invalid Operations - - @pytest.mark.parametrize("other", ["a", 3.14, np.array([2.0, 3.0])]) - def test_td64arr_add_sub_invalid(self, box_with_array, other): - # GH#13624 for str - tdi = TimedeltaIndex(["1 day", "2 days"]) - tdarr = tm.box_expected(tdi, box_with_array) - - with pytest.raises(TypeError): - tdarr + other - with pytest.raises(TypeError): - other + tdarr - with pytest.raises(TypeError): - tdarr - other - with pytest.raises(TypeError): - other - tdarr - - @pytest.mark.parametrize("freq", [None, "H"]) - def test_td64arr_sub_period(self, box_with_array, freq): - # GH#13078 - # not supported, check TypeError - p = pd.Period("2011-01-01", freq="D") - idx = TimedeltaIndex(["1 hours", "2 hours"], freq=freq) - idx = tm.box_expected(idx, box_with_array) - - with pytest.raises(TypeError): - idx - p - - with pytest.raises(TypeError): - p - idx - - @pytest.mark.parametrize("pi_freq", ["D", "W", "Q", "H"]) - @pytest.mark.parametrize("tdi_freq", [None, "H"]) - def test_td64arr_sub_pi(self, box_with_array, tdi_freq, pi_freq): - # GH#20049 subtracting PeriodIndex should raise TypeError - tdi = TimedeltaIndex(["1 hours", "2 hours"], freq=tdi_freq) - dti = Timestamp("2018-03-07 17:16:40") + tdi - pi = dti.to_period(pi_freq) - - # TODO: parametrize over box for pi? - tdi = tm.box_expected(tdi, box_with_array) - with pytest.raises(TypeError): - tdi - pi - # ------------------------------------------------------------- # Binary operations td64 arraylike and datetime-like @@ -962,127 +961,109 @@ def test_td64arr_add_datetime64_nat(self, box_with_array): tm.assert_equal(other + tdser, expected) # ------------------------------------------------------------------ - # Operations with int-like others + # Invalid __add__/__sub__ operations + + # TODO: moved from frame tests; needs parametrization/de-duplication + def test_td64_df_add_int_frame(self): + # GH#22696 Check that we don't dispatch to numpy implementation, + # which treats int64 as m8[ns] + tdi = pd.timedelta_range("1", periods=3) + df = tdi.to_frame() + other = pd.DataFrame([1, 2, 3], index=tdi) # indexed like `df` + assert_invalid_addsub_type(df, other) + + @pytest.mark.parametrize("pi_freq", ["D", "W", "Q", "H"]) + @pytest.mark.parametrize("tdi_freq", [None, "H"]) + def test_td64arr_sub_periodlike(self, box_with_array, tdi_freq, pi_freq): + # GH#20049 subtracting PeriodIndex should raise TypeError + tdi = TimedeltaIndex(["1 hours", "2 hours"], freq=tdi_freq) + dti = Timestamp("2018-03-07 17:16:40") + tdi + pi = dti.to_period(pi_freq) + + # TODO: parametrize over box for pi? + tdi = tm.box_expected(tdi, box_with_array) + with pytest.raises(TypeError): + tdi - pi + + # FIXME: don't leave commented-out + # FIXME: this raises with period scalar but not with PeriodIndex? + # with pytest.raises(TypeError): + # pi - tdi + + # GH#13078 subtraction of Period scalar not supported + with pytest.raises(TypeError): + tdi - pi[0] + with pytest.raises(TypeError): + pi[0] - tdi @pytest.mark.parametrize( "other", [ + # GH#12624 for str case + "a", # GH#19123 1, - Series([20, 30, 40], dtype="uint8"), - np.array([20, 30, 40], dtype="uint8"), - pd.UInt64Index([20, 30, 40]), - pd.Int64Index([20, 30, 40]), - Series([2, 3, 4]), 1.5, np.array(2), ], ) - def test_td64arr_addsub_numeric_invalid(self, box_with_array, other): - box = box_with_array + def test_td64arr_addsub_numeric_scalar_invalid(self, box_with_array, other): + # vector-like others are tested in test_td64arr_add_sub_numeric_arr_invalid tdser = pd.Series(["59 Days", "59 Days", "NaT"], dtype="m8[ns]") - tdser = tm.box_expected(tdser, box) + tdarr = tm.box_expected(tdser, box_with_array) - with pytest.raises(TypeError): - tdser + other - with pytest.raises(TypeError): - other + tdser - with pytest.raises(TypeError): - tdser - other - with pytest.raises(TypeError): - other - tdser + assert_invalid_addsub_type(tdarr, other) - @pytest.mark.parametrize( - "dtype", - [ - "int64", - "int32", - "int16", - "uint64", - "uint32", - "uint16", - "uint8", - "float64", - "float32", - "float16", - ], - ) @pytest.mark.parametrize( "vec", [ np.array([1, 2, 3]), pd.Index([1, 2, 3]), - Series([1, 2, 3]) - # TODO: Add DataFrame in here? + Series([1, 2, 3]), + DataFrame([[1, 2, 3]]), ], ids=lambda x: type(x).__name__, ) - def test_td64arr_add_sub_numeric_arr_invalid(self, box_with_array, vec, dtype): - box = box_with_array + def test_td64arr_addsub_numeric_arr_invalid( + self, box_with_array, vec, any_real_dtype + ): tdser = pd.Series(["59 Days", "59 Days", "NaT"], dtype="m8[ns]") - tdser = tm.box_expected(tdser, box) + tdarr = tm.box_expected(tdser, box_with_array) - vector = vec.astype(dtype) - with pytest.raises(TypeError): - tdser + vector - with pytest.raises(TypeError): - vector + tdser - with pytest.raises(TypeError): - tdser - vector - with pytest.raises(TypeError): - vector - tdser + vector = vec.astype(any_real_dtype) + assert_invalid_addsub_type(tdarr, vector) - # TODO: parameterize over box and de-duplicate - def test_tdi_add_sub_int(self, one): + def test_td64arr_add_sub_int(self, box_with_array, one): # Variants of `one` for #19012, deprecated GH#22535 rng = timedelta_range("1 days 09:00:00", freq="H", periods=10) + tdarr = tm.box_expected(rng, box_with_array) + msg = "Addition/subtraction of integers" + assert_invalid_addsub_type(tdarr, one, msg) + # TOOD: get inplace ops into assert_invalid_addsub_type with pytest.raises(TypeError, match=msg): - rng + one + tdarr += one with pytest.raises(TypeError, match=msg): - rng += one - with pytest.raises(TypeError, match=msg): - rng - one - with pytest.raises(TypeError, match=msg): - rng -= one + tdarr -= one - # TODO: parameterize over box and de-duplicate - @pytest.mark.parametrize("box", [np.array, pd.Index]) - def test_tdi_add_sub_integer_array(self, box): + def test_td64arr_add_sub_integer_array(self, box_with_array): # GH#19959, deprecated GH#22535 rng = timedelta_range("1 days 09:00:00", freq="H", periods=3) - other = box([4, 3, 2]) - msg = "Addition/subtraction of integers and integer-arrays" + tdarr = tm.box_expected(rng, box_with_array) + other = tm.box_expected([4, 3, 2], box_with_array) - with pytest.raises(TypeError, match=msg): - rng + other - - with pytest.raises(TypeError, match=msg): - other + rng - - with pytest.raises(TypeError, match=msg): - rng - other - - with pytest.raises(TypeError, match=msg): - other - rng + msg = "Addition/subtraction of integers and integer-arrays" + assert_invalid_addsub_type(tdarr, other, msg) - # TODO: parameterize over box and de-duplicate - @pytest.mark.parametrize("box", [np.array, pd.Index]) - def test_tdi_addsub_integer_array_no_freq(self, box): + def test_td64arr_addsub_integer_array_no_freq(self, box_with_array): # GH#19959 tdi = TimedeltaIndex(["1 Day", "NaT", "3 Hours"]) - other = box([14, -1, 16]) - msg = "Addition/subtraction of integers" + tdarr = tm.box_expected(tdi, box_with_array) + other = tm.box_expected([14, -1, 16], box_with_array) - with pytest.raises(TypeError, match=msg): - tdi + other - with pytest.raises(TypeError, match=msg): - other + tdi - with pytest.raises(TypeError, match=msg): - tdi - other - with pytest.raises(TypeError, match=msg): - other - tdi + msg = "Addition/subtraction of integers" + assert_invalid_addsub_type(tdarr, other, msg) # ------------------------------------------------------------------ # Operations with timedelta-like others @@ -1913,7 +1894,6 @@ def test_td64arr_mul_td64arr_raises(self, box_with_array): # ------------------------------------------------------------------ # Operations with numeric others - @pytest.mark.parametrize("one", [1, np.array(1), 1.0, np.array(1.0)]) def test_td64arr_mul_numeric_scalar(self, box_with_array, one): # GH#4521 # divide/multiply by integers @@ -1952,33 +1932,18 @@ def test_td64arr_div_numeric_scalar(self, box_with_array, two): with pytest.raises(TypeError, match="Cannot divide"): two / tdser - @pytest.mark.parametrize( - "dtype", - [ - "int64", - "int32", - "int16", - "uint64", - "uint32", - "uint16", - "uint8", - "float64", - "float32", - "float16", - ], - ) @pytest.mark.parametrize( "vector", [np.array([20, 30, 40]), pd.Index([20, 30, 40]), Series([20, 30, 40])], ids=lambda x: type(x).__name__, ) - def test_td64arr_rmul_numeric_array(self, box_with_array, vector, dtype): + def test_td64arr_rmul_numeric_array(self, box_with_array, vector, any_real_dtype): # GH#4521 # divide/multiply by integers xbox = get_upcast_box(box_with_array, vector) tdser = pd.Series(["59 Days", "59 Days", "NaT"], dtype="m8[ns]") - vector = vector.astype(dtype) + vector = vector.astype(any_real_dtype) expected = Series(["1180 Days", "1770 Days", "NaT"], dtype="timedelta64[ns]") @@ -1991,32 +1956,19 @@ def test_td64arr_rmul_numeric_array(self, box_with_array, vector, dtype): result = vector * tdser tm.assert_equal(result, expected) - @pytest.mark.parametrize( - "dtype", - [ - "int64", - "int32", - "int16", - "uint64", - "uint32", - "uint16", - "uint8", - "float64", - "float32", - "float16", - ], - ) @pytest.mark.parametrize( "vector", [np.array([20, 30, 40]), pd.Index([20, 30, 40]), Series([20, 30, 40])], ids=lambda x: type(x).__name__, ) - def test_td64arr_div_numeric_array(self, box_with_array, vector, dtype): + def test_td64arr_div_numeric_array(self, box_with_array, vector, any_real_dtype): # GH#4521 # divide/multiply by integers xbox = get_upcast_box(box_with_array, vector) + tdser = pd.Series(["59 Days", "59 Days", "NaT"], dtype="m8[ns]") - vector = vector.astype(dtype) + vector = vector.astype(any_real_dtype) + expected = Series(["2.95D", "1D 23H 12m", "NaT"], dtype="timedelta64[ns]") tdser = tm.box_expected(tdser, box_with_array) diff --git a/pandas/tests/indexes/timedeltas/test_arithmetic.py b/pandas/tests/indexes/timedeltas/test_arithmetic.py deleted file mode 100644 index 4f9f8341cb1e5..0000000000000 --- a/pandas/tests/indexes/timedeltas/test_arithmetic.py +++ /dev/null @@ -1,156 +0,0 @@ -from datetime import timedelta - -import numpy as np -import pytest - -from pandas.errors import NullFrequencyError - -import pandas as pd -from pandas import Timedelta, TimedeltaIndex, timedelta_range -import pandas.util.testing as tm - - -@pytest.fixture( - params=[ - pd.offsets.Hour(2), - timedelta(hours=2), - np.timedelta64(2, "h"), - Timedelta(hours=2), - ], - ids=str, -) -def delta(request): - # Several ways of representing two hours - return request.param - - -@pytest.fixture(params=["B", "D"]) -def freq(request): - return request.param - - -class TestTimedeltaIndexArithmetic: - # Addition and Subtraction Operations - - # ------------------------------------------------------------- - # TimedeltaIndex.shift is used by __add__/__sub__ - - def test_tdi_shift_empty(self): - # GH#9903 - idx = pd.TimedeltaIndex([], name="xxx") - tm.assert_index_equal(idx.shift(0, freq="H"), idx) - tm.assert_index_equal(idx.shift(3, freq="H"), idx) - - def test_tdi_shift_hours(self): - # GH#9903 - idx = pd.TimedeltaIndex(["5 hours", "6 hours", "9 hours"], name="xxx") - tm.assert_index_equal(idx.shift(0, freq="H"), idx) - exp = pd.TimedeltaIndex(["8 hours", "9 hours", "12 hours"], name="xxx") - tm.assert_index_equal(idx.shift(3, freq="H"), exp) - exp = pd.TimedeltaIndex(["2 hours", "3 hours", "6 hours"], name="xxx") - tm.assert_index_equal(idx.shift(-3, freq="H"), exp) - - def test_tdi_shift_minutes(self): - # GH#9903 - idx = pd.TimedeltaIndex(["5 hours", "6 hours", "9 hours"], name="xxx") - tm.assert_index_equal(idx.shift(0, freq="T"), idx) - exp = pd.TimedeltaIndex(["05:03:00", "06:03:00", "9:03:00"], name="xxx") - tm.assert_index_equal(idx.shift(3, freq="T"), exp) - exp = pd.TimedeltaIndex(["04:57:00", "05:57:00", "8:57:00"], name="xxx") - tm.assert_index_equal(idx.shift(-3, freq="T"), exp) - - def test_tdi_shift_int(self): - # GH#8083 - trange = pd.to_timedelta(range(5), unit="d") + pd.offsets.Hour(1) - result = trange.shift(1) - expected = TimedeltaIndex( - [ - "1 days 01:00:00", - "2 days 01:00:00", - "3 days 01:00:00", - "4 days 01:00:00", - "5 days 01:00:00", - ], - freq="D", - ) - tm.assert_index_equal(result, expected) - - def test_tdi_shift_nonstandard_freq(self): - # GH#8083 - trange = pd.to_timedelta(range(5), unit="d") + pd.offsets.Hour(1) - result = trange.shift(3, freq="2D 1s") - expected = TimedeltaIndex( - [ - "6 days 01:00:03", - "7 days 01:00:03", - "8 days 01:00:03", - "9 days 01:00:03", - "10 days 01:00:03", - ], - freq="D", - ) - tm.assert_index_equal(result, expected) - - def test_shift_no_freq(self): - # GH#19147 - tdi = TimedeltaIndex(["1 days 01:00:00", "2 days 01:00:00"], freq=None) - with pytest.raises(NullFrequencyError): - tdi.shift(2) - - # ------------------------------------------------------------- - # Binary operations TimedeltaIndex and timedelta-like - # Note: add and sub are tested in tests.test_arithmetic, in-place - # tests are kept here because their behavior is Index-specific - - def test_tdi_iadd_timedeltalike(self, delta): - # only test adding/sub offsets as + is now numeric - rng = timedelta_range("1 days", "10 days") - expected = timedelta_range("1 days 02:00:00", "10 days 02:00:00", freq="D") - rng += delta - tm.assert_index_equal(rng, expected) - - def test_tdi_isub_timedeltalike(self, delta): - # only test adding/sub offsets as - is now numeric - rng = timedelta_range("1 days", "10 days") - expected = timedelta_range("0 days 22:00:00", "9 days 22:00:00") - rng -= delta - tm.assert_index_equal(rng, expected) - - # ------------------------------------------------------------- - - def test_tdi_ops_attributes(self): - rng = timedelta_range("2 days", periods=5, freq="2D", name="x") - - result = rng + 1 * rng.freq - exp = timedelta_range("4 days", periods=5, freq="2D", name="x") - tm.assert_index_equal(result, exp) - assert result.freq == "2D" - - result = rng - 2 * rng.freq - exp = timedelta_range("-2 days", periods=5, freq="2D", name="x") - tm.assert_index_equal(result, exp) - assert result.freq == "2D" - - result = rng * 2 - exp = timedelta_range("4 days", periods=5, freq="4D", name="x") - tm.assert_index_equal(result, exp) - assert result.freq == "4D" - - result = rng / 2 - exp = timedelta_range("1 days", periods=5, freq="D", name="x") - tm.assert_index_equal(result, exp) - assert result.freq == "D" - - result = -rng - exp = timedelta_range("-2 days", periods=5, freq="-2D", name="x") - tm.assert_index_equal(result, exp) - assert result.freq == "-2D" - - rng = pd.timedelta_range("-2 days", periods=5, freq="D", name="x") - - result = abs(rng) - exp = TimedeltaIndex( - ["2 days", "1 days", "0 days", "1 days", "2 days"], name="x" - ) - tm.assert_index_equal(result, exp) - assert result.freq is None diff --git a/pandas/tests/indexes/timedeltas/test_shift.py b/pandas/tests/indexes/timedeltas/test_shift.py new file mode 100644 index 0000000000000..048b29c0da501 --- /dev/null +++ b/pandas/tests/indexes/timedeltas/test_shift.py @@ -0,0 +1,75 @@ +import pytest + +from pandas.errors import NullFrequencyError + +import pandas as pd +from pandas import TimedeltaIndex +import pandas.util.testing as tm + + +class TestTimedeltaIndexShift: + + # ------------------------------------------------------------- + # TimedeltaIndex.shift is used by __add__/__sub__ + + def test_tdi_shift_empty(self): + # GH#9903 + idx = pd.TimedeltaIndex([], name="xxx") + tm.assert_index_equal(idx.shift(0, freq="H"), idx) + tm.assert_index_equal(idx.shift(3, freq="H"), idx) + + def test_tdi_shift_hours(self): + # GH#9903 + idx = pd.TimedeltaIndex(["5 hours", "6 hours", "9 hours"], name="xxx") + tm.assert_index_equal(idx.shift(0, freq="H"), idx) + exp = pd.TimedeltaIndex(["8 hours", "9 hours", "12 hours"], name="xxx") + tm.assert_index_equal(idx.shift(3, freq="H"), exp) + exp = pd.TimedeltaIndex(["2 hours", "3 hours", "6 hours"], name="xxx") + tm.assert_index_equal(idx.shift(-3, freq="H"), exp) + + def test_tdi_shift_minutes(self): + # GH#9903 + idx = pd.TimedeltaIndex(["5 hours", "6 hours", "9 hours"], name="xxx") + tm.assert_index_equal(idx.shift(0, freq="T"), idx) + exp = pd.TimedeltaIndex(["05:03:00", "06:03:00", "9:03:00"], name="xxx") + tm.assert_index_equal(idx.shift(3, freq="T"), exp) + exp = pd.TimedeltaIndex(["04:57:00", "05:57:00", "8:57:00"], name="xxx") + tm.assert_index_equal(idx.shift(-3, freq="T"), exp) + + def test_tdi_shift_int(self): + # GH#8083 + trange = pd.to_timedelta(range(5), unit="d") + pd.offsets.Hour(1) + result = trange.shift(1) + expected = TimedeltaIndex( + [ + "1 days 01:00:00", + "2 days 01:00:00", + "3 days 01:00:00", + "4 days 01:00:00", + "5 days 01:00:00", + ], + freq="D", + ) + tm.assert_index_equal(result, expected) + + def test_tdi_shift_nonstandard_freq(self): + # GH#8083 + trange = pd.to_timedelta(range(5), unit="d") + pd.offsets.Hour(1) + result = trange.shift(3, freq="2D 1s") + expected = TimedeltaIndex( + [ + "6 days 01:00:03", + "7 days 01:00:03", + "8 days 01:00:03", + "9 days 01:00:03", + "10 days 01:00:03", + ], + freq="D", + ) + tm.assert_index_equal(result, expected) + + def test_shift_no_freq(self): + # GH#19147 + tdi = TimedeltaIndex(["1 days 01:00:00", "2 days 01:00:00"], freq=None) + with pytest.raises(NullFrequencyError): + tdi.shift(2)