diff --git a/pandas/_libs/tslibs/timestamps.pyi b/pandas/_libs/tslibs/timestamps.pyi index e4be7fda43005..8382fe0274138 100644 --- a/pandas/_libs/tslibs/timestamps.pyi +++ b/pandas/_libs/tslibs/timestamps.pyi @@ -27,6 +27,7 @@ _DatetimeT = TypeVar("_DatetimeT", bound=datetime) def integer_op_not_supported(obj: object) -> TypeError: ... class Timestamp(datetime): + _reso: int min: ClassVar[Timestamp] max: ClassVar[Timestamp] diff --git a/pandas/core/arrays/datetimelike.py b/pandas/core/arrays/datetimelike.py index 848d2f879fbf2..6999d7168622c 100644 --- a/pandas/core/arrays/datetimelike.py +++ b/pandas/core/arrays/datetimelike.py @@ -1209,6 +1209,14 @@ def _sub_datetimelike_scalar(self, other: datetime | np.datetime64): return self - NaT other = Timestamp(other) + + if other._reso != self._reso: + if other._reso < self._reso: + other = other._as_unit(self._unit) + else: + unit = npy_unit_to_abbrev(other._reso) + self = self._as_unit(unit) + return self._sub_datetimelike(other) @final @@ -1221,6 +1229,13 @@ def _sub_datetime_arraylike(self, other): self = cast("DatetimeArray", self) other = ensure_wrapped_if_datetimelike(other) + + if other._reso != self._reso: + if other._reso < self._reso: + other = other._as_unit(self._unit) + else: + self = self._as_unit(other._unit) + return self._sub_datetimelike(other) @final diff --git a/pandas/tests/arrays/test_datetimes.py b/pandas/tests/arrays/test_datetimes.py index 83850c6cd2594..8c2a8df7c2fbd 100644 --- a/pandas/tests/arrays/test_datetimes.py +++ b/pandas/tests/arrays/test_datetimes.py @@ -13,7 +13,10 @@ import pandas as pd import pandas._testing as tm -from pandas.core.arrays import DatetimeArray +from pandas.core.arrays import ( + DatetimeArray, + TimedeltaArray, +) class TestNonNano: @@ -218,6 +221,32 @@ def test_add_mismatched_reso_doesnt_downcast(self): # (so we _could_ downcast to unit="s"), we do not. assert res._unit == "us" + def test_sub_datetimelike_scalar_mismatch(self): + dti = pd.date_range("2016-01-01", periods=3) + dta = dti._data._as_unit("us") + + ts = dta[0]._as_unit("s") + + result = dta - ts + expected = (dti - dti[0])._data._as_unit("us") + assert result.dtype == "m8[us]" + tm.assert_extension_array_equal(result, expected) + + def test_sub_datetime64_reso_mismatch(self): + dti = pd.date_range("2016-01-01", periods=3) + left = dti._data._as_unit("s") + right = left._as_unit("ms") + + result = left - right + exp_values = np.array([0, 0, 0], dtype="m8[ms]") + expected = TimedeltaArray._simple_new( + exp_values, + dtype=exp_values.dtype, + ) + tm.assert_extension_array_equal(result, expected) + result2 = right - left + tm.assert_extension_array_equal(result2, expected) + class TestDatetimeArrayComparisons: # TODO: merge this into tests/arithmetic/test_datetime64 once it is