diff --git a/pandas/_libs/tslibs/timedeltas.pyx b/pandas/_libs/tslibs/timedeltas.pyx index f5efd6922d6d7..1cd875d4ce41d 100644 --- a/pandas/_libs/tslibs/timedeltas.pyx +++ b/pandas/_libs/tslibs/timedeltas.pyx @@ -2026,6 +2026,19 @@ class Timedelta(_Timedelta): "milliseconds, microseconds, nanoseconds]" ) + if ( + unit is not None + and not (is_float_object(value) or is_integer_object(value)) + ): + # GH#53198 + warnings.warn( + "The 'unit' keyword is only used when the Timedelta input is " + f"an integer or float, not {type(value).__name__}. " + "To specify the storage unit of the output use `td.as_unit(unit)`", + UserWarning, + stacklevel=find_stack_level(), + ) + if value is _no_input: if not len(kwargs): raise ValueError("cannot construct a Timedelta without a " diff --git a/pandas/_libs/tslibs/timestamps.pyx b/pandas/_libs/tslibs/timestamps.pyx index 2f0c5fa9ef18e..5d6c7b53f918c 100644 --- a/pandas/_libs/tslibs/timestamps.pyx +++ b/pandas/_libs/tslibs/timestamps.pyx @@ -67,6 +67,7 @@ from pandas._libs.tslibs.dtypes cimport ( ) from pandas._libs.tslibs.util cimport ( is_array, + is_float_object, is_integer_object, ) @@ -2654,6 +2655,19 @@ class Timestamp(_Timestamp): if hasattr(ts_input, "fold"): ts_input = ts_input.replace(fold=fold) + if ( + unit is not None + and not (is_float_object(ts_input) or is_integer_object(ts_input)) + ): + # GH#53198 + warnings.warn( + "The 'unit' keyword is only used when the Timestamp input is " + f"an integer or float, not {type(ts_input).__name__}. " + "To specify the storage unit of the output use `ts.as_unit(unit)`", + UserWarning, + stacklevel=find_stack_level(), + ) + # GH 30543 if pd.Timestamp already passed, return it # check that only ts_input is passed # checking verbosely, because cython doesn't optimize diff --git a/pandas/core/computation/pytables.py b/pandas/core/computation/pytables.py index d176beb6cde00..ee6ac6584569e 100644 --- a/pandas/core/computation/pytables.py +++ b/pandas/core/computation/pytables.py @@ -17,6 +17,7 @@ import numpy as np +from pandas._libs import lib from pandas._libs.tslibs import ( Timedelta, Timestamp, @@ -227,8 +228,10 @@ def stringify(value): elif kind in ("timedelta64", "timedelta"): if isinstance(conv_val, str): conv_val = Timedelta(conv_val) - else: + elif lib.is_integer(conv_val) or lib.is_float(conv_val): conv_val = Timedelta(conv_val, unit="s") + else: + conv_val = Timedelta(conv_val) conv_val = conv_val.as_unit("ns")._value return TermValue(int(conv_val), conv_val, kind) elif meta == "category": diff --git a/pandas/core/resample.py b/pandas/core/resample.py index 3a4ce952ffdcf..e2251afeac205 100644 --- a/pandas/core/resample.py +++ b/pandas/core/resample.py @@ -2650,7 +2650,7 @@ def _adjust_bin_edges( edges_dti = binner.tz_localize(None) edges_dti = ( edges_dti - + Timedelta(days=1, unit=edges_dti.unit).as_unit(edges_dti.unit) + + Timedelta(days=1).as_unit(edges_dti.unit) - Timedelta(1, unit=edges_dti.unit).as_unit(edges_dti.unit) ) bin_edges = edges_dti.tz_localize(binner.tz).asi8 diff --git a/pandas/core/tools/datetimes.py b/pandas/core/tools/datetimes.py index f353a3f7dc2cb..ce311d0c89b55 100644 --- a/pandas/core/tools/datetimes.py +++ b/pandas/core/tools/datetimes.py @@ -599,7 +599,10 @@ def _adjust_to_origin(arg, origin, unit): # we are going to offset back to unix / epoch time try: - offset = Timestamp(origin, unit=unit) + if lib.is_integer(origin) or lib.is_float(origin): + offset = Timestamp(origin, unit=unit) + else: + offset = Timestamp(origin) except OutOfBoundsDatetime as err: raise OutOfBoundsDatetime(f"origin {origin} is Out of Bounds") from err except ValueError as err: diff --git a/pandas/tests/scalar/timedelta/test_constructors.py b/pandas/tests/scalar/timedelta/test_constructors.py index c9904a318e22d..28614c513e8ef 100644 --- a/pandas/tests/scalar/timedelta/test_constructors.py +++ b/pandas/tests/scalar/timedelta/test_constructors.py @@ -193,11 +193,15 @@ def test_construct_from_td64_with_unit(): # results, and in non-overflow cases is irrelevant GH#46827 obj = np.timedelta64(123456789000000000, "h") + msg = "The 'unit' keyword is only used when the Timedelta input is" + with pytest.raises(OutOfBoundsTimedelta, match="123456789000000000 hours"): - Timedelta(obj, unit="ps") + with tm.assert_produces_warning(UserWarning, match=msg): + Timedelta(obj, unit="ps") with pytest.raises(OutOfBoundsTimedelta, match="123456789000000000 hours"): - Timedelta(obj, unit="ns") + with tm.assert_produces_warning(UserWarning, match=msg): + Timedelta(obj, unit="ns") with pytest.raises(OutOfBoundsTimedelta, match="123456789000000000 hours"): Timedelta(obj) @@ -633,6 +637,9 @@ def test_timedelta_pass_td_and_kwargs_raises(): Timedelta(td, days=2) +@pytest.mark.filterwarnings( + "ignore:The 'unit' keyword is only used when the Timedelta input:UserWarning" +) @pytest.mark.parametrize( "constructor, value, unit", [ diff --git a/pandas/tests/scalar/timedelta/test_timedelta.py b/pandas/tests/scalar/timedelta/test_timedelta.py index d9e42bbaf910d..cf878b1164b3f 100644 --- a/pandas/tests/scalar/timedelta/test_timedelta.py +++ b/pandas/tests/scalar/timedelta/test_timedelta.py @@ -83,7 +83,7 @@ def test_mul_preserves_reso(self, td, unit): def test_cmp_cross_reso(self, td): # numpy gets this wrong because of silent overflow - other = Timedelta(days=106751, unit="ns") + other = Timedelta(days=106751) assert other < td assert td > other assert not other == td diff --git a/pandas/tests/scalar/timestamp/test_constructors.py b/pandas/tests/scalar/timestamp/test_constructors.py index 912875f818eb6..d9defd47b6620 100644 --- a/pandas/tests/scalar/timestamp/test_constructors.py +++ b/pandas/tests/scalar/timestamp/test_constructors.py @@ -1076,7 +1076,9 @@ def test_timestamp_nano_range(nano): def test_non_nano_value(): # https://github.com/pandas-dev/pandas/issues/49076 - result = Timestamp("1800-01-01", unit="s").value + msg = "The 'unit' keyword is only used when" + with tm.assert_produces_warning(UserWarning, match=msg): + result = Timestamp("1800-01-01", unit="s").value # `.value` shows nanoseconds, even though unit is 's' assert result == -5364662400000000000