From 77460bf708350d9ff47b91dd5e293c9d5b73e409 Mon Sep 17 00:00:00 2001 From: Matthew Roeschke Date: Mon, 8 Nov 2021 14:32:32 -0800 Subject: [PATCH 1/6] DOC: Clarify DST rounding behavior in Timestamp/DatetimeIndex --- pandas/_libs/tslibs/timestamps.pyx | 21 +++++++++++++++++++++ pandas/core/arrays/datetimelike.py | 7 +++++++ 2 files changed, 28 insertions(+) diff --git a/pandas/_libs/tslibs/timestamps.pyx b/pandas/_libs/tslibs/timestamps.pyx index 613da5a691736..b64a1fadea9d3 100644 --- a/pandas/_libs/tslibs/timestamps.pyx +++ b/pandas/_libs/tslibs/timestamps.pyx @@ -1424,6 +1424,13 @@ timedelta}, default 'raise' ------ ValueError if the freq cannot be converted + Notes + ----- + If the Timestamp has a timezone, rounding will take place relative to the + local ("wall") time and re-localized to the same timezone. When rounding + near daylight savings time, use ``nonexistent`` and ``ambiguous`` to + control the re-localization behavior. + Examples -------- Create a timestamp object: @@ -1497,6 +1504,13 @@ timedelta}, default 'raise' ------ ValueError if the freq cannot be converted. + Notes + ----- + If the Timestamp has a timezone, flooring will take place relative to the + local ("wall") time and re-localized to the same timezone. When rounding + near daylight savings time, use ``nonexistent`` and ``ambiguous`` to + control the re-localization behavior. + Examples -------- Create a timestamp object: @@ -1568,6 +1582,13 @@ timedelta}, default 'raise' ------ ValueError if the freq cannot be converted. + Notes + ----- + If the Timestamp has a timezone, ceiling will take place relative to the + local ("wall") time and re-localized to the same timezone. When rounding + near daylight savings time, use ``nonexistent`` and ``ambiguous`` to + control the re-localization behavior. + Examples -------- Create a timestamp object: diff --git a/pandas/core/arrays/datetimelike.py b/pandas/core/arrays/datetimelike.py index f8aa1656c8c30..40e3ae26f8686 100644 --- a/pandas/core/arrays/datetimelike.py +++ b/pandas/core/arrays/datetimelike.py @@ -1639,6 +1639,13 @@ def strftime(self, date_format: str) -> npt.NDArray[np.object_]: ------ ValueError if the `freq` cannot be converted. + Notes + ----- + If the timestamps have a timezone, {op}ing will take place relative to the + local ("wall") time and re-localized to the same timezone. When {op}ing + near daylight savings time, use ``nonexistent`` and ``ambiguous`` to + control the re-localization behavior. + Examples -------- **DatetimeIndex** From 5184f0e4520b447ddeb034668737d60e8aa03ac2 Mon Sep 17 00:00:00 2001 From: Matthew Roeschke Date: Mon, 8 Nov 2021 14:35:07 -0800 Subject: [PATCH 2/6] fix some wording --- pandas/_libs/tslibs/timestamps.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/_libs/tslibs/timestamps.pyx b/pandas/_libs/tslibs/timestamps.pyx index b64a1fadea9d3..f94a0dc35cb87 100644 --- a/pandas/_libs/tslibs/timestamps.pyx +++ b/pandas/_libs/tslibs/timestamps.pyx @@ -1507,7 +1507,7 @@ timedelta}, default 'raise' Notes ----- If the Timestamp has a timezone, flooring will take place relative to the - local ("wall") time and re-localized to the same timezone. When rounding + local ("wall") time and re-localized to the same timezone. When flooring near daylight savings time, use ``nonexistent`` and ``ambiguous`` to control the re-localization behavior. @@ -1585,7 +1585,7 @@ timedelta}, default 'raise' Notes ----- If the Timestamp has a timezone, ceiling will take place relative to the - local ("wall") time and re-localized to the same timezone. When rounding + local ("wall") time and re-localized to the same timezone. When ceiling near daylight savings time, use ``nonexistent`` and ``ambiguous`` to control the re-localization behavior. From d60faf7850f37d2501eec10e75621540d0465f4e Mon Sep 17 00:00:00 2001 From: Matthew Roeschke Date: Thu, 11 Nov 2021 20:05:25 -0800 Subject: [PATCH 3/6] Add examples --- pandas/_libs/tslibs/timestamps.pyx | 33 +++++++++++++++++++++++++ pandas/core/arrays/datetimelike.py | 39 ++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/pandas/_libs/tslibs/timestamps.pyx b/pandas/_libs/tslibs/timestamps.pyx index f94a0dc35cb87..e22d40822542a 100644 --- a/pandas/_libs/tslibs/timestamps.pyx +++ b/pandas/_libs/tslibs/timestamps.pyx @@ -1465,6 +1465,17 @@ timedelta}, default 'raise' >>> pd.NaT.round() NaT + + When rounding near a daylight savings time transition, use ``ambiguous`` or + ``nonexistent`` to control how the timestamp should be re-localized. + + >>> ts_tz = pd.Timestamp("2021-10-31 01:30:00").tz_localize("Europe/Amsterdam") + + >>> ts_tz.round("H", ambiguous=False) + Timestamp('2021-10-31 02:00:00+0100', tz='Europe/Amsterdam') + + >>> ts_tz.round("H", ambiguous=True) + Timestamp('2021-10-31 02:00:00+0200', tz='Europe/Amsterdam') """ return self._round( freq, RoundTo.NEAREST_HALF_EVEN, ambiguous, nonexistent @@ -1545,6 +1556,17 @@ timedelta}, default 'raise' >>> pd.NaT.floor() NaT + + When rounding near a daylight savings time transition, use ``ambiguous`` or + ``nonexistent`` to control how the timestamp should be re-localized. + + >>> ts_tz = pd.Timestamp("2021-10-31 03:30:00").tz_localize("Europe/Amsterdam") + + >>> ts_tz.floor("2H", ambiguous=False) + Timestamp('2021-10-31 02:00:00+0100', tz='Europe/Amsterdam') + + >>> ts_tz.floor("2H", ambiguous=True) + Timestamp('2021-10-31 02:00:00+0200', tz='Europe/Amsterdam') """ return self._round(freq, RoundTo.MINUS_INFTY, ambiguous, nonexistent) @@ -1623,6 +1645,17 @@ timedelta}, default 'raise' >>> pd.NaT.ceil() NaT + + When rounding near a daylight savings time transition, use ``ambiguous`` or + ``nonexistent`` to control how the timestamp should be re-localized. + + >>> ts_tz = pd.Timestamp("2021-10-31 01:30:00").tz_localize("Europe/Amsterdam") + + >>> ts_tz.ceil("H", ambiguous=False) + Timestamp('2021-10-31 02:00:00+0100', tz='Europe/Amsterdam') + + >>> ts_tz.ceil("H", ambiguous=True) + Timestamp('2021-10-31 02:00:00+0200', tz='Europe/Amsterdam') """ return self._round(freq, RoundTo.PLUS_INFTY, ambiguous, nonexistent) diff --git a/pandas/core/arrays/datetimelike.py b/pandas/core/arrays/datetimelike.py index 40e3ae26f8686..a6029a7bf55cf 100644 --- a/pandas/core/arrays/datetimelike.py +++ b/pandas/core/arrays/datetimelike.py @@ -1669,6 +1669,19 @@ def strftime(self, date_format: str) -> npt.NDArray[np.object_]: 1 2018-01-01 12:00:00 2 2018-01-01 12:00:00 dtype: datetime64[ns] + + When rounding near a daylight savings time transition, use ``ambiguous`` or + ``nonexistent`` to control how the timestamp should be re-localized. + + >>> rng_tz = pd.DatetimeIndex(["2021-10-31 01:30:00"], tz="Europe/Amsterdam") + + >>> rng_tz.floor("H", ambiguous=False) + DatetimeIndex(['2021-10-31 02:00:00+01:00'], + dtype='datetime64[ns, Europe/Amsterdam]', freq=None) + + >>> rng_tz.floor("H", ambiguous=True) + DatetimeIndex(['2021-10-31 02:00:00+02:00'], + dtype='datetime64[ns, Europe/Amsterdam]', freq=None) """ _floor_example = """>>> rng.floor('H') @@ -1683,6 +1696,19 @@ def strftime(self, date_format: str) -> npt.NDArray[np.object_]: 1 2018-01-01 12:00:00 2 2018-01-01 12:00:00 dtype: datetime64[ns] + + When rounding near a daylight savings time transition, use ``ambiguous`` or + ``nonexistent`` to control how the timestamp should be re-localized. + + >>> rng_tz = pd.DatetimeIndex(["2021-10-31 03:30:00"], tz="Europe/Amsterdam") + + >>> rng_tz.floor("2H", ambiguous=False) + DatetimeIndex(['2021-10-31 02:00:00+01:00'], + dtype='datetime64[ns, Europe/Amsterdam]', freq=None) + + >>> rng_tz.floor("2H", ambiguous=True) + DatetimeIndex(['2021-10-31 02:00:00+02:00'], + dtype='datetime64[ns, Europe/Amsterdam]', freq=None) """ _ceil_example = """>>> rng.ceil('H') @@ -1697,6 +1723,19 @@ def strftime(self, date_format: str) -> npt.NDArray[np.object_]: 1 2018-01-01 12:00:00 2 2018-01-01 13:00:00 dtype: datetime64[ns] + + When rounding near a daylight savings time transition, use ``ambiguous`` or + ``nonexistent`` to control how the timestamp should be re-localized. + + >>> rng_tz = pd.DatetimeIndex(["2021-10-31 01:30:00"], tz="Europe/Amsterdam") + + >>> rng_tz.ceil("H", ambiguous=False) + DatetimeIndex(['2021-10-31 02:00:00+01:00'], + dtype='datetime64[ns, Europe/Amsterdam]', freq=None) + + >>> rng_tz.ceil("H", ambiguous=True) + DatetimeIndex(['2021-10-31 02:00:00+02:00'], + dtype='datetime64[ns, Europe/Amsterdam]', freq=None) """ From ee7b62e4765b5272565ac5bc0c73c42208dd0ca7 Mon Sep 17 00:00:00 2001 From: Matthew Roeschke Date: Fri, 12 Nov 2021 16:47:55 -0800 Subject: [PATCH 4/6] Fix doctest --- pandas/core/arrays/datetimelike.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pandas/core/arrays/datetimelike.py b/pandas/core/arrays/datetimelike.py index a6029a7bf55cf..f22aba0e51846 100644 --- a/pandas/core/arrays/datetimelike.py +++ b/pandas/core/arrays/datetimelike.py @@ -1673,13 +1673,13 @@ def strftime(self, date_format: str) -> npt.NDArray[np.object_]: When rounding near a daylight savings time transition, use ``ambiguous`` or ``nonexistent`` to control how the timestamp should be re-localized. - >>> rng_tz = pd.DatetimeIndex(["2021-10-31 01:30:00"], tz="Europe/Amsterdam") + >>> rng_tz = pd.DatetimeIndex(["2021-10-31 03:30:00"], tz="Europe/Amsterdam") - >>> rng_tz.floor("H", ambiguous=False) + >>> rng_tz.floor("2H", ambiguous=False) DatetimeIndex(['2021-10-31 02:00:00+01:00'], dtype='datetime64[ns, Europe/Amsterdam]', freq=None) - >>> rng_tz.floor("H", ambiguous=True) + >>> rng_tz.floor("2H", ambiguous=True) DatetimeIndex(['2021-10-31 02:00:00+02:00'], dtype='datetime64[ns, Europe/Amsterdam]', freq=None) """ From e184483c61b65d2455a4227c87889307a1bf0847 Mon Sep 17 00:00:00 2001 From: Matthew Roeschke Date: Sat, 13 Nov 2021 19:59:43 -0800 Subject: [PATCH 5/6] Fix NaT docstrings --- pandas/_libs/tslibs/nattype.pyx | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/pandas/_libs/tslibs/nattype.pyx b/pandas/_libs/tslibs/nattype.pyx index 2aebf75ba35d4..fe3491c89bd97 100644 --- a/pandas/_libs/tslibs/nattype.pyx +++ b/pandas/_libs/tslibs/nattype.pyx @@ -810,6 +810,17 @@ timedelta}, default 'raise' >>> pd.NaT.round() NaT + + When rounding near a daylight savings time transition, use ``ambiguous`` or + ``nonexistent`` to control how the timestamp should be re-localized. + + >>> ts_tz = pd.Timestamp("2021-10-31 01:30:00").tz_localize("Europe/Amsterdam") + + >>> ts_tz.round("H", ambiguous=False) + Timestamp('2021-10-31 02:00:00+0100', tz='Europe/Amsterdam') + + >>> ts_tz.round("H", ambiguous=True) + Timestamp('2021-10-31 02:00:00+0200', tz='Europe/Amsterdam') """, ) floor = _make_nat_func( @@ -881,6 +892,17 @@ timedelta}, default 'raise' >>> pd.NaT.floor() NaT + + When rounding near a daylight savings time transition, use ``ambiguous`` or + ``nonexistent`` to control how the timestamp should be re-localized. + + >>> ts_tz = pd.Timestamp("2021-10-31 03:30:00").tz_localize("Europe/Amsterdam") + + >>> ts_tz.floor("2H", ambiguous=False) + Timestamp('2021-10-31 02:00:00+0100', tz='Europe/Amsterdam') + + >>> ts_tz.floor("2H", ambiguous=True) + Timestamp('2021-10-31 02:00:00+0200', tz='Europe/Amsterdam') """, ) ceil = _make_nat_func( @@ -952,6 +974,17 @@ timedelta}, default 'raise' >>> pd.NaT.ceil() NaT + + When rounding near a daylight savings time transition, use ``ambiguous`` or + ``nonexistent`` to control how the timestamp should be re-localized. + + >>> ts_tz = pd.Timestamp("2021-10-31 01:30:00").tz_localize("Europe/Amsterdam") + + >>> ts_tz.ceil("H", ambiguous=False) + Timestamp('2021-10-31 02:00:00+0100', tz='Europe/Amsterdam') + + >>> ts_tz.ceil("H", ambiguous=True) + Timestamp('2021-10-31 02:00:00+0200', tz='Europe/Amsterdam') """, ) From fc0616b5036ef767cfbfad1b5a2c2f8acea79e93 Mon Sep 17 00:00:00 2001 From: Matthew Roeschke Date: Sat, 13 Nov 2021 21:30:01 -0800 Subject: [PATCH 6/6] fix again --- pandas/_libs/tslibs/nattype.pyx | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/pandas/_libs/tslibs/nattype.pyx b/pandas/_libs/tslibs/nattype.pyx index d22e916ac32ac..45e3da5c5029d 100644 --- a/pandas/_libs/tslibs/nattype.pyx +++ b/pandas/_libs/tslibs/nattype.pyx @@ -776,6 +776,13 @@ timedelta}, default 'raise' ------ ValueError if the freq cannot be converted + Notes + ----- + If the Timestamp has a timezone, rounding will take place relative to the + local ("wall") time and re-localized to the same timezone. When rounding + near daylight savings time, use ``nonexistent`` and ``ambiguous`` to + control the re-localization behavior. + Examples -------- Create a timestamp object: @@ -858,6 +865,13 @@ timedelta}, default 'raise' ------ ValueError if the freq cannot be converted. + Notes + ----- + If the Timestamp has a timezone, flooring will take place relative to the + local ("wall") time and re-localized to the same timezone. When flooring + near daylight savings time, use ``nonexistent`` and ``ambiguous`` to + control the re-localization behavior. + Examples -------- Create a timestamp object: @@ -940,6 +954,13 @@ timedelta}, default 'raise' ------ ValueError if the freq cannot be converted. + Notes + ----- + If the Timestamp has a timezone, ceiling will take place relative to the + local ("wall") time and re-localized to the same timezone. When ceiling + near daylight savings time, use ``nonexistent`` and ``ambiguous`` to + control the re-localization behavior. + Examples -------- Create a timestamp object: