diff --git a/pandas/_libs/tslibs/timedeltas.pyx b/pandas/_libs/tslibs/timedeltas.pyx index 06334cd6f3081..1e01ad9246aae 100644 --- a/pandas/_libs/tslibs/timedeltas.pyx +++ b/pandas/_libs/tslibs/timedeltas.pyx @@ -2114,7 +2114,7 @@ class Timedelta(_Timedelta): int(ns) + int(us * 1_000) + int(ms * 1_000_000) - + seconds + + seconds, "ns" ) except OverflowError as err: # GH#55503 @@ -2124,6 +2124,13 @@ class Timedelta(_Timedelta): ) raise OutOfBoundsTimedelta(msg) from err + if ( + "nanoseconds" not in kwargs + and cnp.get_timedelta64_value(value) % 1000 == 0 + ): + # If possible, give a microsecond unit + value = value.astype("m8[us]") + disallow_ambiguous_unit(unit) cdef: diff --git a/pandas/conftest.py b/pandas/conftest.py index a76e66dbeaca1..be48dc34e6c71 100644 --- a/pandas/conftest.py +++ b/pandas/conftest.py @@ -938,7 +938,7 @@ def rand_series_with_duplicate_datetimeindex() -> Series: Timestamp("2011-01-01", tz="US/Eastern").as_unit("s"), DatetimeTZDtype(unit="s", tz="US/Eastern"), ), - (Timedelta(seconds=500), "timedelta64[ns]"), + (Timedelta(seconds=500), "timedelta64[us]"), ] ) def ea_scalar_and_dtype(request): diff --git a/pandas/tests/arithmetic/test_numeric.py b/pandas/tests/arithmetic/test_numeric.py index d1db440a8f609..5878246126d61 100644 --- a/pandas/tests/arithmetic/test_numeric.py +++ b/pandas/tests/arithmetic/test_numeric.py @@ -223,8 +223,8 @@ def test_div_td64arr(self, left, box_cls): @pytest.mark.parametrize( "scalar_td", [ - Timedelta(days=1), - Timedelta(days=1).to_timedelta64(), + Timedelta(days=1).as_unit("ns"), + Timedelta(days=1).as_unit("ns").to_timedelta64(), Timedelta(days=1).to_pytimedelta(), Timedelta(days=1).to_timedelta64().astype("timedelta64[s]"), Timedelta(days=1).to_timedelta64().astype("timedelta64[ms]"), @@ -235,7 +235,9 @@ def test_numeric_arr_mul_tdscalar(self, scalar_td, numeric_idx, box_with_array): # GH#19333 box = box_with_array index = numeric_idx - expected = TimedeltaIndex([Timedelta(days=n) for n in range(len(index))]) + expected = TimedeltaIndex( + [Timedelta(days=n) for n in range(len(index))], dtype="m8[ns]" + ) if isinstance(scalar_td, np.timedelta64): dtype = scalar_td.dtype expected = expected.astype(dtype) @@ -254,9 +256,9 @@ def test_numeric_arr_mul_tdscalar(self, scalar_td, numeric_idx, box_with_array): @pytest.mark.parametrize( "scalar_td", [ - Timedelta(days=1), - Timedelta(days=1).to_timedelta64(), - Timedelta(days=1).to_pytimedelta(), + Timedelta(days=1).as_unit("ns"), + Timedelta(days=1).as_unit("ns").to_timedelta64(), + Timedelta(days=1).as_unit("ns").to_pytimedelta(), ], ids=lambda x: type(x).__name__, ) diff --git a/pandas/tests/arithmetic/test_timedelta64.py b/pandas/tests/arithmetic/test_timedelta64.py index 491a509e2af31..e2f26bdcf43da 100644 --- a/pandas/tests/arithmetic/test_timedelta64.py +++ b/pandas/tests/arithmetic/test_timedelta64.py @@ -1255,8 +1255,14 @@ def test_td64arr_add_sub_tdi(self, box_with_array, names): tdi = TimedeltaIndex(["0 days", "1 day"], name=names[1]) tdi = np.array(tdi) if box in [tm.to_array, pd.array] else tdi - ser = Series([Timedelta(hours=3), Timedelta(hours=4)], name=names[0]) - expected = Series([Timedelta(hours=3), Timedelta(days=1, hours=4)], name=exname) + ser = Series( + [Timedelta(hours=3), Timedelta(hours=4)], name=names[0], dtype="m8[ns]" + ) + expected = Series( + [Timedelta(hours=3), Timedelta(days=1, hours=4)], + name=exname, + dtype="m8[ns]", + ) ser = tm.box_expected(ser, box) expected = tm.box_expected(expected, box) @@ -1270,7 +1276,9 @@ def test_td64arr_add_sub_tdi(self, box_with_array, names): assert_dtype(result, "timedelta64[ns]") expected = Series( - [Timedelta(hours=-3), Timedelta(days=1, hours=-4)], name=exname + [Timedelta(hours=-3), Timedelta(days=1, hours=-4)], + name=exname, + dtype="m8[ns]", ) expected = tm.box_expected(expected, box) @@ -1666,7 +1674,7 @@ def test_td64arr_mul_masked(self, dtype, box_with_array): other = Series(np.arange(5), dtype=dtype) other = tm.box_expected(other, box_with_array) - expected = Series([Timedelta(hours=n**2) for n in range(5)]) + expected = Series([Timedelta(hours=n**2) for n in range(5)], dtype="m8[ns]") expected = tm.box_expected(expected, box_with_array) if dtype == "int64[pyarrow]": expected = expected.astype("duration[ns][pyarrow]") diff --git a/pandas/tests/arrays/timedeltas/test_reductions.py b/pandas/tests/arrays/timedeltas/test_reductions.py index e1cafbcfcb6ed..ed7562c69c3b8 100644 --- a/pandas/tests/arrays/timedeltas/test_reductions.py +++ b/pandas/tests/arrays/timedeltas/test_reductions.py @@ -109,7 +109,9 @@ def test_sum_2d_skipna_false(self): assert result is pd.NaT result = tda.sum(axis=0, skipna=False) - expected = pd.TimedeltaIndex([Timedelta(seconds=12), pd.NaT])._values + expected = pd.TimedeltaIndex( + [Timedelta(seconds=12), pd.NaT], dtype="m8[ns]" + )._values tm.assert_timedelta_array_equal(result, expected) result = tda.sum(axis=1, skipna=False) @@ -119,7 +121,8 @@ def test_sum_2d_skipna_false(self): Timedelta(seconds=5), Timedelta(seconds=9), pd.NaT, - ] + ], + dtype="m8[ns]", )._values tm.assert_timedelta_array_equal(result, expected) diff --git a/pandas/tests/base/test_value_counts.py b/pandas/tests/base/test_value_counts.py index d60555246ca6a..d61fb279b789b 100644 --- a/pandas/tests/base/test_value_counts.py +++ b/pandas/tests/base/test_value_counts.py @@ -359,7 +359,7 @@ def test_value_counts_object_inference_deprecated(): + [Timestamp("2016-01-02")] + [Timestamp("2016-01-01") + Timedelta(days=i) for i in range(1, 5)] ), - DatetimeIndex(pd.date_range("2016-01-01", periods=5, freq="D", unit="ns")), + DatetimeIndex(pd.date_range("2016-01-01", periods=5, freq="D", unit="us")), ], [ TimedeltaIndex( @@ -367,7 +367,7 @@ def test_value_counts_object_inference_deprecated(): + [Timedelta(hours=1)] + [Timedelta(hours=i) for i in range(1, 5)], ), - TimedeltaIndex(pd.timedelta_range(Timedelta(0), periods=5, freq="h")), + TimedeltaIndex(pd.timedelta_range(Timedelta(hours=0), periods=5, freq="h")), ], [ DatetimeIndex( diff --git a/pandas/tests/indexes/datetimes/test_arithmetic.py b/pandas/tests/indexes/datetimes/test_arithmetic.py index 25164349babe9..82ecb84220783 100644 --- a/pandas/tests/indexes/datetimes/test_arithmetic.py +++ b/pandas/tests/indexes/datetimes/test_arithmetic.py @@ -55,7 +55,8 @@ def test_sub_datetime_preserves_freq_across_dst(self): Timedelta(days=1), Timedelta(days=2), Timedelta(days=2, hours=23), - ] + ], + dtype="m8[ns]", ) tm.assert_index_equal(res, expected) assert res.freq == expected.freq diff --git a/pandas/tests/indexes/timedeltas/test_constructors.py b/pandas/tests/indexes/timedeltas/test_constructors.py index 1dc64ae5c720b..1a2f7053a98ab 100644 --- a/pandas/tests/indexes/timedeltas/test_constructors.py +++ b/pandas/tests/indexes/timedeltas/test_constructors.py @@ -104,7 +104,9 @@ def test_float64_ns_rounded(self): def test_float64_unit_conversion(self): # GH#23539 tdi = to_timedelta([1.5, 2.25], unit="D") - expected = TimedeltaIndex([Timedelta(days=1.5), Timedelta(days=2.25)]) + expected = TimedeltaIndex( + [Timedelta(days=1.5), Timedelta(days=2.25)], dtype="m8[ns]" + ) tm.assert_index_equal(tdi, expected) def test_construction_base_constructor(self): diff --git a/pandas/tests/scalar/timedelta/methods/test_as_unit.py b/pandas/tests/scalar/timedelta/methods/test_as_unit.py index 8660141e5a537..005c4d1d5fa99 100644 --- a/pandas/tests/scalar/timedelta/methods/test_as_unit.py +++ b/pandas/tests/scalar/timedelta/methods/test_as_unit.py @@ -10,29 +10,29 @@ class TestAsUnit: def test_as_unit(self): td = Timedelta(days=1) - assert td.as_unit("ns") is td + assert td.as_unit("us") is td - res = td.as_unit("us") - assert res._value == td._value // 1000 - assert res._creso == NpyDatetimeUnit.NPY_FR_us.value + res = td.as_unit("ns") + assert res._value == td._value * 1000 + assert res._creso == NpyDatetimeUnit.NPY_FR_ns.value - rt = res.as_unit("ns") + rt = res.as_unit("us") assert rt._value == td._value assert rt._creso == td._creso res = td.as_unit("ms") - assert res._value == td._value // 1_000_000 + assert res._value == td._value // 1_000 assert res._creso == NpyDatetimeUnit.NPY_FR_ms.value - rt = res.as_unit("ns") + rt = res.as_unit("us") assert rt._value == td._value assert rt._creso == td._creso res = td.as_unit("s") - assert res._value == td._value // 1_000_000_000 + assert res._value == td._value // 1_000_000 assert res._creso == NpyDatetimeUnit.NPY_FR_s.value - rt = res.as_unit("ns") + rt = res.as_unit("us") assert rt._value == td._value assert rt._creso == td._creso diff --git a/pandas/tests/scalar/timedelta/test_arithmetic.py b/pandas/tests/scalar/timedelta/test_arithmetic.py index 09bf7ec70d54f..20e46bbbe0803 100644 --- a/pandas/tests/scalar/timedelta/test_arithmetic.py +++ b/pandas/tests/scalar/timedelta/test_arithmetic.py @@ -818,11 +818,11 @@ def test_mod_numeric(self): assert isinstance(result, Timedelta) assert result == Timedelta(0) - result = td % 1e12 + result = td % 1e9 assert isinstance(result, Timedelta) assert result == Timedelta(minutes=3, seconds=20) - result = td % int(1e12) + result = td % int(1e9) assert isinstance(result, Timedelta) assert result == Timedelta(minutes=3, seconds=20) @@ -876,8 +876,8 @@ def test_divmod_numeric(self): # GH#19365 td = Timedelta(days=2, hours=6) - result = divmod(td, 53 * 3600 * 1e9) - assert result[0] == Timedelta(1, unit="ns") + result = divmod(td, 53 * 3600 * 1e6) + assert result[0] == Timedelta(1, unit="us").as_unit("us") assert isinstance(result[1], Timedelta) assert result[1] == Timedelta(hours=1) diff --git a/pandas/tests/scalar/timedelta/test_constructors.py b/pandas/tests/scalar/timedelta/test_constructors.py index ebf6ed44da8ef..d386ef576ddef 100644 --- a/pandas/tests/scalar/timedelta/test_constructors.py +++ b/pandas/tests/scalar/timedelta/test_constructors.py @@ -19,6 +19,21 @@ import pandas._testing as tm +class TestTimedeltaConstructorKeywordBased: + # Tests for constructing a Timedelta from keywords like the pytimedelta + # base class + def test_nanosecond_keyword(self): + # GH#63216 + td = Timedelta(nanoseconds=1000) + assert td.unit == "ns" + + def test_noninteger_microseconds(self): + # GH#63216 + td = Timedelta(microseconds=1.5) + assert td.unit == "ns" + assert td == Timedelta(nanoseconds=1500) + + class TestTimedeltaConstructorUnitKeyword: @pytest.mark.parametrize("unit", ["Y", "y", "M"]) def test_unit_m_y_raises(self, unit): @@ -272,14 +287,14 @@ def test_construction(): assert Timedelta(10, unit="D")._value == expected assert Timedelta(10.0, unit="D")._value == expected assert Timedelta("10 days")._value == expected // 1000 - assert Timedelta(days=10)._value == expected - assert Timedelta(days=10.0)._value == expected + assert Timedelta(days=10)._value == expected // 1000 + assert Timedelta(days=10.0)._value == expected // 1000 expected += np.timedelta64(10, "s").astype("m8[ns]").view("i8") assert Timedelta("10 days 00:00:10")._value == expected // 1000 - assert Timedelta(days=10, seconds=10)._value == expected - assert Timedelta(days=10, milliseconds=10 * 1000)._value == expected - assert Timedelta(days=10, microseconds=10 * 1000 * 1000)._value == expected + assert Timedelta(days=10, seconds=10)._value == expected // 1000 + assert Timedelta(days=10, milliseconds=10 * 1000)._value == expected // 1000 + assert Timedelta(days=10, microseconds=10 * 1000 * 1000)._value == expected // 1000 # rounding cases assert Timedelta(82739999850000)._value == 82739999850000 @@ -411,7 +426,7 @@ def test_construction(): def test_td_construction_with_np_dtypes(npdtype, item): # GH#8757: test construction with np dtypes pykwarg, npkwarg = item - expected = np.timedelta64(1, npkwarg).astype("m8[ns]").view("i8") + expected = np.timedelta64(1, npkwarg).astype("m8[us]").view("i8") assert Timedelta(**{pykwarg: npdtype(1)})._value == expected diff --git a/pandas/tests/scalar/timedelta/test_timedelta.py b/pandas/tests/scalar/timedelta/test_timedelta.py index b66776ff5e564..fe6e9b9e1c7e1 100644 --- a/pandas/tests/scalar/timedelta/test_timedelta.py +++ b/pandas/tests/scalar/timedelta/test_timedelta.py @@ -665,7 +665,7 @@ def test_resolution_deprecated(self): # GH#21344 td = Timedelta(days=4, hours=3) result = td.resolution - assert result == Timedelta(nanoseconds=1) + assert result == Timedelta(microseconds=1) # Check that the attribute is available on the class, mirroring # the stdlib timedelta behavior diff --git a/pandas/tests/tseries/offsets/test_common.py b/pandas/tests/tseries/offsets/test_common.py index 34181f28bb1a0..4f89305bba5e8 100644 --- a/pandas/tests/tseries/offsets/test_common.py +++ b/pandas/tests/tseries/offsets/test_common.py @@ -145,12 +145,12 @@ def test_apply_out_of_range(request, tz_naive_fixture, _offset): elif ( isinstance(tz, tzlocal) and is_platform_windows() - and _offset in (QuarterEnd, BQuarterBegin, BQuarterEnd) + and _offset in (QuarterEnd, BQuarterBegin, BQuarterEnd, FY5253Quarter) ): request.applymarker( pytest.mark.xfail(reason="After GH#49737 t.tzinfo is None on CI") ) - assert str(t.tzinfo) == str(result.tzinfo) + assert str(t.tzinfo) == str(result.tzinfo), (t.tzinfo, result.tzinfo) except OutOfBoundsDatetime: pass