diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index a9fa8b2174dd0..8e1fc352ba4f7 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -19,7 +19,7 @@ including other versions of pandas. Other Enhancements ^^^^^^^^^^^^^^^^^^ -- +- :meth:`Timestamp.replace` now supports the ``fold`` argument to disambiguate DST transition times (:issue:`25017`) - - diff --git a/pandas/_libs/tslibs/nattype.pyx b/pandas/_libs/tslibs/nattype.pyx index a55d15a7c4e85..c719bcb2ef135 100644 --- a/pandas/_libs/tslibs/nattype.pyx +++ b/pandas/_libs/tslibs/nattype.pyx @@ -669,7 +669,6 @@ class NaTType(_NaT): nanosecond : int, optional tzinfo : tz-convertible, optional fold : int, optional, default is 0 - added in 3.6, NotImplemented Returns ------- diff --git a/pandas/_libs/tslibs/timestamps.pyx b/pandas/_libs/tslibs/timestamps.pyx index fe0564cb62c30..85d94f822056b 100644 --- a/pandas/_libs/tslibs/timestamps.pyx +++ b/pandas/_libs/tslibs/timestamps.pyx @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +import sys import warnings from cpython cimport (PyObject_RichCompareBool, PyObject_RichCompare, @@ -43,10 +44,11 @@ from pandas._libs.tslibs.timezones import UTC # Constants _zero_time = datetime_time(0, 0) _no_input = object() - +PY36 = sys.version_info >= (3, 6) # ---------------------------------------------------------------------- + def maybe_integer_op_deprecated(obj): # GH#22535 add/sub of integers and int-arrays is deprecated if obj.freq is not None: @@ -1195,7 +1197,6 @@ class Timestamp(_Timestamp): nanosecond : int, optional tzinfo : tz-convertible, optional fold : int, optional, default is 0 - added in 3.6, NotImplemented Returns ------- @@ -1252,12 +1253,16 @@ class Timestamp(_Timestamp): # see GH#18319 ts_input = _tzinfo.localize(datetime(dts.year, dts.month, dts.day, dts.hour, dts.min, dts.sec, - dts.us)) + dts.us), + is_dst=not bool(fold)) _tzinfo = ts_input.tzinfo else: - ts_input = datetime(dts.year, dts.month, dts.day, - dts.hour, dts.min, dts.sec, dts.us, - tzinfo=_tzinfo) + kwargs = {'year': dts.year, 'month': dts.month, 'day': dts.day, + 'hour': dts.hour, 'minute': dts.min, 'second': dts.sec, + 'microsecond': dts.us, 'tzinfo': _tzinfo} + if PY36: + kwargs['fold'] = fold + ts_input = datetime(**kwargs) ts = convert_datetime_to_tsobject(ts_input, _tzinfo) value = ts.value + (dts.ps // 1000) diff --git a/pandas/tests/scalar/timestamp/test_unary_ops.py b/pandas/tests/scalar/timestamp/test_unary_ops.py index 3f9a30d254126..adcf66200a672 100644 --- a/pandas/tests/scalar/timestamp/test_unary_ops.py +++ b/pandas/tests/scalar/timestamp/test_unary_ops.py @@ -8,7 +8,7 @@ from pandas._libs.tslibs import conversion from pandas._libs.tslibs.frequencies import INVALID_FREQ_ERR_MSG -from pandas.compat import PY3 +from pandas.compat import PY3, PY36 import pandas.util._test_decorators as td from pandas import NaT, Timestamp @@ -329,6 +329,19 @@ def test_replace_dst_border(self): expected = Timestamp('2013-11-3 03:00:00', tz='America/Chicago') assert result == expected + @pytest.mark.skipif(not PY36, reason='Fold not available until PY3.6') + @pytest.mark.parametrize('fold', [0, 1]) + @pytest.mark.parametrize('tz', ['dateutil/Europe/London', 'Europe/London']) + def test_replace_dst_fold(self, fold, tz): + # GH 25017 + d = datetime(2019, 10, 27, 2, 30) + ts = Timestamp(d, tz=tz) + result = ts.replace(hour=1, fold=fold) + expected = Timestamp(datetime(2019, 10, 27, 1, 30)).tz_localize( + tz, ambiguous=not fold + ) + assert result == expected + # -------------------------------------------------------------- # Timestamp.normalize