From 52a709474a2d1486621f991195de47913b78f1a9 Mon Sep 17 00:00:00 2001 From: Brock Date: Sat, 25 Oct 2025 14:10:57 -0700 Subject: [PATCH] Warn on no-op rounding for datetimelike dtypes --- pandas/core/frame.py | 9 +++++++++ pandas/core/internals/blocks.py | 9 +++++++++ pandas/tests/frame/methods/test_round.py | 10 +++++++--- pandas/tests/tools/test_to_datetime.py | 2 +- 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/pandas/core/frame.py b/pandas/core/frame.py index d4ff1bc4f35ac..fe28c459c76b5 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -11521,6 +11521,15 @@ def _dict_round(df: DataFrame, decimals) -> Iterator[Series]: def _series_round(ser: Series, decimals: int) -> Series: if is_integer_dtype(ser.dtype) or is_float_dtype(ser.dtype): return ser.round(decimals) + elif isinstance(ser._values, (DatetimeArray, TimedeltaArray, PeriodArray)): + # GH#57781 + # TODO: also the ArrowDtype analogues? + warnings.warn( + "obj.round has no effect with datetime, timedelta, " + "or period dtypes. Use obj.dt.round(...) instead.", + UserWarning, + stacklevel=find_stack_level(), + ) return ser nv.validate_round(args, kwargs) diff --git a/pandas/core/internals/blocks.py b/pandas/core/internals/blocks.py index 54b89c3bbe48c..a9ad561cbc393 100644 --- a/pandas/core/internals/blocks.py +++ b/pandas/core/internals/blocks.py @@ -1514,6 +1514,15 @@ def round(self, decimals: int) -> Self: Caller is responsible for validating this """ if not self.is_numeric or self.is_bool: + if isinstance(self.values, (DatetimeArray, TimedeltaArray, PeriodArray)): + # GH#57781 + # TODO: also the ArrowDtype analogues? + warnings.warn( + "obj.round has no effect with datetime, timedelta, " + "or period dtypes. Use obj.dt.round(...) instead.", + UserWarning, + stacklevel=find_stack_level(), + ) return self.copy(deep=False) # TODO: round only defined on BaseMaskedArray # Series also does this, so would need to fix both places diff --git a/pandas/tests/frame/methods/test_round.py b/pandas/tests/frame/methods/test_round.py index a96df27b48d7d..5f2566fefca76 100644 --- a/pandas/tests/frame/methods/test_round.py +++ b/pandas/tests/frame/methods/test_round.py @@ -159,12 +159,16 @@ def test_round_mixed_type(self): "col3": date_range("20111111", periods=4), } ) - tm.assert_frame_equal(df.round(), round_0) - tm.assert_frame_equal(df.round(1), df) + msg = "obj.round has no effect with datetime, timedelta, or period dtypes" + with tm.assert_produces_warning(UserWarning, match=msg): + tm.assert_frame_equal(df.round(), round_0) + with tm.assert_produces_warning(UserWarning, match=msg): + tm.assert_frame_equal(df.round(1), df) tm.assert_frame_equal(df.round({"col1": 1}), df) tm.assert_frame_equal(df.round({"col1": 0}), round_0) tm.assert_frame_equal(df.round({"col1": 0, "col2": 1}), round_0) - tm.assert_frame_equal(df.round({"col3": 1}), df) + with tm.assert_produces_warning(UserWarning, match=msg): + tm.assert_frame_equal(df.round({"col3": 1}), df) def test_round_with_duplicate_columns(self): # GH#11611 diff --git a/pandas/tests/tools/test_to_datetime.py b/pandas/tests/tools/test_to_datetime.py index f59339cacd8d8..c9cda0c4153cb 100644 --- a/pandas/tests/tools/test_to_datetime.py +++ b/pandas/tests/tools/test_to_datetime.py @@ -1963,7 +1963,7 @@ def test_to_datetime_unit_fractional_seconds(self): dtype="M8[ns]", ) # GH20455 argument will incur floating point errors but no premature rounding - result = result.round("ms") + result = result.dt.round("ms") tm.assert_series_equal(result, expected) def test_to_datetime_unit_na_values(self):