diff --git a/doc/source/whatsnew/v1.1.0.rst b/doc/source/whatsnew/v1.1.0.rst index 5c39377899a20..6fab4da0921cf 100644 --- a/doc/source/whatsnew/v1.1.0.rst +++ b/doc/source/whatsnew/v1.1.0.rst @@ -400,7 +400,7 @@ Timezones ^^^^^^^^^ - Bug in :func:`to_datetime` with ``infer_datetime_format=True`` where timezone names (e.g. ``UTC``) would not be parsed correctly (:issue:`33133`) -- +- Bug in :func:`DatetimeIndex.astype` with a ``datetime64[ns, tz]`` dtype where the data was not localized to UTC first (:issue:`33401`) Numeric diff --git a/pandas/core/arrays/datetimes.py b/pandas/core/arrays/datetimes.py index b9f9edcebad5b..8907e02444131 100644 --- a/pandas/core/arrays/datetimes.py +++ b/pandas/core/arrays/datetimes.py @@ -586,7 +586,8 @@ def astype(self, dtype, copy=True): # GH#18951: datetime64_ns dtype but not equal means different tz new_tz = getattr(dtype, "tz", None) if getattr(self.dtype, "tz", None) is None: - return self.tz_localize(new_tz) + # GH 33401: Match the behavior of Series.astype + return self.tz_localize(timezones.UTC).tz_convert(new_tz) result = self.tz_convert(new_tz) if copy: result = result.copy() diff --git a/pandas/tests/indexes/datetimes/test_astype.py b/pandas/tests/indexes/datetimes/test_astype.py index 34169a670c169..c0c60291aac3e 100644 --- a/pandas/tests/indexes/datetimes/test_astype.py +++ b/pandas/tests/indexes/datetimes/test_astype.py @@ -90,9 +90,14 @@ def test_astype_with_tz(self): tm.assert_index_equal(result, expected) # GH 18951: tz-naive to tz-aware + # GH 33401: Match the behavior of Series.astype idx = date_range("20170101", periods=4) result = idx.astype("datetime64[ns, US/Eastern]") - expected = date_range("20170101", periods=4, tz="US/Eastern") + expected = ( + date_range("20170101", periods=4) + .tz_localize("UTC") + .tz_convert("US/Eastern") + ) tm.assert_index_equal(result, expected) def test_astype_str_compat(self): @@ -321,3 +326,13 @@ def test_astype_array_fallback(self, tz): result = obj._data.astype(bool) expected = np.array([True, True]) tm.assert_numpy_array_equal(result, expected) + + def test_astype_tz_dtype_utc_localization(self): + # GH 33401 + result = pd.Index([pd.Timestamp("2020-01-01 15:00")]).astype( + "datetime64[ns, US/Eastern]" + ) + expected = pd.Index([pd.Timestamp("2020-01-01 10:00")]).tz_localize( + "US/Eastern" + ) + tm.assert_index_equal(result, expected)