From 544f3e6cfc39330a189cf444cae8a3a85e528ff8 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Thu, 28 May 2020 18:38:35 -0700 Subject: [PATCH 1/3] BUG: Period.to_timestamp not matching PeriodArray.to_timestamp --- pandas/_libs/tslibs/period.pyx | 8 ++++++-- pandas/tests/scalar/period/test_period.py | 7 +++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/pandas/_libs/tslibs/period.pyx b/pandas/_libs/tslibs/period.pyx index cc6c4d06ae562..49abfddc578f9 100644 --- a/pandas/_libs/tslibs/period.pyx +++ b/pandas/_libs/tslibs/period.pyx @@ -1756,18 +1756,22 @@ cdef class _Period: ------- Timestamp """ - if freq is not None: - freq = self._maybe_convert_freq(freq) how = validate_end_alias(how) end = how == 'E' if end: + if freq == "B": + # roll forward to ensure we land on B date + adjust = Timedelta(1, "D") - Timedelta(1, "ns") + return self.to_timestamp(how="start") + adjust endpoint = (self + self.freq).to_timestamp(how='start') return endpoint - Timedelta(1, 'ns') if freq is None: base, mult = get_freq_code(self.freq) freq = get_to_timestamp_base(base) + else: + freq = self._maybe_convert_freq(freq) base, mult = get_freq_code(freq) val = self.asfreq(freq, how) diff --git a/pandas/tests/scalar/period/test_period.py b/pandas/tests/scalar/period/test_period.py index e81f2ee55eebd..22445deb084b9 100644 --- a/pandas/tests/scalar/period/test_period.py +++ b/pandas/tests/scalar/period/test_period.py @@ -632,6 +632,13 @@ def _ex(p): result = p.to_timestamp("5S", how="start") assert result == expected + def test_to_timestamp_business_end(self): + per = pd.Period("1990-01-05", "B") # Friday + result = per.to_timestamp("B", how="E") + + expected = pd.Timestamp("1990-01-06") - pd.Timedelta(nanoseconds=1) + assert result == expected + # -------------------------------------------------------------- # Rendering: __repr__, strftime, etc From 30417792629a76b6dc222a044027ad32fba9262f Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Fri, 29 May 2020 13:19:10 -0700 Subject: [PATCH 2/3] BUG: end_time --- pandas/_libs/tslibs/period.pyx | 7 ++----- pandas/core/arrays/period.py | 2 +- pandas/tests/indexes/period/test_scalar_compat.py | 9 +++++++++ pandas/tests/scalar/period/test_period.py | 8 ++++++++ 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/pandas/_libs/tslibs/period.pyx b/pandas/_libs/tslibs/period.pyx index 49abfddc578f9..b23f7ac3a599e 100644 --- a/pandas/_libs/tslibs/period.pyx +++ b/pandas/_libs/tslibs/period.pyx @@ -1731,10 +1731,7 @@ cdef class _Period: @property def end_time(self) -> Timestamp: - # freq.n can't be negative or 0 - # ordinal = (self + self.freq.n).start_time.value - 1 - ordinal = (self + self.freq).start_time.value - 1 - return Timestamp(ordinal) + return self.to_timestamp(how="end") def to_timestamp(self, freq=None, how='start', tz=None) -> Timestamp: """ @@ -1760,7 +1757,7 @@ cdef class _Period: end = how == 'E' if end: - if freq == "B": + if freq == "B" or self.freq == "B": # roll forward to ensure we land on B date adjust = Timedelta(1, "D") - Timedelta(1, "ns") return self.to_timestamp(how="start") + adjust diff --git a/pandas/core/arrays/period.py b/pandas/core/arrays/period.py index 8b2925b2c0827..765ff10238cea 100644 --- a/pandas/core/arrays/period.py +++ b/pandas/core/arrays/period.py @@ -441,7 +441,7 @@ def to_timestamp(self, freq=None, how="start"): end = how == "E" if end: - if freq == "B": + if freq == "B" or self.freq == "B": # roll forward to ensure we land on B date adjust = Timedelta(1, "D") - Timedelta(1, "ns") return self.to_timestamp(how="start") + adjust diff --git a/pandas/tests/indexes/period/test_scalar_compat.py b/pandas/tests/indexes/period/test_scalar_compat.py index 0f92b7a4e168b..e9d17e7e20778 100644 --- a/pandas/tests/indexes/period/test_scalar_compat.py +++ b/pandas/tests/indexes/period/test_scalar_compat.py @@ -17,3 +17,12 @@ def test_end_time(self): expected_index = date_range("2016-01-01", end="2016-05-31", freq="M") expected_index += Timedelta(1, "D") - Timedelta(1, "ns") tm.assert_index_equal(index.end_time, expected_index) + + def test_end_time_business_friday(self): + # GH#34449 + pi = period_range("1990-01-05", freq="B", periods=1) + result = pi.end_time + + dti = date_range("1990-01-05", freq="D", periods=1)._with_freq(None) + expected = dti + Timedelta(days=1, nanoseconds=-1) + tm.assert_index_equal(result, expected) diff --git a/pandas/tests/scalar/period/test_period.py b/pandas/tests/scalar/period/test_period.py index 22445deb084b9..5f3a2b0f8ea7a 100644 --- a/pandas/tests/scalar/period/test_period.py +++ b/pandas/tests/scalar/period/test_period.py @@ -793,6 +793,14 @@ def _ex(*args): xp = _ex(2012, 1, 2, 1) assert xp == p.end_time + def test_end_time_business_friday(self): + # GH#34449 + per = Period("1990-01-05", "B") + result = per.end_time + + expected = pd.Timestamp("1990-01-06") - pd.Timedelta(nanoseconds=1) + assert result == expected + def test_anchor_week_end_time(self): def _ex(*args): return Timestamp(Timestamp(datetime(*args)).value - 1) From cbebada2b4c2cfd83ae4f8097ceacf477854cbfe Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Sat, 30 May 2020 07:50:49 -0700 Subject: [PATCH 3/3] fix test --- pandas/tests/scalar/period/test_period.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pandas/tests/scalar/period/test_period.py b/pandas/tests/scalar/period/test_period.py index 5f3a2b0f8ea7a..41909b4b1a9bb 100644 --- a/pandas/tests/scalar/period/test_period.py +++ b/pandas/tests/scalar/period/test_period.py @@ -589,6 +589,8 @@ def test_to_timestamp(self): from_lst = ["A", "Q", "M", "W", "B", "D", "H", "Min", "S"] def _ex(p): + if p.freq == "B": + return p.start_time + Timedelta(days=1, nanoseconds=-1) return Timestamp((p + p.freq).start_time.value - 1) for i, fcode in enumerate(from_lst):