diff --git a/doc/source/whatsnew/v2.0.2.rst b/doc/source/whatsnew/v2.0.2.rst index e791e2a58ba5b..843af0bff43a9 100644 --- a/doc/source/whatsnew/v2.0.2.rst +++ b/doc/source/whatsnew/v2.0.2.rst @@ -28,6 +28,7 @@ Bug fixes - Bug in :func:`api.interchange.from_dataframe` was raising ``IndexError`` on empty categorical data (:issue:`53077`) - Bug in :func:`api.interchange.from_dataframe` was returning :class:`DataFrame`'s of incorrect sizes when called on slices (:issue:`52824`) - Bug in :func:`api.interchange.from_dataframe` was unnecessarily raising on bitmasks (:issue:`49888`) +- Bug in :func:`merge` when merging on datetime columns on different resolutions (:issue:`53200`) - Bug in :meth:`DataFrame.convert_dtypes` ignores ``convert_*`` keywords when set to False ``dtype_backend="pyarrow"`` (:issue:`52872`) - Bug in :meth:`Series.describe` treating pyarrow-backed timestamps and timedeltas as categorical data (:issue:`53001`) - Bug in :meth:`pd.array` raising for ``NumPy`` array and ``pa.large_string`` or ``pa.large_binary`` (:issue:`52590`) diff --git a/pandas/core/reshape/merge.py b/pandas/core/reshape/merge.py index bb01d551628d3..a0207d492023f 100644 --- a/pandas/core/reshape/merge.py +++ b/pandas/core/reshape/merge.py @@ -1401,6 +1401,12 @@ def _maybe_coerce_merge_keys(self) -> None: rk.dtype, DatetimeTZDtype ): raise ValueError(msg) + elif ( + isinstance(lk.dtype, DatetimeTZDtype) + and isinstance(rk.dtype, DatetimeTZDtype) + ) or (lk.dtype.kind == "M" and rk.dtype.kind == "M"): + # allows datetime with different resolutions + continue elif lk_is_object and rk_is_object: continue @@ -2355,7 +2361,7 @@ def _factorize_keys( if isinstance(lk.dtype, DatetimeTZDtype) and isinstance(rk.dtype, DatetimeTZDtype): # Extract the ndarray (UTC-localized) values # Note: we dont need the dtypes to match, as these can still be compared - # TODO(non-nano): need to make sure resolutions match + lk, rk = cast("DatetimeArray", lk)._ensure_matching_resos(rk) lk = cast("DatetimeArray", lk)._ndarray rk = cast("DatetimeArray", rk)._ndarray diff --git a/pandas/tests/reshape/merge/test_merge.py b/pandas/tests/reshape/merge/test_merge.py index a7b007f043da9..a4d1bfbaa34be 100644 --- a/pandas/tests/reshape/merge/test_merge.py +++ b/pandas/tests/reshape/merge/test_merge.py @@ -7,6 +7,7 @@ import numpy as np import pytest +import pytz from pandas.core.dtypes.common import ( is_categorical_dtype, @@ -2750,3 +2751,26 @@ def test_merge_arrow_and_numpy_dtypes(dtype): result = df2.merge(df) expected = df2.copy() tm.assert_frame_equal(result, expected) + + +@pytest.mark.parametrize("tzinfo", [None, pytz.timezone("America/Chicago")]) +def test_merge_datetime_different_resolution(tzinfo): + # https://github.com/pandas-dev/pandas/issues/53200 + df1 = DataFrame( + { + "t": [pd.Timestamp(2023, 5, 12, tzinfo=tzinfo, unit="ns")], + "a": [1], + } + ) + df2 = df1.copy() + df2["t"] = df2["t"].dt.as_unit("s") + + expected = DataFrame( + { + "t": [pd.Timestamp(2023, 5, 12, tzinfo=tzinfo)], + "a_x": [1], + "a_y": [1], + } + ) + result = df1.merge(df2, on="t") + tm.assert_frame_equal(result, expected)