From c13a567930c24cd898c2c56b7575a786ab340681 Mon Sep 17 00:00:00 2001 From: Brock Date: Mon, 28 Jun 2021 13:54:56 -0700 Subject: [PATCH 1/3] DEPR: treating dt64 as UTC in Timestamp constructor --- doc/source/whatsnew/v1.4.0.rst | 1 + pandas/_libs/tslibs/timestamps.pyx | 13 +++++++++++++ pandas/core/arrays/datetimes.py | 2 ++ .../tests/scalar/timestamp/test_constructors.py | 17 +++++++++++++++++ 4 files changed, 33 insertions(+) diff --git a/doc/source/whatsnew/v1.4.0.rst b/doc/source/whatsnew/v1.4.0.rst index 81545ada63ce5..9b01952f06393 100644 --- a/doc/source/whatsnew/v1.4.0.rst +++ b/doc/source/whatsnew/v1.4.0.rst @@ -97,6 +97,7 @@ Other API changes Deprecations ~~~~~~~~~~~~ - Deprecated :meth:`Index.is_type_compatible` (:issue:`42113`) +- Deprecated treating ``numpy.datetime64`` objects as UTC times when passed to the :class:`Timestamp` constructor along with a timezone. In a future version, these will be treated as wall-times. To retain the old behavior, use ``Timestamp(dt64).tz_localize("UTC").tz_convert(tz)`` (:issue:`??`) - .. --------------------------------------------------------------------------- diff --git a/pandas/_libs/tslibs/timestamps.pyx b/pandas/_libs/tslibs/timestamps.pyx index 88a2706a35aa2..71654675c4825 100644 --- a/pandas/_libs/tslibs/timestamps.pyx +++ b/pandas/_libs/tslibs/timestamps.pyx @@ -1329,6 +1329,19 @@ class Timestamp(_Timestamp): "the tz parameter. Use tz_convert instead.") tzobj = maybe_get_tz(tz) + if tzobj is not None and is_datetime64_object(ts_input): + # In the future we will treat datetime64 as wall-time + # (consistent with DatetimeIndex) + warnings.warn( + "In a future version, when passing a np.datetime64 object and " + "a timezone to Timestamp, the datetime64 will be interpreted " + "as a wall time, not a UTC time. To interpret as a UTC time, " + "use `Timestamp(dt64).tz_localize('UTC').tz_convert(tz)`", + FutureWarning, + stacklevel=1, + ) + # Once this deprecation is enforced, we can do + # return Timestamp(ts_input).tz_localize(tzobj) ts = convert_to_tsobject(ts_input, tzobj, unit, 0, 0, nanosecond or 0) if ts.value == NPY_NAT: diff --git a/pandas/core/arrays/datetimes.py b/pandas/core/arrays/datetimes.py index 92a906e9fd8b0..70cb06349fa37 100644 --- a/pandas/core/arrays/datetimes.py +++ b/pandas/core/arrays/datetimes.py @@ -509,6 +509,8 @@ def _check_compatible_with(self, other, setitem: bool = False): # Descriptive Properties def _box_func(self, x) -> Timestamp | NaTType: + if isinstance(x, np.datetime64): + x = np.int64(x) ts = Timestamp(x, tz=self.tz) # Non-overlapping identity check (left operand type: "Timestamp", # right operand type: "NaTType") diff --git a/pandas/tests/scalar/timestamp/test_constructors.py b/pandas/tests/scalar/timestamp/test_constructors.py index 16ce51a88340e..e5190cb348639 100644 --- a/pandas/tests/scalar/timestamp/test_constructors.py +++ b/pandas/tests/scalar/timestamp/test_constructors.py @@ -25,6 +25,23 @@ class TestTimestampConstructors: + def test_constructor_datetime64_with_tz(self): + dt = np.datetime64("1970-01-01 05:00:00") + tzstr = "UTC+05:00" + + msg = "interpreted as a wall time" + with tm.assert_produces_warning(FutureWarning, match=msg): + ts = Timestamp(dt, tz=tzstr) + + # Check that we match the old behavior + alt = Timestamp(dt).tz_localize("UTC").tz_convert(tzstr) + assert ts == alt + + # Check that we *don't* match the future behavior + assert ts.hour != 5 + expected_future = Timestamp(dt).tz_localize(tzstr) + assert ts != expected_future + def test_constructor(self): base_str = "2014-07-01 09:00" base_dt = datetime(2014, 7, 1, 9) From 38b6552ef47abb73da24ce6482cfbb62a4c81f9d Mon Sep 17 00:00:00 2001 From: Brock Date: Mon, 28 Jun 2021 13:56:43 -0700 Subject: [PATCH 2/3] GH refs --- doc/source/whatsnew/v1.4.0.rst | 2 +- pandas/_libs/tslibs/timestamps.pyx | 2 +- pandas/tests/scalar/timestamp/test_constructors.py | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/source/whatsnew/v1.4.0.rst b/doc/source/whatsnew/v1.4.0.rst index 9b01952f06393..af5909bd3374f 100644 --- a/doc/source/whatsnew/v1.4.0.rst +++ b/doc/source/whatsnew/v1.4.0.rst @@ -97,7 +97,7 @@ Other API changes Deprecations ~~~~~~~~~~~~ - Deprecated :meth:`Index.is_type_compatible` (:issue:`42113`) -- Deprecated treating ``numpy.datetime64`` objects as UTC times when passed to the :class:`Timestamp` constructor along with a timezone. In a future version, these will be treated as wall-times. To retain the old behavior, use ``Timestamp(dt64).tz_localize("UTC").tz_convert(tz)`` (:issue:`??`) +- Deprecated treating ``numpy.datetime64`` objects as UTC times when passed to the :class:`Timestamp` constructor along with a timezone. In a future version, these will be treated as wall-times. To retain the old behavior, use ``Timestamp(dt64).tz_localize("UTC").tz_convert(tz)`` (:issue:`42288`) - .. --------------------------------------------------------------------------- diff --git a/pandas/_libs/tslibs/timestamps.pyx b/pandas/_libs/tslibs/timestamps.pyx index 71654675c4825..8cc1192a9da79 100644 --- a/pandas/_libs/tslibs/timestamps.pyx +++ b/pandas/_libs/tslibs/timestamps.pyx @@ -1330,7 +1330,7 @@ class Timestamp(_Timestamp): tzobj = maybe_get_tz(tz) if tzobj is not None and is_datetime64_object(ts_input): - # In the future we will treat datetime64 as wall-time + # GH#42288 In the future we will treat datetime64 as wall-time # (consistent with DatetimeIndex) warnings.warn( "In a future version, when passing a np.datetime64 object and " diff --git a/pandas/tests/scalar/timestamp/test_constructors.py b/pandas/tests/scalar/timestamp/test_constructors.py index e5190cb348639..eed654672292e 100644 --- a/pandas/tests/scalar/timestamp/test_constructors.py +++ b/pandas/tests/scalar/timestamp/test_constructors.py @@ -26,6 +26,7 @@ class TestTimestampConstructors: def test_constructor_datetime64_with_tz(self): + # GH#42288 dt = np.datetime64("1970-01-01 05:00:00") tzstr = "UTC+05:00" From 3de5abd7d8e3417602af16333712674409d3f430 Mon Sep 17 00:00:00 2001 From: Brock Date: Mon, 28 Jun 2021 16:53:41 -0700 Subject: [PATCH 3/3] mypy fixup --- pandas/core/arrays/datetimes.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pandas/core/arrays/datetimes.py b/pandas/core/arrays/datetimes.py index 70cb06349fa37..d3daeff0b87cb 100644 --- a/pandas/core/arrays/datetimes.py +++ b/pandas/core/arrays/datetimes.py @@ -510,7 +510,9 @@ def _check_compatible_with(self, other, setitem: bool = False): def _box_func(self, x) -> Timestamp | NaTType: if isinstance(x, np.datetime64): - x = np.int64(x) + # Argument 1 to "signedinteger" has incompatible type "datetime64"; + # expected "Union[SupportsInt, Union[str, bytes], SupportsIndex]" + x = np.int64(x) # type: ignore[arg-type] ts = Timestamp(x, tz=self.tz) # Non-overlapping identity check (left operand type: "Timestamp", # right operand type: "NaTType")