From 30f7275ea03ba19f52ba44e08e9288628f5c1d46 Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Thu, 5 Nov 2020 11:51:36 +0300 Subject: [PATCH 1/9] BUG: preserve fold in Timestamp.replace --- pandas/_libs/tslibs/timestamps.pyx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/pandas/_libs/tslibs/timestamps.pyx b/pandas/_libs/tslibs/timestamps.pyx index 9076325d01bab..b3ae69d7a3237 100644 --- a/pandas/_libs/tslibs/timestamps.pyx +++ b/pandas/_libs/tslibs/timestamps.pyx @@ -1374,7 +1374,7 @@ default 'raise' microsecond=None, nanosecond=None, tzinfo=object, - fold=0, + fold=None, ): """ implements datetime.replace, handles nanoseconds. @@ -1390,7 +1390,7 @@ default 'raise' microsecond : int, optional nanosecond : int, optional tzinfo : tz-convertible, optional - fold : int, optional, default is 0 + fold : int, optional Returns ------- @@ -1407,6 +1407,11 @@ default 'raise' # set to naive if needed tzobj = self.tzinfo value = self.value + + # GH 37610. Preserve fold when replacing. + if fold is None: + fold = self.fold + if tzobj is not None: value = tz_convert_from_utc_single(value, tzobj) From b2745e13e2a3cf9805c17e7ab42664590222c1c4 Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Thu, 5 Nov 2020 11:51:47 +0300 Subject: [PATCH 2/9] DOC: add whatsnew --- doc/source/whatsnew/v1.2.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v1.2.0.rst b/doc/source/whatsnew/v1.2.0.rst index 690e6b8f725ad..57064bd348aa5 100644 --- a/doc/source/whatsnew/v1.2.0.rst +++ b/doc/source/whatsnew/v1.2.0.rst @@ -412,7 +412,7 @@ Timezones ^^^^^^^^^ - Bug in :func:`date_range` was raising AmbiguousTimeError for valid input with ``ambiguous=False`` (:issue:`35297`) -- +- Bug in :meth:`Timestamp.replace` was losing fold information (:issue:`37610`) Numeric From 096ee9b8a07e0bf7d294fe9593cad5e120018a98 Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Thu, 5 Nov 2020 12:52:29 +0300 Subject: [PATCH 3/9] TST: add test --- pandas/tests/indexes/datetimes/test_timezones.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pandas/tests/indexes/datetimes/test_timezones.py b/pandas/tests/indexes/datetimes/test_timezones.py index 8a73f564ef064..01af7844b9bc4 100644 --- a/pandas/tests/indexes/datetimes/test_timezones.py +++ b/pandas/tests/indexes/datetimes/test_timezones.py @@ -1190,3 +1190,13 @@ def test_tz_localize_invalidates_freq(): dti2 = dti[:1] result = dti2.tz_localize("US/Eastern") assert result.freq == "H" + + +def test_replace_preserves_fold(): + # GH 37610. Check that replace preserves Timestamp fold property + tz = gettz("Europe/Moscow") + + result = Timestamp(1256427000000000000, tz=tz, unit="ns").replace(tzinfo=tz).fold + expected = 1 + + assert result == expected From b9fa4919b67455d883beadb63baaba0870ab877f Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Thu, 5 Nov 2020 13:59:54 +0300 Subject: [PATCH 4/9] DOC: mirror doc change for NaT --- pandas/_libs/tslibs/nattype.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/_libs/tslibs/nattype.pyx b/pandas/_libs/tslibs/nattype.pyx index 88ad008b42c21..e10ac6a05ead8 100644 --- a/pandas/_libs/tslibs/nattype.pyx +++ b/pandas/_libs/tslibs/nattype.pyx @@ -774,7 +774,7 @@ default 'raise' microsecond : int, optional nanosecond : int, optional tzinfo : tz-convertible, optional - fold : int, optional, default is 0 + fold : int, optional Returns ------- From b4c4127fc7cef89a82788b2dfbd797f96fbf24c4 Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Thu, 5 Nov 2020 18:25:11 +0300 Subject: [PATCH 5/9] TST: move the test --- pandas/tests/indexes/datetimes/test_timezones.py | 10 ---------- pandas/tests/scalar/timestamp/test_unary_ops.py | 10 ++++++++++ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/pandas/tests/indexes/datetimes/test_timezones.py b/pandas/tests/indexes/datetimes/test_timezones.py index 01af7844b9bc4..8a73f564ef064 100644 --- a/pandas/tests/indexes/datetimes/test_timezones.py +++ b/pandas/tests/indexes/datetimes/test_timezones.py @@ -1190,13 +1190,3 @@ def test_tz_localize_invalidates_freq(): dti2 = dti[:1] result = dti2.tz_localize("US/Eastern") assert result.freq == "H" - - -def test_replace_preserves_fold(): - # GH 37610. Check that replace preserves Timestamp fold property - tz = gettz("Europe/Moscow") - - result = Timestamp(1256427000000000000, tz=tz, unit="ns").replace(tzinfo=tz).fold - expected = 1 - - assert result == expected diff --git a/pandas/tests/scalar/timestamp/test_unary_ops.py b/pandas/tests/scalar/timestamp/test_unary_ops.py index e8196cd8328e7..7c4f77d531581 100644 --- a/pandas/tests/scalar/timestamp/test_unary_ops.py +++ b/pandas/tests/scalar/timestamp/test_unary_ops.py @@ -424,3 +424,13 @@ def test_timestamp(self): # should agree with datetime.timestamp method dt = ts.to_pydatetime() assert dt.timestamp() == ts.timestamp() + + +def test_replace_preserves_fold(): + # GH 37610. Check that replace preserves Timestamp fold property + tz = gettz("Europe/Moscow") + + result = Timestamp(1256427000000000000, tz=tz, unit="ns").replace(tzinfo=tz).fold + expected = 1 + + assert result == expected From 7c2f7821eb33d9614c074871796d2ca90f5a503c Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Thu, 5 Nov 2020 18:40:57 +0300 Subject: [PATCH 6/9] TST: improve test readability --- pandas/tests/scalar/timestamp/test_unary_ops.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pandas/tests/scalar/timestamp/test_unary_ops.py b/pandas/tests/scalar/timestamp/test_unary_ops.py index 7c4f77d531581..4808e13cc1e5d 100644 --- a/pandas/tests/scalar/timestamp/test_unary_ops.py +++ b/pandas/tests/scalar/timestamp/test_unary_ops.py @@ -430,7 +430,9 @@ def test_replace_preserves_fold(): # GH 37610. Check that replace preserves Timestamp fold property tz = gettz("Europe/Moscow") - result = Timestamp(1256427000000000000, tz=tz, unit="ns").replace(tzinfo=tz).fold - expected = 1 + dt = datetime(year=2009, month=10, day=25, hour=2, minute=30, fold=1, tzinfo=tz) + ts = Timestamp(year=2009, month=10, day=25, hour=2, minute=30, fold=1, tz=tz) + result = ts.replace(tzinfo=tz).fold + expected = dt.replace(tzinfo=tz).fold assert result == expected From 2ca31caa7f6f49a16a282efd8efeb6f9a50461ee Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Thu, 5 Nov 2020 18:44:19 +0300 Subject: [PATCH 7/9] TST: incorporate reviewer suggestions --- pandas/tests/scalar/timestamp/test_unary_ops.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/pandas/tests/scalar/timestamp/test_unary_ops.py b/pandas/tests/scalar/timestamp/test_unary_ops.py index 4808e13cc1e5d..a88aa347d8b40 100644 --- a/pandas/tests/scalar/timestamp/test_unary_ops.py +++ b/pandas/tests/scalar/timestamp/test_unary_ops.py @@ -426,13 +426,12 @@ def test_timestamp(self): assert dt.timestamp() == ts.timestamp() -def test_replace_preserves_fold(): +@pytest.mark.parametrize("fold", [0, 1]) +def test_replace_preserves_fold(fold): # GH 37610. Check that replace preserves Timestamp fold property tz = gettz("Europe/Moscow") - dt = datetime(year=2009, month=10, day=25, hour=2, minute=30, fold=1, tzinfo=tz) - ts = Timestamp(year=2009, month=10, day=25, hour=2, minute=30, fold=1, tz=tz) - result = ts.replace(tzinfo=tz).fold - expected = dt.replace(tzinfo=tz).fold + ts = Timestamp(year=2009, month=10, day=25, hour=2, minute=30, fold=fold, tz=tz) + ts_replaced = ts.replace(tzinfo=tz) - assert result == expected + assert ts_replaced.fold == fold From 471969770d7d6e2be8cf0d5d637e925e7ea5555d Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Thu, 5 Nov 2020 18:46:28 +0300 Subject: [PATCH 8/9] TST: replace second instead of fold --- pandas/tests/scalar/timestamp/test_unary_ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/scalar/timestamp/test_unary_ops.py b/pandas/tests/scalar/timestamp/test_unary_ops.py index a88aa347d8b40..a168569787a5e 100644 --- a/pandas/tests/scalar/timestamp/test_unary_ops.py +++ b/pandas/tests/scalar/timestamp/test_unary_ops.py @@ -432,6 +432,6 @@ def test_replace_preserves_fold(fold): tz = gettz("Europe/Moscow") ts = Timestamp(year=2009, month=10, day=25, hour=2, minute=30, fold=fold, tz=tz) - ts_replaced = ts.replace(tzinfo=tz) + ts_replaced = ts.replace(second=1) assert ts_replaced.fold == fold From ada4d94a615c4a9eea131065edc470a6e7d6763e Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Fri, 6 Nov 2020 10:15:38 +0300 Subject: [PATCH 9/9] TST: tzinfo instead of tz in the test --- pandas/tests/scalar/timestamp/test_unary_ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/scalar/timestamp/test_unary_ops.py b/pandas/tests/scalar/timestamp/test_unary_ops.py index a168569787a5e..88f99a6784ba1 100644 --- a/pandas/tests/scalar/timestamp/test_unary_ops.py +++ b/pandas/tests/scalar/timestamp/test_unary_ops.py @@ -431,7 +431,7 @@ def test_replace_preserves_fold(fold): # GH 37610. Check that replace preserves Timestamp fold property tz = gettz("Europe/Moscow") - ts = Timestamp(year=2009, month=10, day=25, hour=2, minute=30, fold=fold, tz=tz) + ts = Timestamp(year=2009, month=10, day=25, hour=2, minute=30, fold=fold, tzinfo=tz) ts_replaced = ts.replace(second=1) assert ts_replaced.fold == fold