From 8e0e04a852b1d1e392e2a0ec3432d29cc747c2b1 Mon Sep 17 00:00:00 2001 From: Jiang Yue Date: Tue, 14 May 2019 10:41:30 +0800 Subject: [PATCH 1/6] BUG: error calculating BusinessHourMixin.apply for long business hour per day --- pandas/tests/tseries/offsets/test_offsets.py | 18 ++++++++++++++++++ pandas/tseries/offsets.py | 16 +++++++--------- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/pandas/tests/tseries/offsets/test_offsets.py b/pandas/tests/tseries/offsets/test_offsets.py index c46cc1de7aa97..d1a390c4cef0a 100644 --- a/pandas/tests/tseries/offsets/test_offsets.py +++ b/pandas/tests/tseries/offsets/test_offsets.py @@ -1287,6 +1287,24 @@ def test_opening_time(self, case): datetime(2014, 7, 7, 19, 30): datetime(2014, 7, 5, 4, 30), datetime(2014, 7, 7, 19, 30, 30): datetime(2014, 7, 5, 4, 30, 30)})) + # long business hours + apply_cases.append((BusinessHour(n=4, start='00:00', end='23:00'), { + datetime(2014, 7, 3, 22): datetime(2014, 7, 4, 3), + datetime(2014, 7, 4, 22): datetime(2014, 7, 7, 3), + datetime(2014, 7, 3, 22, 30): datetime(2014, 7, 4, 3, 30), + datetime(2014, 7, 3, 22, 20): datetime(2014, 7, 4, 3, 20), + datetime(2014, 7, 4, 22, 30, 30): datetime(2014, 7, 7, 3, 30, 30), + datetime(2014, 7, 4, 22, 30, 20): datetime(2014, 7, 7, 3, 30, 20)})) + + apply_cases.append((BusinessHour(n=-4, start='00:00', end='23:00'), { + datetime(2014, 7, 4, 3): datetime(2014, 7, 3, 22), + datetime(2014, 7, 7, 3): datetime(2014, 7, 4, 22), + datetime(2014, 7, 4, 3, 30): datetime(2014, 7, 3, 22, 30), + datetime(2014, 7, 4, 3, 20): datetime(2014, 7, 3, 22, 20), + datetime(2014, 7, 7, 3, 30, 30): datetime(2014, 7, 4, 22, 30, 30), + datetime(2014, 7, 7, 3, 30, 20): datetime(2014, 7, 4, 22, 30, 20)})) + + @pytest.mark.parametrize('case', apply_cases) def test_apply(self, case): offset, cases = case diff --git a/pandas/tseries/offsets.py b/pandas/tseries/offsets.py index 29fee48f85015..25ce497309283 100644 --- a/pandas/tseries/offsets.py +++ b/pandas/tseries/offsets.py @@ -731,21 +731,19 @@ def apply(self, other): result = other + timedelta(hours=hours, minutes=minutes) # because of previous adjustment, time will be larger than start - if ((daytime and (result.time() < self.start or - self.end < result.time())) or - not daytime and (self.end < result.time() < self.start)): - if n >= 0: - bday_edge = self._prev_opening_time(other) - bday_edge = bday_edge + bhdelta - # calculate remainder + if n >=0: + bday_edge = self._prev_opening_time(other) + bhdelta + if bday_edge < result: bday_remain = result - bday_edge result = self._next_opening_time(other) result += bday_remain - else: - bday_edge = self._next_opening_time(other) + else: + bday_edge = self._next_opening_time(other) + if bday_edge > result: bday_remain = result - bday_edge result = self._next_opening_time(result) + bhdelta result += bday_remain + # edge handling if n >= 0: if result.time() == self.end: From fffac752c4cb9961223c57d72afe58124e8a72b3 Mon Sep 17 00:00:00 2001 From: Jiang Yue Date: Tue, 14 May 2019 10:49:40 +0800 Subject: [PATCH 2/6] styling fix --- pandas/tests/tseries/offsets/test_offsets.py | 1 - pandas/tseries/offsets.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/pandas/tests/tseries/offsets/test_offsets.py b/pandas/tests/tseries/offsets/test_offsets.py index d1a390c4cef0a..4396635b5798e 100644 --- a/pandas/tests/tseries/offsets/test_offsets.py +++ b/pandas/tests/tseries/offsets/test_offsets.py @@ -1304,7 +1304,6 @@ def test_opening_time(self, case): datetime(2014, 7, 7, 3, 30, 30): datetime(2014, 7, 4, 22, 30, 30), datetime(2014, 7, 7, 3, 30, 20): datetime(2014, 7, 4, 22, 30, 20)})) - @pytest.mark.parametrize('case', apply_cases) def test_apply(self, case): offset, cases = case diff --git a/pandas/tseries/offsets.py b/pandas/tseries/offsets.py index 25ce497309283..db69a14e22561 100644 --- a/pandas/tseries/offsets.py +++ b/pandas/tseries/offsets.py @@ -731,7 +731,7 @@ def apply(self, other): result = other + timedelta(hours=hours, minutes=minutes) # because of previous adjustment, time will be larger than start - if n >=0: + if n >= 0: bday_edge = self._prev_opening_time(other) + bhdelta if bday_edge < result: bday_remain = result - bday_edge From c97c3edc68161668675184ab6e2d3eae6bbf6063 Mon Sep 17 00:00:00 2001 From: Jiang Yue Date: Tue, 14 May 2019 11:47:28 +0800 Subject: [PATCH 3/6] remove redundant variable --- pandas/tseries/offsets.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pandas/tseries/offsets.py b/pandas/tseries/offsets.py index db69a14e22561..c1764b3845fce 100644 --- a/pandas/tseries/offsets.py +++ b/pandas/tseries/offsets.py @@ -689,7 +689,6 @@ def rollforward(self, dt): @apply_wraps def apply(self, other): - daytime = self._get_daytime_flag businesshours = self._get_business_hours_by_sec bhdelta = timedelta(seconds=businesshours) From 69a5a007d2768d51cb5fbda323bbcfcd6ec0e5f1 Mon Sep 17 00:00:00 2001 From: Jiang Yue Date: Tue, 14 May 2019 16:08:47 +0800 Subject: [PATCH 4/6] add whatsnew entry --- doc/source/whatsnew/v0.25.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index dacd433f112a5..39733bfc1ec1f 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -285,7 +285,7 @@ Timedelta - Bug in :func:`TimedeltaIndex.intersection` where for non-monotonic indices in some cases an empty ``Index`` was returned when in fact an intersection existed (:issue:`25913`) - Bug with comparisons between :class:`Timedelta` and ``NaT`` raising ``TypeError`` (:issue:`26039`) -- +- Bug in adding of business hour to the timestamp with an ending time landing in the next business time period (:pull:`26381`) Timezones ^^^^^^^^^ From 27372d7adb6ff21af7c7d99248755126c96fff1d Mon Sep 17 00:00:00 2001 From: Jiang Yue Date: Wed, 15 May 2019 09:03:43 +0800 Subject: [PATCH 5/6] reference pr in comment --- pandas/tests/tseries/offsets/test_offsets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/tseries/offsets/test_offsets.py b/pandas/tests/tseries/offsets/test_offsets.py index 4396635b5798e..8c8a2f75c4a47 100644 --- a/pandas/tests/tseries/offsets/test_offsets.py +++ b/pandas/tests/tseries/offsets/test_offsets.py @@ -1287,7 +1287,7 @@ def test_opening_time(self, case): datetime(2014, 7, 7, 19, 30): datetime(2014, 7, 5, 4, 30), datetime(2014, 7, 7, 19, 30, 30): datetime(2014, 7, 5, 4, 30, 30)})) - # long business hours + # long business hours (see gh-26381) apply_cases.append((BusinessHour(n=4, start='00:00', end='23:00'), { datetime(2014, 7, 3, 22): datetime(2014, 7, 4, 3), datetime(2014, 7, 4, 22): datetime(2014, 7, 7, 3), From 61079a7e90216307c04797df7a56ab29694cde5b Mon Sep 17 00:00:00 2001 From: Jiang Yue Date: Thu, 16 May 2019 09:20:30 +0800 Subject: [PATCH 6/6] update whatsnew entry --- doc/source/whatsnew/v0.25.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index 39733bfc1ec1f..6f4d589213be2 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -285,7 +285,7 @@ Timedelta - Bug in :func:`TimedeltaIndex.intersection` where for non-monotonic indices in some cases an empty ``Index`` was returned when in fact an intersection existed (:issue:`25913`) - Bug with comparisons between :class:`Timedelta` and ``NaT`` raising ``TypeError`` (:issue:`26039`) -- Bug in adding of business hour to the timestamp with an ending time landing in the next business time period (:pull:`26381`) +- Bug when adding or subtracting a :class:`BusinessHour` to a :class:`Timestamp` with the resulting time landing in a following or prior day respectively (:issue:`26381`) Timezones ^^^^^^^^^