From 57fb83f56f5a78f422104d7eac0811836f855ddf Mon Sep 17 00:00:00 2001 From: Matthew Roeschke Date: Mon, 11 Feb 2019 06:32:53 -0800 Subject: [PATCH] API: Ensure DatetimeTZDtype standardizes pytz timezones (#25254) * API: Ensure DatetimeTZDtype standardizes pytz timezones * Add whatsnew --- doc/source/user_guide/timeseries.rst | 24 ++++++++++++++++++++++++ doc/source/whatsnew/v0.25.0.rst | 2 +- pandas/core/dtypes/dtypes.py | 1 + pandas/tests/dtypes/test_dtypes.py | 10 ++++++++++ 4 files changed, 36 insertions(+), 1 deletion(-) diff --git a/doc/source/user_guide/timeseries.rst b/doc/source/user_guide/timeseries.rst index 5841125817d03..23f1aabd69ff3 100644 --- a/doc/source/user_guide/timeseries.rst +++ b/doc/source/user_guide/timeseries.rst @@ -321,6 +321,15 @@ which can be specified. These are computed from the starting point specified by pd.to_datetime([1349720105100, 1349720105200, 1349720105300, 1349720105400, 1349720105500], unit='ms') +Constructing a :class:`Timestamp` or :class:`DatetimeIndex` with an epoch timestamp +with the ``tz`` argument specified will localize the epoch timestamps to UTC +first then convert the result to the specified time zone. + +.. ipython:: python + + pd.Timestamp(1262347200000000000, tz='US/Pacific') + pd.DatetimeIndex([1262347200000000000], tz='US/Pacific') + .. note:: Epoch times will be rounded to the nearest nanosecond. @@ -2205,6 +2214,21 @@ you can use the ``tz_convert`` method. rng_pytz.tz_convert('US/Eastern') +.. note:: + + When using ``pytz`` time zones, :class:`DatetimeIndex` will construct a different + time zone object than a :class:`Timestamp` for the same time zone input. A :class:`DatetimeIndex` + can hold a collection of :class:`Timestamp` objects that may have different UTC offsets and cannot be + succinctly represented by one ``pytz`` time zone instance while one :class:`Timestamp` + represents one point in time with a specific UTC offset. + + .. ipython:: python + + dti = pd.date_range('2019-01-01', periods=3, freq='D', tz='US/Pacific') + dti.tz + ts = pd.Timestamp('2019-01-01', tz='US/Pacific') + ts.tz + .. warning:: Be wary of conversions between libraries. For some time zones, ``pytz`` and ``dateutil`` have different diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index 95c8d6e2cf6a3..dcb0cbb064f21 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -33,7 +33,7 @@ Backwards incompatible API changes Other API Changes ^^^^^^^^^^^^^^^^^ -- +- :class:`DatetimeTZDtype` will now standardize pytz timezones to a common timezone instance (:issue:`24713`) - - diff --git a/pandas/core/dtypes/dtypes.py b/pandas/core/dtypes/dtypes.py index 8b9ac680493a1..f187d786d9f61 100644 --- a/pandas/core/dtypes/dtypes.py +++ b/pandas/core/dtypes/dtypes.py @@ -639,6 +639,7 @@ def __init__(self, unit="ns", tz=None): if tz: tz = timezones.maybe_get_tz(tz) + tz = timezones.tz_standardize(tz) elif tz is not None: raise pytz.UnknownTimeZoneError(tz) elif tz is None: diff --git a/pandas/tests/dtypes/test_dtypes.py b/pandas/tests/dtypes/test_dtypes.py index 0fe0a845f5129..710f215686eab 100644 --- a/pandas/tests/dtypes/test_dtypes.py +++ b/pandas/tests/dtypes/test_dtypes.py @@ -3,6 +3,7 @@ import numpy as np import pytest +import pytz from pandas.core.dtypes.common import ( is_bool_dtype, is_categorical, is_categorical_dtype, @@ -302,6 +303,15 @@ def test_empty(self): with pytest.raises(TypeError, match="A 'tz' is required."): DatetimeTZDtype() + def test_tz_standardize(self): + # GH 24713 + tz = pytz.timezone('US/Eastern') + dr = date_range('2013-01-01', periods=3, tz='US/Eastern') + dtype = DatetimeTZDtype('ns', dr.tz) + assert dtype.tz == tz + dtype = DatetimeTZDtype('ns', dr[0].tz) + assert dtype.tz == tz + class TestPeriodDtype(Base):