From bb366c9ec46dbfaccc86e6ded74e2215a214b778 Mon Sep 17 00:00:00 2001 From: moink Date: Sun, 3 Jan 2021 17:57:52 +0100 Subject: [PATCH 1/3] TST: 26807 split pandas/tests/tseries/offsets/test_offsets.py into multiple modules --- pandas/tests/tseries/offsets/common.py | 173 +- .../tseries/offsets/test_business_day.py | 441 ++ .../tseries/offsets/test_business_hour.py | 905 ++++ .../offsets/test_custom_business_hour.py | 293 ++ pandas/tests/tseries/offsets/test_dst.py | 175 + pandas/tests/tseries/offsets/test_fiscal.py | 9 +- pandas/tests/tseries/offsets/test_month.py | 838 ++++ pandas/tests/tseries/offsets/test_offsets.py | 3666 +---------------- .../tseries/offsets/test_opening_times.py | 460 +++ pandas/tests/tseries/offsets/test_week.py | 297 ++ .../tests/tseries/offsets/test_yqm_offsets.py | 8 +- 11 files changed, 3601 insertions(+), 3664 deletions(-) create mode 100644 pandas/tests/tseries/offsets/test_business_day.py create mode 100644 pandas/tests/tseries/offsets/test_business_hour.py create mode 100644 pandas/tests/tseries/offsets/test_custom_business_hour.py create mode 100644 pandas/tests/tseries/offsets/test_dst.py create mode 100644 pandas/tests/tseries/offsets/test_month.py create mode 100644 pandas/tests/tseries/offsets/test_opening_times.py create mode 100644 pandas/tests/tseries/offsets/test_week.py diff --git a/pandas/tests/tseries/offsets/common.py b/pandas/tests/tseries/offsets/common.py index 25837c0b6aee2..b2ac28e1865d6 100644 --- a/pandas/tests/tseries/offsets/common.py +++ b/pandas/tests/tseries/offsets/common.py @@ -1,6 +1,24 @@ """ -Assertion helpers for offsets tests +Assertion helpers and base class for offsets tests """ +from datetime import datetime +from typing import Optional, Type + +from dateutil.tz.tz import tzlocal +import pytest + +from pandas._libs.tslibs import OutOfBoundsDatetime, Timestamp +from pandas._libs.tslibs.offsets import ( + FY5253, + BusinessHour, + CustomBusinessHour, + DateOffset, + FY5253Quarter, + LastWeekOfMonth, + Week, + WeekOfMonth, +) +from pandas.compat import IS64 def assert_offset_equal(offset, base, expected): @@ -24,3 +42,156 @@ def assert_is_on_offset(offset, date, expected): f"\nExpected: {expected}\nActual: {actual}\nFor Offset: {offset})" f"\nAt Date: {date}" ) + + +class WeekDay: + MON = 0 + TUE = 1 + WED = 2 + THU = 3 + FRI = 4 + SAT = 5 + SUN = 6 + + +class Base: + _offset: Optional[Type[DateOffset]] = None + d = Timestamp(datetime(2008, 1, 2)) + + timezones = [ + None, + "UTC", + "Asia/Tokyo", + "US/Eastern", + "dateutil/Asia/Tokyo", + "dateutil/US/Pacific", + ] + + def _get_offset(self, klass, value=1, normalize=False): + # create instance from offset class + if klass is FY5253: + klass = klass( + n=value, + startingMonth=1, + weekday=1, + variation="last", + normalize=normalize, + ) + elif klass is FY5253Quarter: + klass = klass( + n=value, + startingMonth=1, + weekday=1, + qtr_with_extra_week=1, + variation="last", + normalize=normalize, + ) + elif klass is LastWeekOfMonth: + klass = klass(n=value, weekday=5, normalize=normalize) + elif klass is WeekOfMonth: + klass = klass(n=value, week=1, weekday=5, normalize=normalize) + elif klass is Week: + klass = klass(n=value, weekday=5, normalize=normalize) + elif klass is DateOffset: + klass = klass(days=value, normalize=normalize) + else: + klass = klass(value, normalize=normalize) + return klass + + def test_apply_out_of_range(self, tz_naive_fixture): + tz = tz_naive_fixture + if self._offset is None: + return + if isinstance(tz, tzlocal) and not IS64: + pytest.xfail(reason="OverflowError inside tzlocal past 2038") + + # try to create an out-of-bounds result timestamp; if we can't create + # the offset skip + try: + if self._offset in (BusinessHour, CustomBusinessHour): + # Using 10000 in BusinessHour fails in tz check because of DST + # difference + offset = self._get_offset(self._offset, value=100000) + else: + offset = self._get_offset(self._offset, value=10000) + + result = Timestamp("20080101") + offset + assert isinstance(result, datetime) + assert result.tzinfo is None + + # Check tz is preserved + t = Timestamp("20080101", tz=tz) + result = t + offset + assert isinstance(result, datetime) + assert t.tzinfo == result.tzinfo + + except OutOfBoundsDatetime: + pass + except (ValueError, KeyError): + # we are creating an invalid offset + # so ignore + pass + + def test_offsets_compare_equal(self): + # root cause of GH#456: __ne__ was not implemented + if self._offset is None: + return + offset1 = self._offset() + offset2 = self._offset() + assert not offset1 != offset2 + assert offset1 == offset2 + + def test_rsub(self): + if self._offset is None or not hasattr(self, "offset2"): + # i.e. skip for TestCommon and YQM subclasses that do not have + # offset2 attr + return + assert self.d - self.offset2 == (-self.offset2).apply(self.d) + + def test_radd(self): + if self._offset is None or not hasattr(self, "offset2"): + # i.e. skip for TestCommon and YQM subclasses that do not have + # offset2 attr + return + assert self.d + self.offset2 == self.offset2 + self.d + + def test_sub(self): + if self._offset is None or not hasattr(self, "offset2"): + # i.e. skip for TestCommon and YQM subclasses that do not have + # offset2 attr + return + off = self.offset2 + msg = "Cannot subtract datetime from offset" + with pytest.raises(TypeError, match=msg): + off - self.d + + assert 2 * off - off == off + assert self.d - self.offset2 == self.d + self._offset(-2) + assert self.d - self.offset2 == self.d - (2 * off - off) + + def testMult1(self): + if self._offset is None or not hasattr(self, "offset1"): + # i.e. skip for TestCommon and YQM subclasses that do not have + # offset1 attr + return + assert self.d + 10 * self.offset1 == self.d + self._offset(10) + assert self.d + 5 * self.offset1 == self.d + self._offset(5) + + def testMult2(self): + if self._offset is None: + return + assert self.d + (-5 * self._offset(-10)) == self.d + self._offset(50) + assert self.d + (-3 * self._offset(-2)) == self.d + self._offset(6) + + def test_compare_str(self): + # GH#23524 + # comparing to strings that cannot be cast to DateOffsets should + # not raise for __eq__ or __ne__ + if self._offset is None: + return + off = self._get_offset(self._offset) + + assert not off == "infer" + assert off != "foo" + # Note: inequalities are only implemented for Tick subclasses; + # tests for this are in test_ticks diff --git a/pandas/tests/tseries/offsets/test_business_day.py b/pandas/tests/tseries/offsets/test_business_day.py new file mode 100644 index 0000000000000..d3c4fb50e2ab0 --- /dev/null +++ b/pandas/tests/tseries/offsets/test_business_day.py @@ -0,0 +1,441 @@ +""" +Tests for offsets.BDay +""" +from datetime import date, datetime, timedelta + +import numpy as np +import pytest + +from pandas._libs.tslibs.offsets import ApplyTypeError, BDay, BMonthEnd, CDay +from pandas.compat.numpy import np_datetime64_compat + +from pandas import DatetimeIndex, _testing as tm, read_pickle +from pandas.tests.tseries.offsets.common import ( + Base, + assert_is_on_offset, + assert_offset_equal, +) +from pandas.tests.tseries.offsets.test_offsets import _ApplyCases + +from pandas.tseries import offsets as offsets +from pandas.tseries.holiday import USFederalHolidayCalendar + + +class TestBusinessDay(Base): + _offset = BDay + + def setup_method(self, method): + self.d = datetime(2008, 1, 1) + + self.offset = BDay() + self.offset1 = self.offset + self.offset2 = BDay(2) + + def test_different_normalize_equals(self): + # GH#21404 changed __eq__ to return False when `normalize` does not match + offset = self._offset() + offset2 = self._offset(normalize=True) + assert offset != offset2 + + def test_repr(self): + assert repr(self.offset) == "" + assert repr(self.offset2) == "<2 * BusinessDays>" + + expected = "" + assert repr(self.offset + timedelta(1)) == expected + + def test_with_offset(self): + offset = self.offset + timedelta(hours=2) + + assert (self.d + offset) == datetime(2008, 1, 2, 2) + + def test_with_offset_index(self): + dti = DatetimeIndex([self.d]) + result = dti + (self.offset + timedelta(hours=2)) + + expected = DatetimeIndex([datetime(2008, 1, 2, 2)]) + tm.assert_index_equal(result, expected) + + def test_eq(self): + assert self.offset2 == self.offset2 + + def test_mul(self): + pass + + def test_hash(self): + assert hash(self.offset2) == hash(self.offset2) + + def test_call(self): + with tm.assert_produces_warning(FutureWarning): + # GH#34171 DateOffset.__call__ is deprecated + assert self.offset2(self.d) == datetime(2008, 1, 3) + + def testRollback1(self): + assert BDay(10).rollback(self.d) == self.d + + def testRollback2(self): + assert BDay(10).rollback(datetime(2008, 1, 5)) == datetime(2008, 1, 4) + + def testRollforward1(self): + assert BDay(10).rollforward(self.d) == self.d + + def testRollforward2(self): + assert BDay(10).rollforward(datetime(2008, 1, 5)) == datetime(2008, 1, 7) + + def test_roll_date_object(self): + offset = BDay() + + dt = date(2012, 9, 15) + + result = offset.rollback(dt) + assert result == datetime(2012, 9, 14) + + result = offset.rollforward(dt) + assert result == datetime(2012, 9, 17) + + offset = offsets.Day() + result = offset.rollback(dt) + assert result == datetime(2012, 9, 15) + + result = offset.rollforward(dt) + assert result == datetime(2012, 9, 15) + + def test_is_on_offset(self): + tests = [ + (BDay(), datetime(2008, 1, 1), True), + (BDay(), datetime(2008, 1, 5), False), + ] + + for offset, d, expected in tests: + assert_is_on_offset(offset, d, expected) + + apply_cases: _ApplyCases = [ + ( + BDay(), + { + datetime(2008, 1, 1): datetime(2008, 1, 2), + datetime(2008, 1, 4): datetime(2008, 1, 7), + datetime(2008, 1, 5): datetime(2008, 1, 7), + datetime(2008, 1, 6): datetime(2008, 1, 7), + datetime(2008, 1, 7): datetime(2008, 1, 8), + }, + ), + ( + 2 * BDay(), + { + datetime(2008, 1, 1): datetime(2008, 1, 3), + datetime(2008, 1, 4): datetime(2008, 1, 8), + datetime(2008, 1, 5): datetime(2008, 1, 8), + datetime(2008, 1, 6): datetime(2008, 1, 8), + datetime(2008, 1, 7): datetime(2008, 1, 9), + }, + ), + ( + -BDay(), + { + datetime(2008, 1, 1): datetime(2007, 12, 31), + datetime(2008, 1, 4): datetime(2008, 1, 3), + datetime(2008, 1, 5): datetime(2008, 1, 4), + datetime(2008, 1, 6): datetime(2008, 1, 4), + datetime(2008, 1, 7): datetime(2008, 1, 4), + datetime(2008, 1, 8): datetime(2008, 1, 7), + }, + ), + ( + -2 * BDay(), + { + datetime(2008, 1, 1): datetime(2007, 12, 28), + datetime(2008, 1, 4): datetime(2008, 1, 2), + datetime(2008, 1, 5): datetime(2008, 1, 3), + datetime(2008, 1, 6): datetime(2008, 1, 3), + datetime(2008, 1, 7): datetime(2008, 1, 3), + datetime(2008, 1, 8): datetime(2008, 1, 4), + datetime(2008, 1, 9): datetime(2008, 1, 7), + }, + ), + ( + BDay(0), + { + datetime(2008, 1, 1): datetime(2008, 1, 1), + datetime(2008, 1, 4): datetime(2008, 1, 4), + datetime(2008, 1, 5): datetime(2008, 1, 7), + datetime(2008, 1, 6): datetime(2008, 1, 7), + datetime(2008, 1, 7): datetime(2008, 1, 7), + }, + ), + ] + + @pytest.mark.parametrize("case", apply_cases) + def test_apply(self, case): + offset, cases = case + for base, expected in cases.items(): + assert_offset_equal(offset, base, expected) + + def test_apply_large_n(self): + dt = datetime(2012, 10, 23) + + result = dt + BDay(10) + assert result == datetime(2012, 11, 6) + + result = dt + BDay(100) - BDay(100) + assert result == dt + + off = BDay() * 6 + rs = datetime(2012, 1, 1) - off + xp = datetime(2011, 12, 23) + assert rs == xp + + st = datetime(2011, 12, 18) + rs = st + off + xp = datetime(2011, 12, 26) + assert rs == xp + + off = BDay() * 10 + rs = datetime(2014, 1, 5) + off # see #5890 + xp = datetime(2014, 1, 17) + assert rs == xp + + def test_apply_corner(self): + msg = "Only know how to combine business day with datetime or timedelta" + with pytest.raises(ApplyTypeError, match=msg): + BDay().apply(BMonthEnd()) + + +class TestCustomBusinessDay(Base): + _offset = CDay + + def setup_method(self, method): + self.d = datetime(2008, 1, 1) + self.nd = np_datetime64_compat("2008-01-01 00:00:00Z") + + self.offset = CDay() + self.offset1 = self.offset + self.offset2 = CDay(2) + + def test_different_normalize_equals(self): + # GH#21404 changed __eq__ to return False when `normalize` does not match + offset = self._offset() + offset2 = self._offset(normalize=True) + assert offset != offset2 + + def test_repr(self): + assert repr(self.offset) == "" + assert repr(self.offset2) == "<2 * CustomBusinessDays>" + + expected = "" + assert repr(self.offset + timedelta(1)) == expected + + def test_with_offset(self): + offset = self.offset + timedelta(hours=2) + + assert (self.d + offset) == datetime(2008, 1, 2, 2) + + def test_with_offset_index(self): + dti = DatetimeIndex([self.d]) + result = dti + (self.offset + timedelta(hours=2)) + + expected = DatetimeIndex([datetime(2008, 1, 2, 2)]) + tm.assert_index_equal(result, expected) + + def test_eq(self): + assert self.offset2 == self.offset2 + + def test_mul(self): + pass + + def test_hash(self): + assert hash(self.offset2) == hash(self.offset2) + + def test_call(self): + with tm.assert_produces_warning(FutureWarning): + # GH#34171 DateOffset.__call__ is deprecated + assert self.offset2(self.d) == datetime(2008, 1, 3) + assert self.offset2(self.nd) == datetime(2008, 1, 3) + + def testRollback1(self): + assert CDay(10).rollback(self.d) == self.d + + def testRollback2(self): + assert CDay(10).rollback(datetime(2008, 1, 5)) == datetime(2008, 1, 4) + + def testRollforward1(self): + assert CDay(10).rollforward(self.d) == self.d + + def testRollforward2(self): + assert CDay(10).rollforward(datetime(2008, 1, 5)) == datetime(2008, 1, 7) + + def test_roll_date_object(self): + offset = CDay() + + dt = date(2012, 9, 15) + + result = offset.rollback(dt) + assert result == datetime(2012, 9, 14) + + result = offset.rollforward(dt) + assert result == datetime(2012, 9, 17) + + offset = offsets.Day() + result = offset.rollback(dt) + assert result == datetime(2012, 9, 15) + + result = offset.rollforward(dt) + assert result == datetime(2012, 9, 15) + + on_offset_cases = [ + (CDay(), datetime(2008, 1, 1), True), + (CDay(), datetime(2008, 1, 5), False), + ] + + @pytest.mark.parametrize("case", on_offset_cases) + def test_is_on_offset(self, case): + offset, d, expected = case + assert_is_on_offset(offset, d, expected) + + apply_cases: _ApplyCases = [ + ( + CDay(), + { + datetime(2008, 1, 1): datetime(2008, 1, 2), + datetime(2008, 1, 4): datetime(2008, 1, 7), + datetime(2008, 1, 5): datetime(2008, 1, 7), + datetime(2008, 1, 6): datetime(2008, 1, 7), + datetime(2008, 1, 7): datetime(2008, 1, 8), + }, + ), + ( + 2 * CDay(), + { + datetime(2008, 1, 1): datetime(2008, 1, 3), + datetime(2008, 1, 4): datetime(2008, 1, 8), + datetime(2008, 1, 5): datetime(2008, 1, 8), + datetime(2008, 1, 6): datetime(2008, 1, 8), + datetime(2008, 1, 7): datetime(2008, 1, 9), + }, + ), + ( + -CDay(), + { + datetime(2008, 1, 1): datetime(2007, 12, 31), + datetime(2008, 1, 4): datetime(2008, 1, 3), + datetime(2008, 1, 5): datetime(2008, 1, 4), + datetime(2008, 1, 6): datetime(2008, 1, 4), + datetime(2008, 1, 7): datetime(2008, 1, 4), + datetime(2008, 1, 8): datetime(2008, 1, 7), + }, + ), + ( + -2 * CDay(), + { + datetime(2008, 1, 1): datetime(2007, 12, 28), + datetime(2008, 1, 4): datetime(2008, 1, 2), + datetime(2008, 1, 5): datetime(2008, 1, 3), + datetime(2008, 1, 6): datetime(2008, 1, 3), + datetime(2008, 1, 7): datetime(2008, 1, 3), + datetime(2008, 1, 8): datetime(2008, 1, 4), + datetime(2008, 1, 9): datetime(2008, 1, 7), + }, + ), + ( + CDay(0), + { + datetime(2008, 1, 1): datetime(2008, 1, 1), + datetime(2008, 1, 4): datetime(2008, 1, 4), + datetime(2008, 1, 5): datetime(2008, 1, 7), + datetime(2008, 1, 6): datetime(2008, 1, 7), + datetime(2008, 1, 7): datetime(2008, 1, 7), + }, + ), + ] + + @pytest.mark.parametrize("case", apply_cases) + def test_apply(self, case): + offset, cases = case + for base, expected in cases.items(): + assert_offset_equal(offset, base, expected) + + def test_apply_large_n(self): + dt = datetime(2012, 10, 23) + + result = dt + CDay(10) + assert result == datetime(2012, 11, 6) + + result = dt + CDay(100) - CDay(100) + assert result == dt + + off = CDay() * 6 + rs = datetime(2012, 1, 1) - off + xp = datetime(2011, 12, 23) + assert rs == xp + + st = datetime(2011, 12, 18) + rs = st + off + xp = datetime(2011, 12, 26) + assert rs == xp + + def test_apply_corner(self): + msg = ( + "Only know how to combine trading day " + "with datetime, datetime64 or timedelta" + ) + with pytest.raises(ApplyTypeError, match=msg): + CDay().apply(BMonthEnd()) + + def test_holidays(self): + # Define a TradingDay offset + holidays = ["2012-05-01", datetime(2013, 5, 1), np.datetime64("2014-05-01")] + tday = CDay(holidays=holidays) + for year in range(2012, 2015): + dt = datetime(year, 4, 30) + xp = datetime(year, 5, 2) + rs = dt + tday + assert rs == xp + + def test_weekmask(self): + weekmask_saudi = "Sat Sun Mon Tue Wed" # Thu-Fri Weekend + weekmask_uae = "1111001" # Fri-Sat Weekend + weekmask_egypt = [1, 1, 1, 1, 0, 0, 1] # Fri-Sat Weekend + bday_saudi = CDay(weekmask=weekmask_saudi) + bday_uae = CDay(weekmask=weekmask_uae) + bday_egypt = CDay(weekmask=weekmask_egypt) + dt = datetime(2013, 5, 1) + xp_saudi = datetime(2013, 5, 4) + xp_uae = datetime(2013, 5, 2) + xp_egypt = datetime(2013, 5, 2) + assert xp_saudi == dt + bday_saudi + assert xp_uae == dt + bday_uae + assert xp_egypt == dt + bday_egypt + xp2 = datetime(2013, 5, 5) + assert xp2 == dt + 2 * bday_saudi + assert xp2 == dt + 2 * bday_uae + assert xp2 == dt + 2 * bday_egypt + + def test_weekmask_and_holidays(self): + weekmask_egypt = "Sun Mon Tue Wed Thu" # Fri-Sat Weekend + holidays = ["2012-05-01", datetime(2013, 5, 1), np.datetime64("2014-05-01")] + bday_egypt = CDay(holidays=holidays, weekmask=weekmask_egypt) + dt = datetime(2013, 4, 30) + xp_egypt = datetime(2013, 5, 5) + assert xp_egypt == dt + 2 * bday_egypt + + @pytest.mark.filterwarnings("ignore:Non:pandas.errors.PerformanceWarning") + def test_calendar(self): + calendar = USFederalHolidayCalendar() + dt = datetime(2014, 1, 17) + assert_offset_equal(CDay(calendar=calendar), dt, datetime(2014, 1, 21)) + + def test_roundtrip_pickle(self): + def _check_roundtrip(obj): + unpickled = tm.round_trip_pickle(obj) + assert unpickled == obj + + _check_roundtrip(self.offset) + _check_roundtrip(self.offset2) + _check_roundtrip(self.offset * 2) + + def test_pickle_compat_0_14_1(self, datapath): + hdays = [datetime(2013, 1, 1) for ele in range(4)] + pth = datapath("tseries", "offsets", "data", "cday-0.14.1.pickle") + cday0_14_1 = read_pickle(pth) + cday = CDay(holidays=hdays) + assert cday == cday0_14_1 diff --git a/pandas/tests/tseries/offsets/test_business_hour.py b/pandas/tests/tseries/offsets/test_business_hour.py new file mode 100644 index 0000000000000..5f387b2edeb0b --- /dev/null +++ b/pandas/tests/tseries/offsets/test_business_hour.py @@ -0,0 +1,905 @@ +""" +Tests for offsets.BusinessHour +""" +from datetime import datetime, time as dt_time + +import pytest + +from pandas._libs.tslibs import Timedelta, Timestamp +from pandas._libs.tslibs.offsets import BDay, BusinessHour, Nano + +from pandas import DatetimeIndex, _testing as tm, date_range +from pandas.tests.tseries.offsets.common import Base, assert_offset_equal + + +class TestBusinessHour(Base): + _offset = BusinessHour + + def setup_method(self, method): + self.d = datetime(2014, 7, 1, 10, 00) + + self.offset1 = BusinessHour() + self.offset2 = BusinessHour(n=3) + + self.offset3 = BusinessHour(n=-1) + self.offset4 = BusinessHour(n=-4) + + from datetime import time as dt_time + + self.offset5 = BusinessHour(start=dt_time(11, 0), end=dt_time(14, 30)) + self.offset6 = BusinessHour(start="20:00", end="05:00") + self.offset7 = BusinessHour(n=-2, start=dt_time(21, 30), end=dt_time(6, 30)) + self.offset8 = BusinessHour(start=["09:00", "13:00"], end=["12:00", "17:00"]) + self.offset9 = BusinessHour( + n=3, start=["09:00", "22:00"], end=["13:00", "03:00"] + ) + self.offset10 = BusinessHour( + n=-1, start=["23:00", "13:00"], end=["02:00", "17:00"] + ) + + @pytest.mark.parametrize( + "start,end,match", + [ + ( + dt_time(11, 0, 5), + "17:00", + "time data must be specified only with hour and minute", + ), + ("AAA", "17:00", "time data must match '%H:%M' format"), + ("14:00:05", "17:00", "time data must match '%H:%M' format"), + ([], "17:00", "Must include at least 1 start time"), + ("09:00", [], "Must include at least 1 end time"), + ( + ["09:00", "11:00"], + "17:00", + "number of starting time and ending time must be the same", + ), + ( + ["09:00", "11:00"], + ["10:00"], + "number of starting time and ending time must be the same", + ), + ( + ["09:00", "11:00"], + ["12:00", "20:00"], + r"invalid starting and ending time\(s\): opening hours should not " + "touch or overlap with one another", + ), + ( + ["12:00", "20:00"], + ["09:00", "11:00"], + r"invalid starting and ending time\(s\): opening hours should not " + "touch or overlap with one another", + ), + ], + ) + def test_constructor_errors(self, start, end, match): + with pytest.raises(ValueError, match=match): + BusinessHour(start=start, end=end) + + def test_different_normalize_equals(self): + # GH#21404 changed __eq__ to return False when `normalize` does not match + offset = self._offset() + offset2 = self._offset(normalize=True) + assert offset != offset2 + + def test_repr(self): + assert repr(self.offset1) == "" + assert repr(self.offset2) == "<3 * BusinessHours: BH=09:00-17:00>" + assert repr(self.offset3) == "<-1 * BusinessHour: BH=09:00-17:00>" + assert repr(self.offset4) == "<-4 * BusinessHours: BH=09:00-17:00>" + + assert repr(self.offset5) == "" + assert repr(self.offset6) == "" + assert repr(self.offset7) == "<-2 * BusinessHours: BH=21:30-06:30>" + assert repr(self.offset8) == "" + assert repr(self.offset9) == "<3 * BusinessHours: BH=09:00-13:00,22:00-03:00>" + assert repr(self.offset10) == "<-1 * BusinessHour: BH=13:00-17:00,23:00-02:00>" + + def test_with_offset(self): + expected = Timestamp("2014-07-01 13:00") + + assert self.d + BusinessHour() * 3 == expected + assert self.d + BusinessHour(n=3) == expected + + @pytest.mark.parametrize( + "offset_name", + ["offset1", "offset2", "offset3", "offset4", "offset8", "offset9", "offset10"], + ) + def test_eq_attribute(self, offset_name): + offset = getattr(self, offset_name) + assert offset == offset + + @pytest.mark.parametrize( + "offset1,offset2", + [ + (BusinessHour(start="09:00"), BusinessHour()), + ( + BusinessHour(start=["23:00", "13:00"], end=["12:00", "17:00"]), + BusinessHour(start=["13:00", "23:00"], end=["17:00", "12:00"]), + ), + ], + ) + def test_eq(self, offset1, offset2): + assert offset1 == offset2 + + @pytest.mark.parametrize( + "offset1,offset2", + [ + (BusinessHour(), BusinessHour(-1)), + (BusinessHour(start="09:00"), BusinessHour(start="09:01")), + ( + BusinessHour(start="09:00", end="17:00"), + BusinessHour(start="17:00", end="09:01"), + ), + ( + BusinessHour(start=["13:00", "23:00"], end=["18:00", "07:00"]), + BusinessHour(start=["13:00", "23:00"], end=["17:00", "12:00"]), + ), + ], + ) + def test_neq(self, offset1, offset2): + assert offset1 != offset2 + + @pytest.mark.parametrize( + "offset_name", + ["offset1", "offset2", "offset3", "offset4", "offset8", "offset9", "offset10"], + ) + def test_hash(self, offset_name): + offset = getattr(self, offset_name) + assert offset == offset + + def test_call(self): + with tm.assert_produces_warning(FutureWarning): + # GH#34171 DateOffset.__call__ is deprecated + assert self.offset1(self.d) == datetime(2014, 7, 1, 11) + assert self.offset2(self.d) == datetime(2014, 7, 1, 13) + assert self.offset3(self.d) == datetime(2014, 6, 30, 17) + assert self.offset4(self.d) == datetime(2014, 6, 30, 14) + assert self.offset8(self.d) == datetime(2014, 7, 1, 11) + assert self.offset9(self.d) == datetime(2014, 7, 1, 22) + assert self.offset10(self.d) == datetime(2014, 7, 1, 1) + + def test_sub(self): + # we have to override test_sub here because self.offset2 is not + # defined as self._offset(2) + off = self.offset2 + msg = "Cannot subtract datetime from offset" + with pytest.raises(TypeError, match=msg): + off - self.d + assert 2 * off - off == off + + assert self.d - self.offset2 == self.d + self._offset(-3) + + def testRollback1(self): + assert self.offset1.rollback(self.d) == self.d + assert self.offset2.rollback(self.d) == self.d + assert self.offset3.rollback(self.d) == self.d + assert self.offset4.rollback(self.d) == self.d + assert self.offset5.rollback(self.d) == datetime(2014, 6, 30, 14, 30) + assert self.offset6.rollback(self.d) == datetime(2014, 7, 1, 5, 0) + assert self.offset7.rollback(self.d) == datetime(2014, 7, 1, 6, 30) + assert self.offset8.rollback(self.d) == self.d + assert self.offset9.rollback(self.d) == self.d + assert self.offset10.rollback(self.d) == datetime(2014, 7, 1, 2) + + d = datetime(2014, 7, 1, 0) + assert self.offset1.rollback(d) == datetime(2014, 6, 30, 17) + assert self.offset2.rollback(d) == datetime(2014, 6, 30, 17) + assert self.offset3.rollback(d) == datetime(2014, 6, 30, 17) + assert self.offset4.rollback(d) == datetime(2014, 6, 30, 17) + assert self.offset5.rollback(d) == datetime(2014, 6, 30, 14, 30) + assert self.offset6.rollback(d) == d + assert self.offset7.rollback(d) == d + assert self.offset8.rollback(d) == datetime(2014, 6, 30, 17) + assert self.offset9.rollback(d) == d + assert self.offset10.rollback(d) == d + + assert self._offset(5).rollback(self.d) == self.d + + def testRollback2(self): + assert self._offset(-3).rollback(datetime(2014, 7, 5, 15, 0)) == datetime( + 2014, 7, 4, 17, 0 + ) + + def testRollforward1(self): + assert self.offset1.rollforward(self.d) == self.d + assert self.offset2.rollforward(self.d) == self.d + assert self.offset3.rollforward(self.d) == self.d + assert self.offset4.rollforward(self.d) == self.d + assert self.offset5.rollforward(self.d) == datetime(2014, 7, 1, 11, 0) + assert self.offset6.rollforward(self.d) == datetime(2014, 7, 1, 20, 0) + assert self.offset7.rollforward(self.d) == datetime(2014, 7, 1, 21, 30) + assert self.offset8.rollforward(self.d) == self.d + assert self.offset9.rollforward(self.d) == self.d + assert self.offset10.rollforward(self.d) == datetime(2014, 7, 1, 13) + + d = datetime(2014, 7, 1, 0) + assert self.offset1.rollforward(d) == datetime(2014, 7, 1, 9) + assert self.offset2.rollforward(d) == datetime(2014, 7, 1, 9) + assert self.offset3.rollforward(d) == datetime(2014, 7, 1, 9) + assert self.offset4.rollforward(d) == datetime(2014, 7, 1, 9) + assert self.offset5.rollforward(d) == datetime(2014, 7, 1, 11) + assert self.offset6.rollforward(d) == d + assert self.offset7.rollforward(d) == d + assert self.offset8.rollforward(d) == datetime(2014, 7, 1, 9) + assert self.offset9.rollforward(d) == d + assert self.offset10.rollforward(d) == d + + assert self._offset(5).rollforward(self.d) == self.d + + def testRollforward2(self): + assert self._offset(-3).rollforward(datetime(2014, 7, 5, 16, 0)) == datetime( + 2014, 7, 7, 9 + ) + + def test_roll_date_object(self): + offset = BusinessHour() + + dt = datetime(2014, 7, 6, 15, 0) + + result = offset.rollback(dt) + assert result == datetime(2014, 7, 4, 17) + + result = offset.rollforward(dt) + assert result == datetime(2014, 7, 7, 9) + + normalize_cases = [] + normalize_cases.append( + ( + BusinessHour(normalize=True), + { + datetime(2014, 7, 1, 8): datetime(2014, 7, 1), + datetime(2014, 7, 1, 17): datetime(2014, 7, 2), + datetime(2014, 7, 1, 16): datetime(2014, 7, 2), + datetime(2014, 7, 1, 23): datetime(2014, 7, 2), + datetime(2014, 7, 1, 0): datetime(2014, 7, 1), + datetime(2014, 7, 4, 15): datetime(2014, 7, 4), + datetime(2014, 7, 4, 15, 59): datetime(2014, 7, 4), + datetime(2014, 7, 4, 16, 30): datetime(2014, 7, 7), + datetime(2014, 7, 5, 23): datetime(2014, 7, 7), + datetime(2014, 7, 6, 10): datetime(2014, 7, 7), + }, + ) + ) + + normalize_cases.append( + ( + BusinessHour(-1, normalize=True), + { + datetime(2014, 7, 1, 8): datetime(2014, 6, 30), + datetime(2014, 7, 1, 17): datetime(2014, 7, 1), + datetime(2014, 7, 1, 16): datetime(2014, 7, 1), + datetime(2014, 7, 1, 10): datetime(2014, 6, 30), + datetime(2014, 7, 1, 0): datetime(2014, 6, 30), + datetime(2014, 7, 7, 10): datetime(2014, 7, 4), + datetime(2014, 7, 7, 10, 1): datetime(2014, 7, 7), + datetime(2014, 7, 5, 23): datetime(2014, 7, 4), + datetime(2014, 7, 6, 10): datetime(2014, 7, 4), + }, + ) + ) + + normalize_cases.append( + ( + BusinessHour(1, normalize=True, start="17:00", end="04:00"), + { + datetime(2014, 7, 1, 8): datetime(2014, 7, 1), + datetime(2014, 7, 1, 17): datetime(2014, 7, 1), + datetime(2014, 7, 1, 23): datetime(2014, 7, 2), + datetime(2014, 7, 2, 2): datetime(2014, 7, 2), + datetime(2014, 7, 2, 3): datetime(2014, 7, 2), + datetime(2014, 7, 4, 23): datetime(2014, 7, 5), + datetime(2014, 7, 5, 2): datetime(2014, 7, 5), + datetime(2014, 7, 7, 2): datetime(2014, 7, 7), + datetime(2014, 7, 7, 17): datetime(2014, 7, 7), + }, + ) + ) + + @pytest.mark.parametrize("case", normalize_cases) + def test_normalize(self, case): + offset, cases = case + for dt, expected in cases.items(): + assert offset.apply(dt) == expected + + on_offset_cases = [] + on_offset_cases.append( + ( + BusinessHour(), + { + datetime(2014, 7, 1, 9): True, + datetime(2014, 7, 1, 8, 59): False, + datetime(2014, 7, 1, 8): False, + datetime(2014, 7, 1, 17): True, + datetime(2014, 7, 1, 17, 1): False, + datetime(2014, 7, 1, 18): False, + datetime(2014, 7, 5, 9): False, + datetime(2014, 7, 6, 12): False, + }, + ) + ) + + on_offset_cases.append( + ( + BusinessHour(start="10:00", end="15:00"), + { + datetime(2014, 7, 1, 9): False, + datetime(2014, 7, 1, 10): True, + datetime(2014, 7, 1, 15): True, + datetime(2014, 7, 1, 15, 1): False, + datetime(2014, 7, 5, 12): False, + datetime(2014, 7, 6, 12): False, + }, + ) + ) + + on_offset_cases.append( + ( + BusinessHour(start="19:00", end="05:00"), + { + datetime(2014, 7, 1, 9, 0): False, + datetime(2014, 7, 1, 10, 0): False, + datetime(2014, 7, 1, 15): False, + datetime(2014, 7, 1, 15, 1): False, + datetime(2014, 7, 5, 12, 0): False, + datetime(2014, 7, 6, 12, 0): False, + datetime(2014, 7, 1, 19, 0): True, + datetime(2014, 7, 2, 0, 0): True, + datetime(2014, 7, 4, 23): True, + datetime(2014, 7, 5, 1): True, + datetime(2014, 7, 5, 5, 0): True, + datetime(2014, 7, 6, 23, 0): False, + datetime(2014, 7, 7, 3, 0): False, + }, + ) + ) + + on_offset_cases.append( + ( + BusinessHour(start=["09:00", "13:00"], end=["12:00", "17:00"]), + { + datetime(2014, 7, 1, 9): True, + datetime(2014, 7, 1, 8, 59): False, + datetime(2014, 7, 1, 8): False, + datetime(2014, 7, 1, 17): True, + datetime(2014, 7, 1, 17, 1): False, + datetime(2014, 7, 1, 18): False, + datetime(2014, 7, 5, 9): False, + datetime(2014, 7, 6, 12): False, + datetime(2014, 7, 1, 12, 30): False, + }, + ) + ) + + on_offset_cases.append( + ( + BusinessHour(start=["19:00", "23:00"], end=["21:00", "05:00"]), + { + datetime(2014, 7, 1, 9, 0): False, + datetime(2014, 7, 1, 10, 0): False, + datetime(2014, 7, 1, 15): False, + datetime(2014, 7, 1, 15, 1): False, + datetime(2014, 7, 5, 12, 0): False, + datetime(2014, 7, 6, 12, 0): False, + datetime(2014, 7, 1, 19, 0): True, + datetime(2014, 7, 2, 0, 0): True, + datetime(2014, 7, 4, 23): True, + datetime(2014, 7, 5, 1): True, + datetime(2014, 7, 5, 5, 0): True, + datetime(2014, 7, 6, 23, 0): False, + datetime(2014, 7, 7, 3, 0): False, + datetime(2014, 7, 4, 22): False, + }, + ) + ) + + @pytest.mark.parametrize("case", on_offset_cases) + def test_is_on_offset(self, case): + offset, cases = case + for dt, expected in cases.items(): + assert offset.is_on_offset(dt) == expected + + apply_cases = [ + ( + BusinessHour(), + { + datetime(2014, 7, 1, 11): datetime(2014, 7, 1, 12), + datetime(2014, 7, 1, 13): datetime(2014, 7, 1, 14), + datetime(2014, 7, 1, 15): datetime(2014, 7, 1, 16), + datetime(2014, 7, 1, 19): datetime(2014, 7, 2, 10), + datetime(2014, 7, 1, 16): datetime(2014, 7, 2, 9), + datetime(2014, 7, 1, 16, 30, 15): datetime(2014, 7, 2, 9, 30, 15), + datetime(2014, 7, 1, 17): datetime(2014, 7, 2, 10), + datetime(2014, 7, 2, 11): datetime(2014, 7, 2, 12), + # out of business hours + datetime(2014, 7, 2, 8): datetime(2014, 7, 2, 10), + datetime(2014, 7, 2, 19): datetime(2014, 7, 3, 10), + datetime(2014, 7, 2, 23): datetime(2014, 7, 3, 10), + datetime(2014, 7, 3, 0): datetime(2014, 7, 3, 10), + # saturday + datetime(2014, 7, 5, 15): datetime(2014, 7, 7, 10), + datetime(2014, 7, 4, 17): datetime(2014, 7, 7, 10), + datetime(2014, 7, 4, 16, 30): datetime(2014, 7, 7, 9, 30), + datetime(2014, 7, 4, 16, 30, 30): datetime(2014, 7, 7, 9, 30, 30), + }, + ), + ( + BusinessHour(4), + { + datetime(2014, 7, 1, 11): datetime(2014, 7, 1, 15), + datetime(2014, 7, 1, 13): datetime(2014, 7, 2, 9), + datetime(2014, 7, 1, 15): datetime(2014, 7, 2, 11), + datetime(2014, 7, 1, 16): datetime(2014, 7, 2, 12), + datetime(2014, 7, 1, 17): datetime(2014, 7, 2, 13), + datetime(2014, 7, 2, 11): datetime(2014, 7, 2, 15), + datetime(2014, 7, 2, 8): datetime(2014, 7, 2, 13), + datetime(2014, 7, 2, 19): datetime(2014, 7, 3, 13), + datetime(2014, 7, 2, 23): datetime(2014, 7, 3, 13), + datetime(2014, 7, 3, 0): datetime(2014, 7, 3, 13), + datetime(2014, 7, 5, 15): datetime(2014, 7, 7, 13), + datetime(2014, 7, 4, 17): datetime(2014, 7, 7, 13), + datetime(2014, 7, 4, 16, 30): datetime(2014, 7, 7, 12, 30), + datetime(2014, 7, 4, 16, 30, 30): datetime(2014, 7, 7, 12, 30, 30), + }, + ), + ( + BusinessHour(-1), + { + datetime(2014, 7, 1, 11): datetime(2014, 7, 1, 10), + datetime(2014, 7, 1, 13): datetime(2014, 7, 1, 12), + datetime(2014, 7, 1, 15): datetime(2014, 7, 1, 14), + datetime(2014, 7, 1, 16): datetime(2014, 7, 1, 15), + datetime(2014, 7, 1, 10): datetime(2014, 6, 30, 17), + datetime(2014, 7, 1, 16, 30, 15): datetime(2014, 7, 1, 15, 30, 15), + datetime(2014, 7, 1, 9, 30, 15): datetime(2014, 6, 30, 16, 30, 15), + datetime(2014, 7, 1, 17): datetime(2014, 7, 1, 16), + datetime(2014, 7, 1, 5): datetime(2014, 6, 30, 16), + datetime(2014, 7, 2, 11): datetime(2014, 7, 2, 10), + # out of business hours + datetime(2014, 7, 2, 8): datetime(2014, 7, 1, 16), + datetime(2014, 7, 2, 19): datetime(2014, 7, 2, 16), + datetime(2014, 7, 2, 23): datetime(2014, 7, 2, 16), + datetime(2014, 7, 3, 0): datetime(2014, 7, 2, 16), + # saturday + datetime(2014, 7, 5, 15): datetime(2014, 7, 4, 16), + datetime(2014, 7, 7, 9): datetime(2014, 7, 4, 16), + datetime(2014, 7, 7, 9, 30): datetime(2014, 7, 4, 16, 30), + datetime(2014, 7, 7, 9, 30, 30): datetime(2014, 7, 4, 16, 30, 30), + }, + ), + ( + BusinessHour(-4), + { + datetime(2014, 7, 1, 11): datetime(2014, 6, 30, 15), + datetime(2014, 7, 1, 13): datetime(2014, 6, 30, 17), + datetime(2014, 7, 1, 15): datetime(2014, 7, 1, 11), + datetime(2014, 7, 1, 16): datetime(2014, 7, 1, 12), + datetime(2014, 7, 1, 17): datetime(2014, 7, 1, 13), + datetime(2014, 7, 2, 11): datetime(2014, 7, 1, 15), + datetime(2014, 7, 2, 8): datetime(2014, 7, 1, 13), + datetime(2014, 7, 2, 19): datetime(2014, 7, 2, 13), + datetime(2014, 7, 2, 23): datetime(2014, 7, 2, 13), + datetime(2014, 7, 3, 0): datetime(2014, 7, 2, 13), + datetime(2014, 7, 5, 15): datetime(2014, 7, 4, 13), + datetime(2014, 7, 4, 18): datetime(2014, 7, 4, 13), + datetime(2014, 7, 7, 9, 30): datetime(2014, 7, 4, 13, 30), + datetime(2014, 7, 7, 9, 30, 30): datetime(2014, 7, 4, 13, 30, 30), + }, + ), + ( + BusinessHour(start="13:00", end="16:00"), + { + datetime(2014, 7, 1, 11): datetime(2014, 7, 1, 14), + datetime(2014, 7, 1, 13): datetime(2014, 7, 1, 14), + datetime(2014, 7, 1, 15): datetime(2014, 7, 2, 13), + datetime(2014, 7, 1, 19): datetime(2014, 7, 2, 14), + datetime(2014, 7, 1, 16): datetime(2014, 7, 2, 14), + datetime(2014, 7, 1, 15, 30, 15): datetime(2014, 7, 2, 13, 30, 15), + datetime(2014, 7, 5, 15): datetime(2014, 7, 7, 14), + datetime(2014, 7, 4, 17): datetime(2014, 7, 7, 14), + }, + ), + ( + BusinessHour(n=2, start="13:00", end="16:00"), + { + datetime(2014, 7, 1, 17): datetime(2014, 7, 2, 15), + datetime(2014, 7, 2, 14): datetime(2014, 7, 3, 13), + datetime(2014, 7, 2, 8): datetime(2014, 7, 2, 15), + datetime(2014, 7, 2, 19): datetime(2014, 7, 3, 15), + datetime(2014, 7, 2, 14, 30): datetime(2014, 7, 3, 13, 30), + datetime(2014, 7, 3, 0): datetime(2014, 7, 3, 15), + datetime(2014, 7, 5, 15): datetime(2014, 7, 7, 15), + datetime(2014, 7, 4, 17): datetime(2014, 7, 7, 15), + datetime(2014, 7, 4, 14, 30): datetime(2014, 7, 7, 13, 30), + datetime(2014, 7, 4, 14, 30, 30): datetime(2014, 7, 7, 13, 30, 30), + }, + ), + ( + BusinessHour(n=-1, start="13:00", end="16:00"), + { + datetime(2014, 7, 2, 11): datetime(2014, 7, 1, 15), + datetime(2014, 7, 2, 13): datetime(2014, 7, 1, 15), + datetime(2014, 7, 2, 14): datetime(2014, 7, 1, 16), + datetime(2014, 7, 2, 15): datetime(2014, 7, 2, 14), + datetime(2014, 7, 2, 19): datetime(2014, 7, 2, 15), + datetime(2014, 7, 2, 16): datetime(2014, 7, 2, 15), + datetime(2014, 7, 2, 13, 30, 15): datetime(2014, 7, 1, 15, 30, 15), + datetime(2014, 7, 5, 15): datetime(2014, 7, 4, 15), + datetime(2014, 7, 7, 11): datetime(2014, 7, 4, 15), + }, + ), + ( + BusinessHour(n=-3, start="10:00", end="16:00"), + { + datetime(2014, 7, 1, 17): datetime(2014, 7, 1, 13), + datetime(2014, 7, 2, 14): datetime(2014, 7, 2, 11), + datetime(2014, 7, 2, 8): datetime(2014, 7, 1, 13), + datetime(2014, 7, 2, 13): datetime(2014, 7, 1, 16), + datetime(2014, 7, 2, 19): datetime(2014, 7, 2, 13), + datetime(2014, 7, 2, 11, 30): datetime(2014, 7, 1, 14, 30), + datetime(2014, 7, 3, 0): datetime(2014, 7, 2, 13), + datetime(2014, 7, 4, 10): datetime(2014, 7, 3, 13), + datetime(2014, 7, 5, 15): datetime(2014, 7, 4, 13), + datetime(2014, 7, 4, 16): datetime(2014, 7, 4, 13), + datetime(2014, 7, 4, 12, 30): datetime(2014, 7, 3, 15, 30), + datetime(2014, 7, 4, 12, 30, 30): datetime(2014, 7, 3, 15, 30, 30), + }, + ), + ( + BusinessHour(start="19:00", end="05:00"), + { + datetime(2014, 7, 1, 17): datetime(2014, 7, 1, 20), + datetime(2014, 7, 2, 14): datetime(2014, 7, 2, 20), + datetime(2014, 7, 2, 8): datetime(2014, 7, 2, 20), + datetime(2014, 7, 2, 13): datetime(2014, 7, 2, 20), + datetime(2014, 7, 2, 19): datetime(2014, 7, 2, 20), + datetime(2014, 7, 2, 4, 30): datetime(2014, 7, 2, 19, 30), + datetime(2014, 7, 3, 0): datetime(2014, 7, 3, 1), + datetime(2014, 7, 4, 10): datetime(2014, 7, 4, 20), + datetime(2014, 7, 4, 23): datetime(2014, 7, 5, 0), + datetime(2014, 7, 5, 0): datetime(2014, 7, 5, 1), + datetime(2014, 7, 5, 4): datetime(2014, 7, 7, 19), + datetime(2014, 7, 5, 4, 30): datetime(2014, 7, 7, 19, 30), + datetime(2014, 7, 5, 4, 30, 30): datetime(2014, 7, 7, 19, 30, 30), + }, + ), + ( + BusinessHour(n=-1, start="19:00", end="05:00"), + { + datetime(2014, 7, 1, 17): datetime(2014, 7, 1, 4), + datetime(2014, 7, 2, 14): datetime(2014, 7, 2, 4), + datetime(2014, 7, 2, 8): datetime(2014, 7, 2, 4), + datetime(2014, 7, 2, 13): datetime(2014, 7, 2, 4), + datetime(2014, 7, 2, 20): datetime(2014, 7, 2, 5), + datetime(2014, 7, 2, 19): datetime(2014, 7, 2, 4), + datetime(2014, 7, 2, 19, 30): datetime(2014, 7, 2, 4, 30), + datetime(2014, 7, 3, 0): datetime(2014, 7, 2, 23), + datetime(2014, 7, 3, 6): datetime(2014, 7, 3, 4), + datetime(2014, 7, 4, 23): datetime(2014, 7, 4, 22), + datetime(2014, 7, 5, 0): datetime(2014, 7, 4, 23), + datetime(2014, 7, 5, 4): datetime(2014, 7, 5, 3), + 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), + }, + ), + ( + 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), + }, + ), + ( + 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), + }, + ), + ( + BusinessHour(start=["09:00", "14:00"], end=["12:00", "18:00"]), + { + datetime(2014, 7, 1, 11): datetime(2014, 7, 1, 14), + datetime(2014, 7, 1, 15): datetime(2014, 7, 1, 16), + datetime(2014, 7, 1, 19): datetime(2014, 7, 2, 10), + datetime(2014, 7, 1, 16): datetime(2014, 7, 1, 17), + datetime(2014, 7, 1, 16, 30, 15): datetime(2014, 7, 1, 17, 30, 15), + datetime(2014, 7, 1, 17): datetime(2014, 7, 2, 9), + datetime(2014, 7, 2, 11): datetime(2014, 7, 2, 14), + # out of business hours + datetime(2014, 7, 1, 13): datetime(2014, 7, 1, 15), + datetime(2014, 7, 2, 8): datetime(2014, 7, 2, 10), + datetime(2014, 7, 2, 19): datetime(2014, 7, 3, 10), + datetime(2014, 7, 2, 23): datetime(2014, 7, 3, 10), + datetime(2014, 7, 3, 0): datetime(2014, 7, 3, 10), + # saturday + datetime(2014, 7, 5, 15): datetime(2014, 7, 7, 10), + datetime(2014, 7, 4, 17): datetime(2014, 7, 7, 9), + datetime(2014, 7, 4, 17, 30): datetime(2014, 7, 7, 9, 30), + datetime(2014, 7, 4, 17, 30, 30): datetime(2014, 7, 7, 9, 30, 30), + }, + ), + ( + BusinessHour(n=4, start=["09:00", "14:00"], end=["12:00", "18:00"]), + { + datetime(2014, 7, 1, 11): datetime(2014, 7, 1, 17), + datetime(2014, 7, 1, 13): datetime(2014, 7, 2, 9), + datetime(2014, 7, 1, 15): datetime(2014, 7, 2, 10), + datetime(2014, 7, 1, 16): datetime(2014, 7, 2, 11), + datetime(2014, 7, 1, 17): datetime(2014, 7, 2, 14), + datetime(2014, 7, 2, 11): datetime(2014, 7, 2, 17), + datetime(2014, 7, 2, 8): datetime(2014, 7, 2, 15), + datetime(2014, 7, 2, 19): datetime(2014, 7, 3, 15), + datetime(2014, 7, 2, 23): datetime(2014, 7, 3, 15), + datetime(2014, 7, 3, 0): datetime(2014, 7, 3, 15), + datetime(2014, 7, 5, 15): datetime(2014, 7, 7, 15), + datetime(2014, 7, 4, 17): datetime(2014, 7, 7, 14), + datetime(2014, 7, 4, 16, 30): datetime(2014, 7, 7, 11, 30), + datetime(2014, 7, 4, 16, 30, 30): datetime(2014, 7, 7, 11, 30, 30), + }, + ), + ( + BusinessHour(n=-4, start=["09:00", "14:00"], end=["12:00", "18:00"]), + { + datetime(2014, 7, 1, 11): datetime(2014, 6, 30, 16), + datetime(2014, 7, 1, 13): datetime(2014, 6, 30, 17), + datetime(2014, 7, 1, 15): datetime(2014, 6, 30, 18), + datetime(2014, 7, 1, 16): datetime(2014, 7, 1, 10), + datetime(2014, 7, 1, 17): datetime(2014, 7, 1, 11), + datetime(2014, 7, 2, 11): datetime(2014, 7, 1, 16), + datetime(2014, 7, 2, 8): datetime(2014, 7, 1, 12), + datetime(2014, 7, 2, 19): datetime(2014, 7, 2, 12), + datetime(2014, 7, 2, 23): datetime(2014, 7, 2, 12), + datetime(2014, 7, 3, 0): datetime(2014, 7, 2, 12), + datetime(2014, 7, 5, 15): datetime(2014, 7, 4, 12), + datetime(2014, 7, 4, 18): datetime(2014, 7, 4, 12), + datetime(2014, 7, 7, 9, 30): datetime(2014, 7, 4, 14, 30), + datetime(2014, 7, 7, 9, 30, 30): datetime(2014, 7, 4, 14, 30, 30), + }, + ), + ( + BusinessHour(n=-1, start=["19:00", "03:00"], end=["01:00", "05:00"]), + { + datetime(2014, 7, 1, 17): datetime(2014, 7, 1, 4), + datetime(2014, 7, 2, 14): datetime(2014, 7, 2, 4), + datetime(2014, 7, 2, 8): datetime(2014, 7, 2, 4), + datetime(2014, 7, 2, 13): datetime(2014, 7, 2, 4), + datetime(2014, 7, 2, 20): datetime(2014, 7, 2, 5), + datetime(2014, 7, 2, 19): datetime(2014, 7, 2, 4), + datetime(2014, 7, 2, 4): datetime(2014, 7, 2, 1), + datetime(2014, 7, 2, 19, 30): datetime(2014, 7, 2, 4, 30), + datetime(2014, 7, 3, 0): datetime(2014, 7, 2, 23), + datetime(2014, 7, 3, 6): datetime(2014, 7, 3, 4), + datetime(2014, 7, 4, 23): datetime(2014, 7, 4, 22), + datetime(2014, 7, 5, 0): datetime(2014, 7, 4, 23), + datetime(2014, 7, 5, 4): datetime(2014, 7, 5, 0), + datetime(2014, 7, 7, 3, 30): datetime(2014, 7, 5, 0, 30), + datetime(2014, 7, 7, 19, 30): datetime(2014, 7, 7, 4, 30), + datetime(2014, 7, 7, 19, 30, 30): datetime(2014, 7, 7, 4, 30, 30), + }, + ), + ] + + # long business hours (see gh-26381) + + # multiple business hours + + @pytest.mark.parametrize("case", apply_cases) + def test_apply(self, case): + offset, cases = case + for base, expected in cases.items(): + assert_offset_equal(offset, base, expected) + + apply_large_n_cases = [ + ( + # A week later + BusinessHour(40), + { + datetime(2014, 7, 1, 11): datetime(2014, 7, 8, 11), + datetime(2014, 7, 1, 13): datetime(2014, 7, 8, 13), + datetime(2014, 7, 1, 15): datetime(2014, 7, 8, 15), + datetime(2014, 7, 1, 16): datetime(2014, 7, 8, 16), + datetime(2014, 7, 1, 17): datetime(2014, 7, 9, 9), + datetime(2014, 7, 2, 11): datetime(2014, 7, 9, 11), + datetime(2014, 7, 2, 8): datetime(2014, 7, 9, 9), + datetime(2014, 7, 2, 19): datetime(2014, 7, 10, 9), + datetime(2014, 7, 2, 23): datetime(2014, 7, 10, 9), + datetime(2014, 7, 3, 0): datetime(2014, 7, 10, 9), + datetime(2014, 7, 5, 15): datetime(2014, 7, 14, 9), + datetime(2014, 7, 4, 18): datetime(2014, 7, 14, 9), + datetime(2014, 7, 7, 9, 30): datetime(2014, 7, 14, 9, 30), + datetime(2014, 7, 7, 9, 30, 30): datetime(2014, 7, 14, 9, 30, 30), + }, + ), + ( + # 3 days and 1 hour before + BusinessHour(-25), + { + datetime(2014, 7, 1, 11): datetime(2014, 6, 26, 10), + datetime(2014, 7, 1, 13): datetime(2014, 6, 26, 12), + datetime(2014, 7, 1, 9): datetime(2014, 6, 25, 16), + datetime(2014, 7, 1, 10): datetime(2014, 6, 25, 17), + datetime(2014, 7, 3, 11): datetime(2014, 6, 30, 10), + datetime(2014, 7, 3, 8): datetime(2014, 6, 27, 16), + datetime(2014, 7, 3, 19): datetime(2014, 6, 30, 16), + datetime(2014, 7, 3, 23): datetime(2014, 6, 30, 16), + datetime(2014, 7, 4, 9): datetime(2014, 6, 30, 16), + datetime(2014, 7, 5, 15): datetime(2014, 7, 1, 16), + datetime(2014, 7, 6, 18): datetime(2014, 7, 1, 16), + datetime(2014, 7, 7, 9, 30): datetime(2014, 7, 1, 16, 30), + datetime(2014, 7, 7, 10, 30, 30): datetime(2014, 7, 2, 9, 30, 30), + }, + ), + ( + # 5 days and 3 hours later + BusinessHour(28, start="21:00", end="02:00"), + { + datetime(2014, 7, 1, 11): datetime(2014, 7, 9, 0), + datetime(2014, 7, 1, 22): datetime(2014, 7, 9, 1), + datetime(2014, 7, 1, 23): datetime(2014, 7, 9, 21), + datetime(2014, 7, 2, 2): datetime(2014, 7, 10, 0), + datetime(2014, 7, 3, 21): datetime(2014, 7, 11, 0), + datetime(2014, 7, 4, 1): datetime(2014, 7, 11, 23), + datetime(2014, 7, 4, 2): datetime(2014, 7, 12, 0), + datetime(2014, 7, 4, 3): datetime(2014, 7, 12, 0), + datetime(2014, 7, 5, 1): datetime(2014, 7, 14, 23), + datetime(2014, 7, 5, 15): datetime(2014, 7, 15, 0), + datetime(2014, 7, 6, 18): datetime(2014, 7, 15, 0), + datetime(2014, 7, 7, 1): datetime(2014, 7, 15, 0), + datetime(2014, 7, 7, 23, 30): datetime(2014, 7, 15, 21, 30), + }, + ), + ( + # large n for multiple opening hours (3 days and 1 hour before) + BusinessHour(n=-25, start=["09:00", "14:00"], end=["12:00", "19:00"]), + { + datetime(2014, 7, 1, 11): datetime(2014, 6, 26, 10), + datetime(2014, 7, 1, 13): datetime(2014, 6, 26, 11), + datetime(2014, 7, 1, 9): datetime(2014, 6, 25, 18), + datetime(2014, 7, 1, 10): datetime(2014, 6, 25, 19), + datetime(2014, 7, 3, 11): datetime(2014, 6, 30, 10), + datetime(2014, 7, 3, 8): datetime(2014, 6, 27, 18), + datetime(2014, 7, 3, 19): datetime(2014, 6, 30, 18), + datetime(2014, 7, 3, 23): datetime(2014, 6, 30, 18), + datetime(2014, 7, 4, 9): datetime(2014, 6, 30, 18), + datetime(2014, 7, 5, 15): datetime(2014, 7, 1, 18), + datetime(2014, 7, 6, 18): datetime(2014, 7, 1, 18), + datetime(2014, 7, 7, 9, 30): datetime(2014, 7, 1, 18, 30), + datetime(2014, 7, 7, 10, 30, 30): datetime(2014, 7, 2, 9, 30, 30), + }, + ), + ( + # 5 days and 3 hours later + BusinessHour(28, start=["21:00", "03:00"], end=["01:00", "04:00"]), + { + datetime(2014, 7, 1, 11): datetime(2014, 7, 9, 0), + datetime(2014, 7, 1, 22): datetime(2014, 7, 9, 3), + datetime(2014, 7, 1, 23): datetime(2014, 7, 9, 21), + datetime(2014, 7, 2, 2): datetime(2014, 7, 9, 23), + datetime(2014, 7, 3, 21): datetime(2014, 7, 11, 0), + datetime(2014, 7, 4, 1): datetime(2014, 7, 11, 23), + datetime(2014, 7, 4, 2): datetime(2014, 7, 11, 23), + datetime(2014, 7, 4, 3): datetime(2014, 7, 11, 23), + datetime(2014, 7, 4, 21): datetime(2014, 7, 12, 0), + datetime(2014, 7, 5, 0): datetime(2014, 7, 14, 22), + datetime(2014, 7, 5, 1): datetime(2014, 7, 14, 23), + datetime(2014, 7, 5, 15): datetime(2014, 7, 14, 23), + datetime(2014, 7, 6, 18): datetime(2014, 7, 14, 23), + datetime(2014, 7, 7, 1): datetime(2014, 7, 14, 23), + datetime(2014, 7, 7, 23, 30): datetime(2014, 7, 15, 21, 30), + }, + ), + ] + + @pytest.mark.parametrize("case", apply_large_n_cases) + def test_apply_large_n(self, case): + offset, cases = case + for base, expected in cases.items(): + assert_offset_equal(offset, base, expected) + + def test_apply_nanoseconds(self): + tests = [ + ( + BusinessHour(), + { + Timestamp("2014-07-04 15:00") + + Nano(5): Timestamp("2014-07-04 16:00") + + Nano(5), + Timestamp("2014-07-04 16:00") + + Nano(5): Timestamp("2014-07-07 09:00") + + Nano(5), + Timestamp("2014-07-04 16:00") + - Nano(5): Timestamp("2014-07-04 17:00") + - Nano(5), + }, + ), + ( + BusinessHour(-1), + { + Timestamp("2014-07-04 15:00") + + Nano(5): Timestamp("2014-07-04 14:00") + + Nano(5), + Timestamp("2014-07-04 10:00") + + Nano(5): Timestamp("2014-07-04 09:00") + + Nano(5), + Timestamp("2014-07-04 10:00") + - Nano(5): Timestamp("2014-07-03 17:00") + - Nano(5), + }, + ), + ] + + for offset, cases in tests: + for base, expected in cases.items(): + assert_offset_equal(offset, base, expected) + + def test_datetimeindex(self): + idx1 = date_range(start="2014-07-04 15:00", end="2014-07-08 10:00", freq="BH") + idx2 = date_range(start="2014-07-04 15:00", periods=12, freq="BH") + idx3 = date_range(end="2014-07-08 10:00", periods=12, freq="BH") + expected = DatetimeIndex( + [ + "2014-07-04 15:00", + "2014-07-04 16:00", + "2014-07-07 09:00", + "2014-07-07 10:00", + "2014-07-07 11:00", + "2014-07-07 12:00", + "2014-07-07 13:00", + "2014-07-07 14:00", + "2014-07-07 15:00", + "2014-07-07 16:00", + "2014-07-08 09:00", + "2014-07-08 10:00", + ], + freq="BH", + ) + for idx in [idx1, idx2, idx3]: + tm.assert_index_equal(idx, expected) + + idx1 = date_range(start="2014-07-04 15:45", end="2014-07-08 10:45", freq="BH") + idx2 = date_range(start="2014-07-04 15:45", periods=12, freq="BH") + idx3 = date_range(end="2014-07-08 10:45", periods=12, freq="BH") + + expected = idx1 + for idx in [idx1, idx2, idx3]: + tm.assert_index_equal(idx, expected) + + def test_bday_ignores_timedeltas(self): + idx = date_range("2010/02/01", "2010/02/10", freq="12H") + t1 = idx + BDay(offset=Timedelta(3, unit="H")) + + expected = DatetimeIndex( + [ + "2010-02-02 03:00:00", + "2010-02-02 15:00:00", + "2010-02-03 03:00:00", + "2010-02-03 15:00:00", + "2010-02-04 03:00:00", + "2010-02-04 15:00:00", + "2010-02-05 03:00:00", + "2010-02-05 15:00:00", + "2010-02-08 03:00:00", + "2010-02-08 15:00:00", + "2010-02-08 03:00:00", + "2010-02-08 15:00:00", + "2010-02-08 03:00:00", + "2010-02-08 15:00:00", + "2010-02-09 03:00:00", + "2010-02-09 15:00:00", + "2010-02-10 03:00:00", + "2010-02-10 15:00:00", + "2010-02-11 03:00:00", + ], + freq=None, + ) + tm.assert_index_equal(t1, expected) diff --git a/pandas/tests/tseries/offsets/test_custom_business_hour.py b/pandas/tests/tseries/offsets/test_custom_business_hour.py new file mode 100644 index 0000000000000..f05b286616572 --- /dev/null +++ b/pandas/tests/tseries/offsets/test_custom_business_hour.py @@ -0,0 +1,293 @@ +""" +Tests for offsets.CustomBusinessHour +""" +from datetime import datetime + +import numpy as np +import pytest + +from pandas._libs.tslibs import Timestamp +from pandas._libs.tslibs.offsets import BusinessHour, CustomBusinessHour, Nano + +import pandas._testing as tm +from pandas.tests.tseries.offsets.common import Base, assert_offset_equal + + +class TestCustomBusinessHour(Base): + _offset = CustomBusinessHour + holidays = ["2014-06-27", datetime(2014, 6, 30), np.datetime64("2014-07-02")] + + def setup_method(self, method): + # 2014 Calendar to check custom holidays + # Sun Mon Tue Wed Thu Fri Sat + # 6/22 23 24 25 26 27 28 + # 29 30 7/1 2 3 4 5 + # 6 7 8 9 10 11 12 + self.d = datetime(2014, 7, 1, 10, 00) + self.offset1 = CustomBusinessHour(weekmask="Tue Wed Thu Fri") + + self.offset2 = CustomBusinessHour(holidays=self.holidays) + + def test_constructor_errors(self): + from datetime import time as dt_time + + msg = "time data must be specified only with hour and minute" + with pytest.raises(ValueError, match=msg): + CustomBusinessHour(start=dt_time(11, 0, 5)) + msg = "time data must match '%H:%M' format" + with pytest.raises(ValueError, match=msg): + CustomBusinessHour(start="AAA") + msg = "time data must match '%H:%M' format" + with pytest.raises(ValueError, match=msg): + CustomBusinessHour(start="14:00:05") + + def test_different_normalize_equals(self): + # GH#21404 changed __eq__ to return False when `normalize` does not match + offset = self._offset() + offset2 = self._offset(normalize=True) + assert offset != offset2 + + def test_repr(self): + assert repr(self.offset1) == "" + assert repr(self.offset2) == "" + + def test_with_offset(self): + expected = Timestamp("2014-07-01 13:00") + + assert self.d + CustomBusinessHour() * 3 == expected + assert self.d + CustomBusinessHour(n=3) == expected + + def test_eq(self): + for offset in [self.offset1, self.offset2]: + assert offset == offset + + assert CustomBusinessHour() != CustomBusinessHour(-1) + assert CustomBusinessHour(start="09:00") == CustomBusinessHour() + assert CustomBusinessHour(start="09:00") != CustomBusinessHour(start="09:01") + assert CustomBusinessHour(start="09:00", end="17:00") != CustomBusinessHour( + start="17:00", end="09:01" + ) + + assert CustomBusinessHour(weekmask="Tue Wed Thu Fri") != CustomBusinessHour( + weekmask="Mon Tue Wed Thu Fri" + ) + assert CustomBusinessHour(holidays=["2014-06-27"]) != CustomBusinessHour( + holidays=["2014-06-28"] + ) + + def test_sub(self): + # override the Base.test_sub implementation because self.offset2 is + # defined differently in this class than the test expects + pass + + def test_hash(self): + assert hash(self.offset1) == hash(self.offset1) + assert hash(self.offset2) == hash(self.offset2) + + def test_call(self): + with tm.assert_produces_warning(FutureWarning): + # GH#34171 DateOffset.__call__ is deprecated + assert self.offset1(self.d) == datetime(2014, 7, 1, 11) + assert self.offset2(self.d) == datetime(2014, 7, 1, 11) + + def testRollback1(self): + assert self.offset1.rollback(self.d) == self.d + assert self.offset2.rollback(self.d) == self.d + + d = datetime(2014, 7, 1, 0) + + # 2014/07/01 is Tuesday, 06/30 is Monday(holiday) + assert self.offset1.rollback(d) == datetime(2014, 6, 27, 17) + + # 2014/6/30 and 2014/6/27 are holidays + assert self.offset2.rollback(d) == datetime(2014, 6, 26, 17) + + def testRollback2(self): + assert self._offset(-3).rollback(datetime(2014, 7, 5, 15, 0)) == datetime( + 2014, 7, 4, 17, 0 + ) + + def testRollforward1(self): + assert self.offset1.rollforward(self.d) == self.d + assert self.offset2.rollforward(self.d) == self.d + + d = datetime(2014, 7, 1, 0) + assert self.offset1.rollforward(d) == datetime(2014, 7, 1, 9) + assert self.offset2.rollforward(d) == datetime(2014, 7, 1, 9) + + def testRollforward2(self): + assert self._offset(-3).rollforward(datetime(2014, 7, 5, 16, 0)) == datetime( + 2014, 7, 7, 9 + ) + + def test_roll_date_object(self): + offset = BusinessHour() + + dt = datetime(2014, 7, 6, 15, 0) + + result = offset.rollback(dt) + assert result == datetime(2014, 7, 4, 17) + + result = offset.rollforward(dt) + assert result == datetime(2014, 7, 7, 9) + + normalize_cases = [ + ( + CustomBusinessHour(normalize=True, holidays=holidays), + { + datetime(2014, 7, 1, 8): datetime(2014, 7, 1), + datetime(2014, 7, 1, 17): datetime(2014, 7, 3), + datetime(2014, 7, 1, 16): datetime(2014, 7, 3), + datetime(2014, 7, 1, 23): datetime(2014, 7, 3), + datetime(2014, 7, 1, 0): datetime(2014, 7, 1), + datetime(2014, 7, 4, 15): datetime(2014, 7, 4), + datetime(2014, 7, 4, 15, 59): datetime(2014, 7, 4), + datetime(2014, 7, 4, 16, 30): datetime(2014, 7, 7), + datetime(2014, 7, 5, 23): datetime(2014, 7, 7), + datetime(2014, 7, 6, 10): datetime(2014, 7, 7), + }, + ), + ( + CustomBusinessHour(-1, normalize=True, holidays=holidays), + { + datetime(2014, 7, 1, 8): datetime(2014, 6, 26), + datetime(2014, 7, 1, 17): datetime(2014, 7, 1), + datetime(2014, 7, 1, 16): datetime(2014, 7, 1), + datetime(2014, 7, 1, 10): datetime(2014, 6, 26), + datetime(2014, 7, 1, 0): datetime(2014, 6, 26), + datetime(2014, 7, 7, 10): datetime(2014, 7, 4), + datetime(2014, 7, 7, 10, 1): datetime(2014, 7, 7), + datetime(2014, 7, 5, 23): datetime(2014, 7, 4), + datetime(2014, 7, 6, 10): datetime(2014, 7, 4), + }, + ), + ( + CustomBusinessHour( + 1, normalize=True, start="17:00", end="04:00", holidays=holidays + ), + { + datetime(2014, 7, 1, 8): datetime(2014, 7, 1), + datetime(2014, 7, 1, 17): datetime(2014, 7, 1), + datetime(2014, 7, 1, 23): datetime(2014, 7, 2), + datetime(2014, 7, 2, 2): datetime(2014, 7, 2), + datetime(2014, 7, 2, 3): datetime(2014, 7, 3), + datetime(2014, 7, 4, 23): datetime(2014, 7, 5), + datetime(2014, 7, 5, 2): datetime(2014, 7, 5), + datetime(2014, 7, 7, 2): datetime(2014, 7, 7), + datetime(2014, 7, 7, 17): datetime(2014, 7, 7), + }, + ), + ] + + @pytest.mark.parametrize("norm_cases", normalize_cases) + def test_normalize(self, norm_cases): + offset, cases = norm_cases + for dt, expected in cases.items(): + assert offset.apply(dt) == expected + + def test_is_on_offset(self): + tests = [ + ( + CustomBusinessHour(start="10:00", end="15:00", holidays=self.holidays), + { + datetime(2014, 7, 1, 9): False, + datetime(2014, 7, 1, 10): True, + datetime(2014, 7, 1, 15): True, + datetime(2014, 7, 1, 15, 1): False, + datetime(2014, 7, 5, 12): False, + datetime(2014, 7, 6, 12): False, + }, + ) + ] + + for offset, cases in tests: + for dt, expected in cases.items(): + assert offset.is_on_offset(dt) == expected + + apply_cases = [ + ( + CustomBusinessHour(holidays=holidays), + { + datetime(2014, 7, 1, 11): datetime(2014, 7, 1, 12), + datetime(2014, 7, 1, 13): datetime(2014, 7, 1, 14), + datetime(2014, 7, 1, 15): datetime(2014, 7, 1, 16), + datetime(2014, 7, 1, 19): datetime(2014, 7, 3, 10), + datetime(2014, 7, 1, 16): datetime(2014, 7, 3, 9), + datetime(2014, 7, 1, 16, 30, 15): datetime(2014, 7, 3, 9, 30, 15), + datetime(2014, 7, 1, 17): datetime(2014, 7, 3, 10), + datetime(2014, 7, 2, 11): datetime(2014, 7, 3, 10), + # out of business hours + datetime(2014, 7, 2, 8): datetime(2014, 7, 3, 10), + datetime(2014, 7, 2, 19): datetime(2014, 7, 3, 10), + datetime(2014, 7, 2, 23): datetime(2014, 7, 3, 10), + datetime(2014, 7, 3, 0): datetime(2014, 7, 3, 10), + # saturday + datetime(2014, 7, 5, 15): datetime(2014, 7, 7, 10), + datetime(2014, 7, 4, 17): datetime(2014, 7, 7, 10), + datetime(2014, 7, 4, 16, 30): datetime(2014, 7, 7, 9, 30), + datetime(2014, 7, 4, 16, 30, 30): datetime(2014, 7, 7, 9, 30, 30), + }, + ), + ( + CustomBusinessHour(4, holidays=holidays), + { + datetime(2014, 7, 1, 11): datetime(2014, 7, 1, 15), + datetime(2014, 7, 1, 13): datetime(2014, 7, 3, 9), + datetime(2014, 7, 1, 15): datetime(2014, 7, 3, 11), + datetime(2014, 7, 1, 16): datetime(2014, 7, 3, 12), + datetime(2014, 7, 1, 17): datetime(2014, 7, 3, 13), + datetime(2014, 7, 2, 11): datetime(2014, 7, 3, 13), + datetime(2014, 7, 2, 8): datetime(2014, 7, 3, 13), + datetime(2014, 7, 2, 19): datetime(2014, 7, 3, 13), + datetime(2014, 7, 2, 23): datetime(2014, 7, 3, 13), + datetime(2014, 7, 3, 0): datetime(2014, 7, 3, 13), + datetime(2014, 7, 5, 15): datetime(2014, 7, 7, 13), + datetime(2014, 7, 4, 17): datetime(2014, 7, 7, 13), + datetime(2014, 7, 4, 16, 30): datetime(2014, 7, 7, 12, 30), + datetime(2014, 7, 4, 16, 30, 30): datetime(2014, 7, 7, 12, 30, 30), + }, + ), + ] + + @pytest.mark.parametrize("apply_case", apply_cases) + def test_apply(self, apply_case): + offset, cases = apply_case + for base, expected in cases.items(): + assert_offset_equal(offset, base, expected) + + nano_cases = [ + ( + CustomBusinessHour(holidays=holidays), + { + Timestamp("2014-07-01 15:00") + + Nano(5): Timestamp("2014-07-01 16:00") + + Nano(5), + Timestamp("2014-07-01 16:00") + + Nano(5): Timestamp("2014-07-03 09:00") + + Nano(5), + Timestamp("2014-07-01 16:00") + - Nano(5): Timestamp("2014-07-01 17:00") + - Nano(5), + }, + ), + ( + CustomBusinessHour(-1, holidays=holidays), + { + Timestamp("2014-07-01 15:00") + + Nano(5): Timestamp("2014-07-01 14:00") + + Nano(5), + Timestamp("2014-07-01 10:00") + + Nano(5): Timestamp("2014-07-01 09:00") + + Nano(5), + Timestamp("2014-07-01 10:00") + - Nano(5): Timestamp("2014-06-26 17:00") + - Nano(5), + }, + ), + ] + + @pytest.mark.parametrize("nano_case", nano_cases) + def test_apply_nanoseconds(self, nano_case): + offset, cases = nano_case + for base, expected in cases.items(): + assert_offset_equal(offset, base, expected) diff --git a/pandas/tests/tseries/offsets/test_dst.py b/pandas/tests/tseries/offsets/test_dst.py new file mode 100644 index 0000000000000..0ae94b6b57640 --- /dev/null +++ b/pandas/tests/tseries/offsets/test_dst.py @@ -0,0 +1,175 @@ +""" +Tests for DateOffset additions over Daylight Savings Time +""" +from datetime import timedelta + +import pytest + +from pandas._libs.tslibs import Timestamp +from pandas._libs.tslibs.offsets import ( + BMonthBegin, + BMonthEnd, + BQuarterBegin, + BQuarterEnd, + BYearBegin, + BYearEnd, + CBMonthBegin, + CBMonthEnd, + DateOffset, + Day, + MonthBegin, + MonthEnd, + QuarterBegin, + QuarterEnd, + SemiMonthBegin, + SemiMonthEnd, + Week, + YearBegin, + YearEnd, +) + +from pandas.tests.tseries.offsets.test_offsets import get_utc_offset_hours + + +class TestDST: + + # one microsecond before the DST transition + ts_pre_fallback = "2013-11-03 01:59:59.999999" + ts_pre_springfwd = "2013-03-10 01:59:59.999999" + + # test both basic names and dateutil timezones + timezone_utc_offsets = { + "US/Eastern": {"utc_offset_daylight": -4, "utc_offset_standard": -5}, + "dateutil/US/Pacific": {"utc_offset_daylight": -7, "utc_offset_standard": -8}, + } + valid_date_offsets_singular = [ + "weekday", + "day", + "hour", + "minute", + "second", + "microsecond", + ] + valid_date_offsets_plural = [ + "weeks", + "days", + "hours", + "minutes", + "seconds", + "milliseconds", + "microseconds", + ] + + def _test_all_offsets(self, n, **kwds): + valid_offsets = ( + self.valid_date_offsets_plural + if n > 1 + else self.valid_date_offsets_singular + ) + + for name in valid_offsets: + self._test_offset(offset_name=name, offset_n=n, **kwds) + + def _test_offset(self, offset_name, offset_n, tstart, expected_utc_offset): + offset = DateOffset(**{offset_name: offset_n}) + + t = tstart + offset + if expected_utc_offset is not None: + assert get_utc_offset_hours(t) == expected_utc_offset + + if offset_name == "weeks": + # dates should match + assert t.date() == timedelta(days=7 * offset.kwds["weeks"]) + tstart.date() + # expect the same day of week, hour of day, minute, second, ... + assert ( + t.dayofweek == tstart.dayofweek + and t.hour == tstart.hour + and t.minute == tstart.minute + and t.second == tstart.second + ) + elif offset_name == "days": + # dates should match + assert timedelta(offset.kwds["days"]) + tstart.date() == t.date() + # expect the same hour of day, minute, second, ... + assert ( + t.hour == tstart.hour + and t.minute == tstart.minute + and t.second == tstart.second + ) + elif offset_name in self.valid_date_offsets_singular: + # expect the singular offset value to match between tstart and t + datepart_offset = getattr( + t, offset_name if offset_name != "weekday" else "dayofweek" + ) + assert datepart_offset == offset.kwds[offset_name] + else: + # the offset should be the same as if it was done in UTC + assert t == (tstart.tz_convert("UTC") + offset).tz_convert("US/Pacific") + + def _make_timestamp(self, string, hrs_offset, tz): + if hrs_offset >= 0: + offset_string = f"{hrs_offset:02d}00" + else: + offset_string = f"-{(hrs_offset * -1):02}00" + return Timestamp(string + offset_string).tz_convert(tz) + + def test_springforward_plural(self): + # test moving from standard to daylight savings + for tz, utc_offsets in self.timezone_utc_offsets.items(): + hrs_pre = utc_offsets["utc_offset_standard"] + hrs_post = utc_offsets["utc_offset_daylight"] + self._test_all_offsets( + n=3, + tstart=self._make_timestamp(self.ts_pre_springfwd, hrs_pre, tz), + expected_utc_offset=hrs_post, + ) + + def test_fallback_singular(self): + # in the case of singular offsets, we don't necessarily know which utc + # offset the new Timestamp will wind up in (the tz for 1 month may be + # different from 1 second) so we don't specify an expected_utc_offset + for tz, utc_offsets in self.timezone_utc_offsets.items(): + hrs_pre = utc_offsets["utc_offset_standard"] + self._test_all_offsets( + n=1, + tstart=self._make_timestamp(self.ts_pre_fallback, hrs_pre, tz), + expected_utc_offset=None, + ) + + def test_springforward_singular(self): + for tz, utc_offsets in self.timezone_utc_offsets.items(): + hrs_pre = utc_offsets["utc_offset_standard"] + self._test_all_offsets( + n=1, + tstart=self._make_timestamp(self.ts_pre_springfwd, hrs_pre, tz), + expected_utc_offset=None, + ) + + offset_classes = { + MonthBegin: ["11/2/2012", "12/1/2012"], + MonthEnd: ["11/2/2012", "11/30/2012"], + BMonthBegin: ["11/2/2012", "12/3/2012"], + BMonthEnd: ["11/2/2012", "11/30/2012"], + CBMonthBegin: ["11/2/2012", "12/3/2012"], + CBMonthEnd: ["11/2/2012", "11/30/2012"], + SemiMonthBegin: ["11/2/2012", "11/15/2012"], + SemiMonthEnd: ["11/2/2012", "11/15/2012"], + Week: ["11/2/2012", "11/9/2012"], + YearBegin: ["11/2/2012", "1/1/2013"], + YearEnd: ["11/2/2012", "12/31/2012"], + BYearBegin: ["11/2/2012", "1/1/2013"], + BYearEnd: ["11/2/2012", "12/31/2012"], + QuarterBegin: ["11/2/2012", "12/1/2012"], + QuarterEnd: ["11/2/2012", "12/31/2012"], + BQuarterBegin: ["11/2/2012", "12/3/2012"], + BQuarterEnd: ["11/2/2012", "12/31/2012"], + Day: ["11/4/2012", "11/4/2012 23:00"], + }.items() + + @pytest.mark.parametrize("tup", offset_classes) + def test_all_offset_classes(self, tup): + offset, test_values = tup + + first = Timestamp(test_values[0], tz="US/Eastern") + offset() + second = Timestamp(test_values[1], tz="US/Eastern") + assert first == second diff --git a/pandas/tests/tseries/offsets/test_fiscal.py b/pandas/tests/tseries/offsets/test_fiscal.py index 7713be67a7e05..14728314b8e20 100644 --- a/pandas/tests/tseries/offsets/test_fiscal.py +++ b/pandas/tests/tseries/offsets/test_fiscal.py @@ -10,13 +10,16 @@ from pandas import Timestamp import pandas._testing as tm +from pandas.tests.tseries.offsets.common import ( + Base, + WeekDay, + assert_is_on_offset, + assert_offset_equal, +) from pandas.tseries.frequencies import get_offset from pandas.tseries.offsets import FY5253, FY5253Quarter -from .common import assert_is_on_offset, assert_offset_equal -from .test_offsets import Base, WeekDay - def makeFY5253LastOfMonthQuarter(*args, **kwds): return FY5253Quarter(*args, variation="last", **kwds) diff --git a/pandas/tests/tseries/offsets/test_month.py b/pandas/tests/tseries/offsets/test_month.py new file mode 100644 index 0000000000000..578af79084e09 --- /dev/null +++ b/pandas/tests/tseries/offsets/test_month.py @@ -0,0 +1,838 @@ +""" +Tests for CBMonthEnd CBMonthBegin, SemiMonthEnd, and SemiMonthBegin in offsets +""" +from datetime import date, datetime + +import numpy as np +import pytest + +from pandas._libs.tslibs import Timestamp +from pandas._libs.tslibs.offsets import ( + CBMonthBegin, + CBMonthEnd, + CDay, + SemiMonthBegin, + SemiMonthEnd, +) + +from pandas import DatetimeIndex, Series, _testing as tm, date_range +from pandas.tests.tseries.offsets.common import ( + Base, + assert_is_on_offset, + assert_offset_equal, +) +from pandas.tests.tseries.offsets.test_offsets import _ApplyCases + +from pandas.tseries import offsets as offsets +from pandas.tseries.holiday import USFederalHolidayCalendar + + +class CustomBusinessMonthBase: + def setup_method(self, method): + self.d = datetime(2008, 1, 1) + + self.offset = self._offset() + self.offset1 = self.offset + self.offset2 = self._offset(2) + + def test_eq(self): + assert self.offset2 == self.offset2 + + def test_mul(self): + pass + + def test_hash(self): + assert hash(self.offset2) == hash(self.offset2) + + def test_roundtrip_pickle(self): + def _check_roundtrip(obj): + unpickled = tm.round_trip_pickle(obj) + assert unpickled == obj + + _check_roundtrip(self._offset()) + _check_roundtrip(self._offset(2)) + _check_roundtrip(self._offset() * 2) + + def test_copy(self): + # GH 17452 + off = self._offset(weekmask="Mon Wed Fri") + assert off == off.copy() + + +class TestCustomBusinessMonthEnd(CustomBusinessMonthBase, Base): + _offset = CBMonthEnd + + def test_different_normalize_equals(self): + # GH#21404 changed __eq__ to return False when `normalize` does not match + offset = self._offset() + offset2 = self._offset(normalize=True) + assert offset != offset2 + + def test_repr(self): + assert repr(self.offset) == "" + assert repr(self.offset2) == "<2 * CustomBusinessMonthEnds>" + + def test_call(self): + with tm.assert_produces_warning(FutureWarning): + # GH#34171 DateOffset.__call__ is deprecated + assert self.offset2(self.d) == datetime(2008, 2, 29) + + def testRollback1(self): + assert CDay(10).rollback(datetime(2007, 12, 31)) == datetime(2007, 12, 31) + + def testRollback2(self): + assert CBMonthEnd(10).rollback(self.d) == datetime(2007, 12, 31) + + def testRollforward1(self): + assert CBMonthEnd(10).rollforward(self.d) == datetime(2008, 1, 31) + + def test_roll_date_object(self): + offset = CBMonthEnd() + + dt = date(2012, 9, 15) + + result = offset.rollback(dt) + assert result == datetime(2012, 8, 31) + + result = offset.rollforward(dt) + assert result == datetime(2012, 9, 28) + + offset = offsets.Day() + result = offset.rollback(dt) + assert result == datetime(2012, 9, 15) + + result = offset.rollforward(dt) + assert result == datetime(2012, 9, 15) + + on_offset_cases = [ + (CBMonthEnd(), datetime(2008, 1, 31), True), + (CBMonthEnd(), datetime(2008, 1, 1), False), + ] + + @pytest.mark.parametrize("case", on_offset_cases) + def test_is_on_offset(self, case): + offset, d, expected = case + assert_is_on_offset(offset, d, expected) + + apply_cases: _ApplyCases = [ + ( + CBMonthEnd(), + { + datetime(2008, 1, 1): datetime(2008, 1, 31), + datetime(2008, 2, 7): datetime(2008, 2, 29), + }, + ), + ( + 2 * CBMonthEnd(), + { + datetime(2008, 1, 1): datetime(2008, 2, 29), + datetime(2008, 2, 7): datetime(2008, 3, 31), + }, + ), + ( + -CBMonthEnd(), + { + datetime(2008, 1, 1): datetime(2007, 12, 31), + datetime(2008, 2, 8): datetime(2008, 1, 31), + }, + ), + ( + -2 * CBMonthEnd(), + { + datetime(2008, 1, 1): datetime(2007, 11, 30), + datetime(2008, 2, 9): datetime(2007, 12, 31), + }, + ), + ( + CBMonthEnd(0), + { + datetime(2008, 1, 1): datetime(2008, 1, 31), + datetime(2008, 2, 7): datetime(2008, 2, 29), + }, + ), + ] + + @pytest.mark.parametrize("case", apply_cases) + def test_apply(self, case): + offset, cases = case + for base, expected in cases.items(): + assert_offset_equal(offset, base, expected) + + def test_apply_large_n(self): + dt = datetime(2012, 10, 23) + + result = dt + CBMonthEnd(10) + assert result == datetime(2013, 7, 31) + + result = dt + CDay(100) - CDay(100) + assert result == dt + + off = CBMonthEnd() * 6 + rs = datetime(2012, 1, 1) - off + xp = datetime(2011, 7, 29) + assert rs == xp + + st = datetime(2011, 12, 18) + rs = st + off + xp = datetime(2012, 5, 31) + assert rs == xp + + def test_holidays(self): + # Define a TradingDay offset + holidays = ["2012-01-31", datetime(2012, 2, 28), np.datetime64("2012-02-29")] + bm_offset = CBMonthEnd(holidays=holidays) + dt = datetime(2012, 1, 1) + assert dt + bm_offset == datetime(2012, 1, 30) + assert dt + 2 * bm_offset == datetime(2012, 2, 27) + + @pytest.mark.filterwarnings("ignore:Non:pandas.errors.PerformanceWarning") + def test_datetimeindex(self): + from pandas.tseries.holiday import USFederalHolidayCalendar + + hcal = USFederalHolidayCalendar() + freq = CBMonthEnd(calendar=hcal) + + assert date_range(start="20120101", end="20130101", freq=freq).tolist()[ + 0 + ] == datetime(2012, 1, 31) + + +class TestCustomBusinessMonthBegin(CustomBusinessMonthBase, Base): + _offset = CBMonthBegin + + def test_different_normalize_equals(self): + # GH#21404 changed __eq__ to return False when `normalize` does not match + offset = self._offset() + offset2 = self._offset(normalize=True) + assert offset != offset2 + + def test_repr(self): + assert repr(self.offset) == "" + assert repr(self.offset2) == "<2 * CustomBusinessMonthBegins>" + + def test_call(self): + with tm.assert_produces_warning(FutureWarning): + # GH#34171 DateOffset.__call__ is deprecated + assert self.offset2(self.d) == datetime(2008, 3, 3) + + def testRollback1(self): + assert CDay(10).rollback(datetime(2007, 12, 31)) == datetime(2007, 12, 31) + + def testRollback2(self): + assert CBMonthBegin(10).rollback(self.d) == datetime(2008, 1, 1) + + def testRollforward1(self): + assert CBMonthBegin(10).rollforward(self.d) == datetime(2008, 1, 1) + + def test_roll_date_object(self): + offset = CBMonthBegin() + + dt = date(2012, 9, 15) + + result = offset.rollback(dt) + assert result == datetime(2012, 9, 3) + + result = offset.rollforward(dt) + assert result == datetime(2012, 10, 1) + + offset = offsets.Day() + result = offset.rollback(dt) + assert result == datetime(2012, 9, 15) + + result = offset.rollforward(dt) + assert result == datetime(2012, 9, 15) + + on_offset_cases = [ + (CBMonthBegin(), datetime(2008, 1, 1), True), + (CBMonthBegin(), datetime(2008, 1, 31), False), + ] + + @pytest.mark.parametrize("case", on_offset_cases) + def test_is_on_offset(self, case): + offset, dt, expected = case + assert_is_on_offset(offset, dt, expected) + + apply_cases: _ApplyCases = [ + ( + CBMonthBegin(), + { + datetime(2008, 1, 1): datetime(2008, 2, 1), + datetime(2008, 2, 7): datetime(2008, 3, 3), + }, + ), + ( + 2 * CBMonthBegin(), + { + datetime(2008, 1, 1): datetime(2008, 3, 3), + datetime(2008, 2, 7): datetime(2008, 4, 1), + }, + ), + ( + -CBMonthBegin(), + { + datetime(2008, 1, 1): datetime(2007, 12, 3), + datetime(2008, 2, 8): datetime(2008, 2, 1), + }, + ), + ( + -2 * CBMonthBegin(), + { + datetime(2008, 1, 1): datetime(2007, 11, 1), + datetime(2008, 2, 9): datetime(2008, 1, 1), + }, + ), + ( + CBMonthBegin(0), + { + datetime(2008, 1, 1): datetime(2008, 1, 1), + datetime(2008, 1, 7): datetime(2008, 2, 1), + }, + ), + ] + + @pytest.mark.parametrize("case", apply_cases) + def test_apply(self, case): + offset, cases = case + for base, expected in cases.items(): + assert_offset_equal(offset, base, expected) + + def test_apply_large_n(self): + dt = datetime(2012, 10, 23) + + result = dt + CBMonthBegin(10) + assert result == datetime(2013, 8, 1) + + result = dt + CDay(100) - CDay(100) + assert result == dt + + off = CBMonthBegin() * 6 + rs = datetime(2012, 1, 1) - off + xp = datetime(2011, 7, 1) + assert rs == xp + + st = datetime(2011, 12, 18) + rs = st + off + + xp = datetime(2012, 6, 1) + assert rs == xp + + def test_holidays(self): + # Define a TradingDay offset + holidays = ["2012-02-01", datetime(2012, 2, 2), np.datetime64("2012-03-01")] + bm_offset = CBMonthBegin(holidays=holidays) + dt = datetime(2012, 1, 1) + + assert dt + bm_offset == datetime(2012, 1, 2) + assert dt + 2 * bm_offset == datetime(2012, 2, 3) + + @pytest.mark.filterwarnings("ignore:Non:pandas.errors.PerformanceWarning") + def test_datetimeindex(self): + hcal = USFederalHolidayCalendar() + cbmb = CBMonthBegin(calendar=hcal) + assert date_range(start="20120101", end="20130101", freq=cbmb).tolist()[ + 0 + ] == datetime(2012, 1, 3) + + +class TestSemiMonthEnd(Base): + _offset = SemiMonthEnd + offset1 = _offset() + offset2 = _offset(2) + + def test_offset_whole_year(self): + dates = ( + datetime(2007, 12, 31), + datetime(2008, 1, 15), + datetime(2008, 1, 31), + datetime(2008, 2, 15), + datetime(2008, 2, 29), + datetime(2008, 3, 15), + datetime(2008, 3, 31), + datetime(2008, 4, 15), + datetime(2008, 4, 30), + datetime(2008, 5, 15), + datetime(2008, 5, 31), + datetime(2008, 6, 15), + datetime(2008, 6, 30), + datetime(2008, 7, 15), + datetime(2008, 7, 31), + datetime(2008, 8, 15), + datetime(2008, 8, 31), + datetime(2008, 9, 15), + datetime(2008, 9, 30), + datetime(2008, 10, 15), + datetime(2008, 10, 31), + datetime(2008, 11, 15), + datetime(2008, 11, 30), + datetime(2008, 12, 15), + datetime(2008, 12, 31), + ) + + for base, exp_date in zip(dates[:-1], dates[1:]): + assert_offset_equal(SemiMonthEnd(), base, exp_date) + + # ensure .apply_index works as expected + s = DatetimeIndex(dates[:-1]) + with tm.assert_produces_warning(None): + # GH#22535 check that we don't get a FutureWarning from adding + # an integer array to PeriodIndex + result = SemiMonthEnd() + s + + exp = DatetimeIndex(dates[1:]) + tm.assert_index_equal(result, exp) + + # ensure generating a range with DatetimeIndex gives same result + result = date_range(start=dates[0], end=dates[-1], freq="SM") + exp = DatetimeIndex(dates, freq="SM") + tm.assert_index_equal(result, exp) + + offset_cases = [] + offset_cases.append( + ( + SemiMonthEnd(), + { + datetime(2008, 1, 1): datetime(2008, 1, 15), + datetime(2008, 1, 15): datetime(2008, 1, 31), + datetime(2008, 1, 31): datetime(2008, 2, 15), + datetime(2006, 12, 14): datetime(2006, 12, 15), + datetime(2006, 12, 29): datetime(2006, 12, 31), + datetime(2006, 12, 31): datetime(2007, 1, 15), + datetime(2007, 1, 1): datetime(2007, 1, 15), + datetime(2006, 12, 1): datetime(2006, 12, 15), + datetime(2006, 12, 15): datetime(2006, 12, 31), + }, + ) + ) + + offset_cases.append( + ( + SemiMonthEnd(day_of_month=20), + { + datetime(2008, 1, 1): datetime(2008, 1, 20), + datetime(2008, 1, 15): datetime(2008, 1, 20), + datetime(2008, 1, 21): datetime(2008, 1, 31), + datetime(2008, 1, 31): datetime(2008, 2, 20), + datetime(2006, 12, 14): datetime(2006, 12, 20), + datetime(2006, 12, 29): datetime(2006, 12, 31), + datetime(2006, 12, 31): datetime(2007, 1, 20), + datetime(2007, 1, 1): datetime(2007, 1, 20), + datetime(2006, 12, 1): datetime(2006, 12, 20), + datetime(2006, 12, 15): datetime(2006, 12, 20), + }, + ) + ) + + offset_cases.append( + ( + SemiMonthEnd(0), + { + datetime(2008, 1, 1): datetime(2008, 1, 15), + datetime(2008, 1, 16): datetime(2008, 1, 31), + datetime(2008, 1, 15): datetime(2008, 1, 15), + datetime(2008, 1, 31): datetime(2008, 1, 31), + datetime(2006, 12, 29): datetime(2006, 12, 31), + datetime(2006, 12, 31): datetime(2006, 12, 31), + datetime(2007, 1, 1): datetime(2007, 1, 15), + }, + ) + ) + + offset_cases.append( + ( + SemiMonthEnd(0, day_of_month=16), + { + datetime(2008, 1, 1): datetime(2008, 1, 16), + datetime(2008, 1, 16): datetime(2008, 1, 16), + datetime(2008, 1, 15): datetime(2008, 1, 16), + datetime(2008, 1, 31): datetime(2008, 1, 31), + datetime(2006, 12, 29): datetime(2006, 12, 31), + datetime(2006, 12, 31): datetime(2006, 12, 31), + datetime(2007, 1, 1): datetime(2007, 1, 16), + }, + ) + ) + + offset_cases.append( + ( + SemiMonthEnd(2), + { + datetime(2008, 1, 1): datetime(2008, 1, 31), + datetime(2008, 1, 31): datetime(2008, 2, 29), + datetime(2006, 12, 29): datetime(2007, 1, 15), + datetime(2006, 12, 31): datetime(2007, 1, 31), + datetime(2007, 1, 1): datetime(2007, 1, 31), + datetime(2007, 1, 16): datetime(2007, 2, 15), + datetime(2006, 11, 1): datetime(2006, 11, 30), + }, + ) + ) + + offset_cases.append( + ( + SemiMonthEnd(-1), + { + datetime(2007, 1, 1): datetime(2006, 12, 31), + datetime(2008, 6, 30): datetime(2008, 6, 15), + datetime(2008, 12, 31): datetime(2008, 12, 15), + datetime(2006, 12, 29): datetime(2006, 12, 15), + datetime(2006, 12, 30): datetime(2006, 12, 15), + datetime(2007, 1, 1): datetime(2006, 12, 31), + }, + ) + ) + + offset_cases.append( + ( + SemiMonthEnd(-1, day_of_month=4), + { + datetime(2007, 1, 1): datetime(2006, 12, 31), + datetime(2007, 1, 4): datetime(2006, 12, 31), + datetime(2008, 6, 30): datetime(2008, 6, 4), + datetime(2008, 12, 31): datetime(2008, 12, 4), + datetime(2006, 12, 5): datetime(2006, 12, 4), + datetime(2006, 12, 30): datetime(2006, 12, 4), + datetime(2007, 1, 1): datetime(2006, 12, 31), + }, + ) + ) + + offset_cases.append( + ( + SemiMonthEnd(-2), + { + datetime(2007, 1, 1): datetime(2006, 12, 15), + datetime(2008, 6, 30): datetime(2008, 5, 31), + datetime(2008, 3, 15): datetime(2008, 2, 15), + datetime(2008, 12, 31): datetime(2008, 11, 30), + datetime(2006, 12, 29): datetime(2006, 11, 30), + datetime(2006, 12, 14): datetime(2006, 11, 15), + datetime(2007, 1, 1): datetime(2006, 12, 15), + }, + ) + ) + + @pytest.mark.parametrize("case", offset_cases) + def test_offset(self, case): + offset, cases = case + for base, expected in cases.items(): + assert_offset_equal(offset, base, expected) + + @pytest.mark.parametrize("case", offset_cases) + def test_apply_index(self, case): + # https://github.com/pandas-dev/pandas/issues/34580 + offset, cases = case + s = DatetimeIndex(cases.keys()) + exp = DatetimeIndex(cases.values()) + + with tm.assert_produces_warning(None): + # GH#22535 check that we don't get a FutureWarning from adding + # an integer array to PeriodIndex + result = offset + s + tm.assert_index_equal(result, exp) + + with tm.assert_produces_warning(FutureWarning): + result = offset.apply_index(s) + tm.assert_index_equal(result, exp) + + on_offset_cases = [ + (datetime(2007, 12, 31), True), + (datetime(2007, 12, 15), True), + (datetime(2007, 12, 14), False), + (datetime(2007, 12, 1), False), + (datetime(2008, 2, 29), True), + ] + + @pytest.mark.parametrize("case", on_offset_cases) + def test_is_on_offset(self, case): + dt, expected = case + assert_is_on_offset(SemiMonthEnd(), dt, expected) + + @pytest.mark.parametrize("klass", [Series, DatetimeIndex]) + def test_vectorized_offset_addition(self, klass): + s = klass( + [ + Timestamp("2000-01-15 00:15:00", tz="US/Central"), + Timestamp("2000-02-15", tz="US/Central"), + ], + name="a", + ) + + with tm.assert_produces_warning(None): + # GH#22535 check that we don't get a FutureWarning from adding + # an integer array to PeriodIndex + result = s + SemiMonthEnd() + result2 = SemiMonthEnd() + s + + exp = klass( + [ + Timestamp("2000-01-31 00:15:00", tz="US/Central"), + Timestamp("2000-02-29", tz="US/Central"), + ], + name="a", + ) + tm.assert_equal(result, exp) + tm.assert_equal(result2, exp) + + s = klass( + [ + Timestamp("2000-01-01 00:15:00", tz="US/Central"), + Timestamp("2000-02-01", tz="US/Central"), + ], + name="a", + ) + + with tm.assert_produces_warning(None): + # GH#22535 check that we don't get a FutureWarning from adding + # an integer array to PeriodIndex + result = s + SemiMonthEnd() + result2 = SemiMonthEnd() + s + + exp = klass( + [ + Timestamp("2000-01-15 00:15:00", tz="US/Central"), + Timestamp("2000-02-15", tz="US/Central"), + ], + name="a", + ) + tm.assert_equal(result, exp) + tm.assert_equal(result2, exp) + + +class TestSemiMonthBegin(Base): + _offset = SemiMonthBegin + offset1 = _offset() + offset2 = _offset(2) + + def test_offset_whole_year(self): + dates = ( + datetime(2007, 12, 15), + datetime(2008, 1, 1), + datetime(2008, 1, 15), + datetime(2008, 2, 1), + datetime(2008, 2, 15), + datetime(2008, 3, 1), + datetime(2008, 3, 15), + datetime(2008, 4, 1), + datetime(2008, 4, 15), + datetime(2008, 5, 1), + datetime(2008, 5, 15), + datetime(2008, 6, 1), + datetime(2008, 6, 15), + datetime(2008, 7, 1), + datetime(2008, 7, 15), + datetime(2008, 8, 1), + datetime(2008, 8, 15), + datetime(2008, 9, 1), + datetime(2008, 9, 15), + datetime(2008, 10, 1), + datetime(2008, 10, 15), + datetime(2008, 11, 1), + datetime(2008, 11, 15), + datetime(2008, 12, 1), + datetime(2008, 12, 15), + ) + + for base, exp_date in zip(dates[:-1], dates[1:]): + assert_offset_equal(SemiMonthBegin(), base, exp_date) + + # ensure .apply_index works as expected + s = DatetimeIndex(dates[:-1]) + with tm.assert_produces_warning(None): + # GH#22535 check that we don't get a FutureWarning from adding + # an integer array to PeriodIndex + result = SemiMonthBegin() + s + + exp = DatetimeIndex(dates[1:]) + tm.assert_index_equal(result, exp) + + # ensure generating a range with DatetimeIndex gives same result + result = date_range(start=dates[0], end=dates[-1], freq="SMS") + exp = DatetimeIndex(dates, freq="SMS") + tm.assert_index_equal(result, exp) + + offset_cases = [ + ( + SemiMonthBegin(), + { + datetime(2008, 1, 1): datetime(2008, 1, 15), + datetime(2008, 1, 15): datetime(2008, 2, 1), + datetime(2008, 1, 31): datetime(2008, 2, 1), + datetime(2006, 12, 14): datetime(2006, 12, 15), + datetime(2006, 12, 29): datetime(2007, 1, 1), + datetime(2006, 12, 31): datetime(2007, 1, 1), + datetime(2007, 1, 1): datetime(2007, 1, 15), + datetime(2006, 12, 1): datetime(2006, 12, 15), + datetime(2006, 12, 15): datetime(2007, 1, 1), + }, + ), + ( + SemiMonthBegin(day_of_month=20), + { + datetime(2008, 1, 1): datetime(2008, 1, 20), + datetime(2008, 1, 15): datetime(2008, 1, 20), + datetime(2008, 1, 21): datetime(2008, 2, 1), + datetime(2008, 1, 31): datetime(2008, 2, 1), + datetime(2006, 12, 14): datetime(2006, 12, 20), + datetime(2006, 12, 29): datetime(2007, 1, 1), + datetime(2006, 12, 31): datetime(2007, 1, 1), + datetime(2007, 1, 1): datetime(2007, 1, 20), + datetime(2006, 12, 1): datetime(2006, 12, 20), + datetime(2006, 12, 15): datetime(2006, 12, 20), + }, + ), + ( + SemiMonthBegin(0), + { + datetime(2008, 1, 1): datetime(2008, 1, 1), + datetime(2008, 1, 16): datetime(2008, 2, 1), + datetime(2008, 1, 15): datetime(2008, 1, 15), + datetime(2008, 1, 31): datetime(2008, 2, 1), + datetime(2006, 12, 29): datetime(2007, 1, 1), + datetime(2006, 12, 2): datetime(2006, 12, 15), + datetime(2007, 1, 1): datetime(2007, 1, 1), + }, + ), + ( + SemiMonthBegin(0, day_of_month=16), + { + datetime(2008, 1, 1): datetime(2008, 1, 1), + datetime(2008, 1, 16): datetime(2008, 1, 16), + datetime(2008, 1, 15): datetime(2008, 1, 16), + datetime(2008, 1, 31): datetime(2008, 2, 1), + datetime(2006, 12, 29): datetime(2007, 1, 1), + datetime(2006, 12, 31): datetime(2007, 1, 1), + datetime(2007, 1, 5): datetime(2007, 1, 16), + datetime(2007, 1, 1): datetime(2007, 1, 1), + }, + ), + ( + SemiMonthBegin(2), + { + datetime(2008, 1, 1): datetime(2008, 2, 1), + datetime(2008, 1, 31): datetime(2008, 2, 15), + datetime(2006, 12, 1): datetime(2007, 1, 1), + datetime(2006, 12, 29): datetime(2007, 1, 15), + datetime(2006, 12, 15): datetime(2007, 1, 15), + datetime(2007, 1, 1): datetime(2007, 2, 1), + datetime(2007, 1, 16): datetime(2007, 2, 15), + datetime(2006, 11, 1): datetime(2006, 12, 1), + }, + ), + ( + SemiMonthBegin(-1), + { + datetime(2007, 1, 1): datetime(2006, 12, 15), + datetime(2008, 6, 30): datetime(2008, 6, 15), + datetime(2008, 6, 14): datetime(2008, 6, 1), + datetime(2008, 12, 31): datetime(2008, 12, 15), + datetime(2006, 12, 29): datetime(2006, 12, 15), + datetime(2006, 12, 15): datetime(2006, 12, 1), + datetime(2007, 1, 1): datetime(2006, 12, 15), + }, + ), + ( + SemiMonthBegin(-1, day_of_month=4), + { + datetime(2007, 1, 1): datetime(2006, 12, 4), + datetime(2007, 1, 4): datetime(2007, 1, 1), + datetime(2008, 6, 30): datetime(2008, 6, 4), + datetime(2008, 12, 31): datetime(2008, 12, 4), + datetime(2006, 12, 5): datetime(2006, 12, 4), + datetime(2006, 12, 30): datetime(2006, 12, 4), + datetime(2006, 12, 2): datetime(2006, 12, 1), + datetime(2007, 1, 1): datetime(2006, 12, 4), + }, + ), + ( + SemiMonthBegin(-2), + { + datetime(2007, 1, 1): datetime(2006, 12, 1), + datetime(2008, 6, 30): datetime(2008, 6, 1), + datetime(2008, 6, 14): datetime(2008, 5, 15), + datetime(2008, 12, 31): datetime(2008, 12, 1), + datetime(2006, 12, 29): datetime(2006, 12, 1), + datetime(2006, 12, 15): datetime(2006, 11, 15), + datetime(2007, 1, 1): datetime(2006, 12, 1), + }, + ), + ] + + @pytest.mark.parametrize("case", offset_cases) + def test_offset(self, case): + offset, cases = case + for base, expected in cases.items(): + assert_offset_equal(offset, base, expected) + + @pytest.mark.parametrize("case", offset_cases) + def test_apply_index(self, case): + offset, cases = case + s = DatetimeIndex(cases.keys()) + + with tm.assert_produces_warning(None): + # GH#22535 check that we don't get a FutureWarning from adding + # an integer array to PeriodIndex + result = offset + s + + exp = DatetimeIndex(cases.values()) + tm.assert_index_equal(result, exp) + + on_offset_cases = [ + (datetime(2007, 12, 1), True), + (datetime(2007, 12, 15), True), + (datetime(2007, 12, 14), False), + (datetime(2007, 12, 31), False), + (datetime(2008, 2, 15), True), + ] + + @pytest.mark.parametrize("case", on_offset_cases) + def test_is_on_offset(self, case): + dt, expected = case + assert_is_on_offset(SemiMonthBegin(), dt, expected) + + @pytest.mark.parametrize("klass", [Series, DatetimeIndex]) + def test_vectorized_offset_addition(self, klass): + s = klass( + [ + Timestamp("2000-01-15 00:15:00", tz="US/Central"), + Timestamp("2000-02-15", tz="US/Central"), + ], + name="a", + ) + with tm.assert_produces_warning(None): + # GH#22535 check that we don't get a FutureWarning from adding + # an integer array to PeriodIndex + result = s + SemiMonthBegin() + result2 = SemiMonthBegin() + s + + exp = klass( + [ + Timestamp("2000-02-01 00:15:00", tz="US/Central"), + Timestamp("2000-03-01", tz="US/Central"), + ], + name="a", + ) + tm.assert_equal(result, exp) + tm.assert_equal(result2, exp) + + s = klass( + [ + Timestamp("2000-01-01 00:15:00", tz="US/Central"), + Timestamp("2000-02-01", tz="US/Central"), + ], + name="a", + ) + with tm.assert_produces_warning(None): + # GH#22535 check that we don't get a FutureWarning from adding + # an integer array to PeriodIndex + result = s + SemiMonthBegin() + result2 = SemiMonthBegin() + s + + exp = klass( + [ + Timestamp("2000-01-15 00:15:00", tz="US/Central"), + Timestamp("2000-02-15", tz="US/Central"), + ], + name="a", + ) + tm.assert_equal(result, exp) + tm.assert_equal(result2, exp) diff --git a/pandas/tests/tseries/offsets/test_offsets.py b/pandas/tests/tseries/offsets/test_offsets.py index 325a5311829dc..b65f8084e4bec 100644 --- a/pandas/tests/tseries/offsets/test_offsets.py +++ b/pandas/tests/tseries/offsets/test_offsets.py @@ -1,43 +1,30 @@ -from datetime import date, datetime, time as dt_time, timedelta -from typing import Dict, List, Optional, Tuple, Type +""" +Tests of pandas.tseries.offsets +""" +from datetime import datetime, timedelta +from typing import Dict, List, Tuple -from dateutil.tz import tzlocal import numpy as np import pytest -from pandas._libs.tslibs import ( - NaT, - OutOfBoundsDatetime, - Timestamp, - conversion, - timezones, -) +from pandas._libs.tslibs import NaT, Timestamp, conversion, timezones import pandas._libs.tslibs.offsets as liboffsets -from pandas._libs.tslibs.offsets import ApplyTypeError, _get_offset, _offset_map +from pandas._libs.tslibs.offsets import _get_offset, _offset_map from pandas._libs.tslibs.period import INVALID_FREQ_ERR_MSG -from pandas.compat import IS64 from pandas.compat.numpy import np_datetime64_compat from pandas.errors import PerformanceWarning -from pandas import DatetimeIndex, Series, Timedelta, date_range, read_pickle +from pandas import DatetimeIndex import pandas._testing as tm +from pandas.tests.tseries.offsets.common import Base, WeekDay, assert_offset_equal -from pandas.tseries.holiday import USFederalHolidayCalendar import pandas.tseries.offsets as offsets from pandas.tseries.offsets import ( FY5253, BaseOffset, BDay, - BMonthBegin, BMonthEnd, - BQuarterBegin, - BQuarterEnd, BusinessHour, - BYearBegin, - BYearEnd, - CBMonthBegin, - CBMonthEnd, - CDay, CustomBusinessDay, CustomBusinessHour, CustomBusinessMonthBegin, @@ -48,182 +35,15 @@ FY5253Quarter, LastWeekOfMonth, MonthBegin, - MonthEnd, Nano, - QuarterBegin, - QuarterEnd, - SemiMonthBegin, - SemiMonthEnd, Tick, Week, WeekOfMonth, - YearBegin, - YearEnd, ) -from .common import assert_is_on_offset, assert_offset_equal - - -class WeekDay: - # TODO: Remove: This is not used outside of tests - MON = 0 - TUE = 1 - WED = 2 - THU = 3 - FRI = 4 - SAT = 5 - SUN = 6 - - -##### -# DateOffset Tests -##### _ApplyCases = List[Tuple[BaseOffset, Dict[datetime, datetime]]] -class Base: - _offset: Optional[Type[DateOffset]] = None - d = Timestamp(datetime(2008, 1, 2)) - - timezones = [ - None, - "UTC", - "Asia/Tokyo", - "US/Eastern", - "dateutil/Asia/Tokyo", - "dateutil/US/Pacific", - ] - - def _get_offset(self, klass, value=1, normalize=False): - # create instance from offset class - if klass is FY5253: - klass = klass( - n=value, - startingMonth=1, - weekday=1, - variation="last", - normalize=normalize, - ) - elif klass is FY5253Quarter: - klass = klass( - n=value, - startingMonth=1, - weekday=1, - qtr_with_extra_week=1, - variation="last", - normalize=normalize, - ) - elif klass is LastWeekOfMonth: - klass = klass(n=value, weekday=5, normalize=normalize) - elif klass is WeekOfMonth: - klass = klass(n=value, week=1, weekday=5, normalize=normalize) - elif klass is Week: - klass = klass(n=value, weekday=5, normalize=normalize) - elif klass is DateOffset: - klass = klass(days=value, normalize=normalize) - else: - klass = klass(value, normalize=normalize) - return klass - - def test_apply_out_of_range(self, tz_naive_fixture): - tz = tz_naive_fixture - if self._offset is None: - return - if isinstance(tz, tzlocal) and not IS64: - pytest.xfail(reason="OverflowError inside tzlocal past 2038") - - # try to create an out-of-bounds result timestamp; if we can't create - # the offset skip - try: - if self._offset in (BusinessHour, CustomBusinessHour): - # Using 10000 in BusinessHour fails in tz check because of DST - # difference - offset = self._get_offset(self._offset, value=100000) - else: - offset = self._get_offset(self._offset, value=10000) - - result = Timestamp("20080101") + offset - assert isinstance(result, datetime) - assert result.tzinfo is None - - # Check tz is preserved - t = Timestamp("20080101", tz=tz) - result = t + offset - assert isinstance(result, datetime) - assert t.tzinfo == result.tzinfo - - except OutOfBoundsDatetime: - pass - except (ValueError, KeyError): - # we are creating an invalid offset - # so ignore - pass - - def test_offsets_compare_equal(self): - # root cause of GH#456: __ne__ was not implemented - if self._offset is None: - return - offset1 = self._offset() - offset2 = self._offset() - assert not offset1 != offset2 - assert offset1 == offset2 - - def test_rsub(self): - if self._offset is None or not hasattr(self, "offset2"): - # i.e. skip for TestCommon and YQM subclasses that do not have - # offset2 attr - return - assert self.d - self.offset2 == (-self.offset2).apply(self.d) - - def test_radd(self): - if self._offset is None or not hasattr(self, "offset2"): - # i.e. skip for TestCommon and YQM subclasses that do not have - # offset2 attr - return - assert self.d + self.offset2 == self.offset2 + self.d - - def test_sub(self): - if self._offset is None or not hasattr(self, "offset2"): - # i.e. skip for TestCommon and YQM subclasses that do not have - # offset2 attr - return - off = self.offset2 - msg = "Cannot subtract datetime from offset" - with pytest.raises(TypeError, match=msg): - off - self.d - - assert 2 * off - off == off - assert self.d - self.offset2 == self.d + self._offset(-2) - assert self.d - self.offset2 == self.d - (2 * off - off) - - def testMult1(self): - if self._offset is None or not hasattr(self, "offset1"): - # i.e. skip for TestCommon and YQM subclasses that do not have - # offset1 attr - return - assert self.d + 10 * self.offset1 == self.d + self._offset(10) - assert self.d + 5 * self.offset1 == self.d + self._offset(5) - - def testMult2(self): - if self._offset is None: - return - assert self.d + (-5 * self._offset(-10)) == self.d + self._offset(50) - assert self.d + (-3 * self._offset(-2)) == self.d + self._offset(6) - - def test_compare_str(self): - # GH#23524 - # comparing to strings that cannot be cast to DateOffsets should - # not raise for __eq__ or __ne__ - if self._offset is None: - return - off = self._get_offset(self._offset) - - assert not off == "infer" - assert off != "foo" - # Note: inequalities are only implemented for Tick subclasses; - # tests for this are in test_ticks - - class TestCommon(Base): # exected value created by Base._get_offset # are applied to 2011/01/01 09:00 (Saturday) @@ -724,3327 +544,6 @@ def test_eq(self): assert offset1 != offset2 -class TestBusinessDay(Base): - _offset = BDay - - def setup_method(self, method): - self.d = datetime(2008, 1, 1) - - self.offset = BDay() - self.offset1 = self.offset - self.offset2 = BDay(2) - - def test_different_normalize_equals(self): - # GH#21404 changed __eq__ to return False when `normalize` does not match - offset = self._offset() - offset2 = self._offset(normalize=True) - assert offset != offset2 - - def test_repr(self): - assert repr(self.offset) == "" - assert repr(self.offset2) == "<2 * BusinessDays>" - - expected = "" - assert repr(self.offset + timedelta(1)) == expected - - def test_with_offset(self): - offset = self.offset + timedelta(hours=2) - - assert (self.d + offset) == datetime(2008, 1, 2, 2) - - def test_with_offset_index(self): - dti = DatetimeIndex([self.d]) - result = dti + (self.offset + timedelta(hours=2)) - - expected = DatetimeIndex([datetime(2008, 1, 2, 2)]) - tm.assert_index_equal(result, expected) - - def test_eq(self): - assert self.offset2 == self.offset2 - - def test_mul(self): - pass - - def test_hash(self): - assert hash(self.offset2) == hash(self.offset2) - - def test_call(self): - with tm.assert_produces_warning(FutureWarning): - # GH#34171 DateOffset.__call__ is deprecated - assert self.offset2(self.d) == datetime(2008, 1, 3) - - def testRollback1(self): - assert BDay(10).rollback(self.d) == self.d - - def testRollback2(self): - assert BDay(10).rollback(datetime(2008, 1, 5)) == datetime(2008, 1, 4) - - def testRollforward1(self): - assert BDay(10).rollforward(self.d) == self.d - - def testRollforward2(self): - assert BDay(10).rollforward(datetime(2008, 1, 5)) == datetime(2008, 1, 7) - - def test_roll_date_object(self): - offset = BDay() - - dt = date(2012, 9, 15) - - result = offset.rollback(dt) - assert result == datetime(2012, 9, 14) - - result = offset.rollforward(dt) - assert result == datetime(2012, 9, 17) - - offset = offsets.Day() - result = offset.rollback(dt) - assert result == datetime(2012, 9, 15) - - result = offset.rollforward(dt) - assert result == datetime(2012, 9, 15) - - def test_is_on_offset(self): - tests = [ - (BDay(), datetime(2008, 1, 1), True), - (BDay(), datetime(2008, 1, 5), False), - ] - - for offset, d, expected in tests: - assert_is_on_offset(offset, d, expected) - - apply_cases: _ApplyCases = [] - apply_cases.append( - ( - BDay(), - { - datetime(2008, 1, 1): datetime(2008, 1, 2), - datetime(2008, 1, 4): datetime(2008, 1, 7), - datetime(2008, 1, 5): datetime(2008, 1, 7), - datetime(2008, 1, 6): datetime(2008, 1, 7), - datetime(2008, 1, 7): datetime(2008, 1, 8), - }, - ) - ) - - apply_cases.append( - ( - 2 * BDay(), - { - datetime(2008, 1, 1): datetime(2008, 1, 3), - datetime(2008, 1, 4): datetime(2008, 1, 8), - datetime(2008, 1, 5): datetime(2008, 1, 8), - datetime(2008, 1, 6): datetime(2008, 1, 8), - datetime(2008, 1, 7): datetime(2008, 1, 9), - }, - ) - ) - - apply_cases.append( - ( - -BDay(), - { - datetime(2008, 1, 1): datetime(2007, 12, 31), - datetime(2008, 1, 4): datetime(2008, 1, 3), - datetime(2008, 1, 5): datetime(2008, 1, 4), - datetime(2008, 1, 6): datetime(2008, 1, 4), - datetime(2008, 1, 7): datetime(2008, 1, 4), - datetime(2008, 1, 8): datetime(2008, 1, 7), - }, - ) - ) - - apply_cases.append( - ( - -2 * BDay(), - { - datetime(2008, 1, 1): datetime(2007, 12, 28), - datetime(2008, 1, 4): datetime(2008, 1, 2), - datetime(2008, 1, 5): datetime(2008, 1, 3), - datetime(2008, 1, 6): datetime(2008, 1, 3), - datetime(2008, 1, 7): datetime(2008, 1, 3), - datetime(2008, 1, 8): datetime(2008, 1, 4), - datetime(2008, 1, 9): datetime(2008, 1, 7), - }, - ) - ) - - apply_cases.append( - ( - BDay(0), - { - datetime(2008, 1, 1): datetime(2008, 1, 1), - datetime(2008, 1, 4): datetime(2008, 1, 4), - datetime(2008, 1, 5): datetime(2008, 1, 7), - datetime(2008, 1, 6): datetime(2008, 1, 7), - datetime(2008, 1, 7): datetime(2008, 1, 7), - }, - ) - ) - - @pytest.mark.parametrize("case", apply_cases) - def test_apply(self, case): - offset, cases = case - for base, expected in cases.items(): - assert_offset_equal(offset, base, expected) - - def test_apply_large_n(self): - dt = datetime(2012, 10, 23) - - result = dt + BDay(10) - assert result == datetime(2012, 11, 6) - - result = dt + BDay(100) - BDay(100) - assert result == dt - - off = BDay() * 6 - rs = datetime(2012, 1, 1) - off - xp = datetime(2011, 12, 23) - assert rs == xp - - st = datetime(2011, 12, 18) - rs = st + off - xp = datetime(2011, 12, 26) - assert rs == xp - - off = BDay() * 10 - rs = datetime(2014, 1, 5) + off # see #5890 - xp = datetime(2014, 1, 17) - assert rs == xp - - def test_apply_corner(self): - msg = "Only know how to combine business day with datetime or timedelta" - with pytest.raises(ApplyTypeError, match=msg): - BDay().apply(BMonthEnd()) - - -class TestBusinessHour(Base): - _offset = BusinessHour - - def setup_method(self, method): - self.d = datetime(2014, 7, 1, 10, 00) - - self.offset1 = BusinessHour() - self.offset2 = BusinessHour(n=3) - - self.offset3 = BusinessHour(n=-1) - self.offset4 = BusinessHour(n=-4) - - from datetime import time as dt_time - - self.offset5 = BusinessHour(start=dt_time(11, 0), end=dt_time(14, 30)) - self.offset6 = BusinessHour(start="20:00", end="05:00") - self.offset7 = BusinessHour(n=-2, start=dt_time(21, 30), end=dt_time(6, 30)) - self.offset8 = BusinessHour(start=["09:00", "13:00"], end=["12:00", "17:00"]) - self.offset9 = BusinessHour( - n=3, start=["09:00", "22:00"], end=["13:00", "03:00"] - ) - self.offset10 = BusinessHour( - n=-1, start=["23:00", "13:00"], end=["02:00", "17:00"] - ) - - @pytest.mark.parametrize( - "start,end,match", - [ - ( - dt_time(11, 0, 5), - "17:00", - "time data must be specified only with hour and minute", - ), - ("AAA", "17:00", "time data must match '%H:%M' format"), - ("14:00:05", "17:00", "time data must match '%H:%M' format"), - ([], "17:00", "Must include at least 1 start time"), - ("09:00", [], "Must include at least 1 end time"), - ( - ["09:00", "11:00"], - "17:00", - "number of starting time and ending time must be the same", - ), - ( - ["09:00", "11:00"], - ["10:00"], - "number of starting time and ending time must be the same", - ), - ( - ["09:00", "11:00"], - ["12:00", "20:00"], - r"invalid starting and ending time\(s\): opening hours should not " - "touch or overlap with one another", - ), - ( - ["12:00", "20:00"], - ["09:00", "11:00"], - r"invalid starting and ending time\(s\): opening hours should not " - "touch or overlap with one another", - ), - ], - ) - def test_constructor_errors(self, start, end, match): - with pytest.raises(ValueError, match=match): - BusinessHour(start=start, end=end) - - def test_different_normalize_equals(self): - # GH#21404 changed __eq__ to return False when `normalize` does not match - offset = self._offset() - offset2 = self._offset(normalize=True) - assert offset != offset2 - - def test_repr(self): - assert repr(self.offset1) == "" - assert repr(self.offset2) == "<3 * BusinessHours: BH=09:00-17:00>" - assert repr(self.offset3) == "<-1 * BusinessHour: BH=09:00-17:00>" - assert repr(self.offset4) == "<-4 * BusinessHours: BH=09:00-17:00>" - - assert repr(self.offset5) == "" - assert repr(self.offset6) == "" - assert repr(self.offset7) == "<-2 * BusinessHours: BH=21:30-06:30>" - assert repr(self.offset8) == "" - assert repr(self.offset9) == "<3 * BusinessHours: BH=09:00-13:00,22:00-03:00>" - assert repr(self.offset10) == "<-1 * BusinessHour: BH=13:00-17:00,23:00-02:00>" - - def test_with_offset(self): - expected = Timestamp("2014-07-01 13:00") - - assert self.d + BusinessHour() * 3 == expected - assert self.d + BusinessHour(n=3) == expected - - @pytest.mark.parametrize( - "offset_name", - ["offset1", "offset2", "offset3", "offset4", "offset8", "offset9", "offset10"], - ) - def test_eq_attribute(self, offset_name): - offset = getattr(self, offset_name) - assert offset == offset - - @pytest.mark.parametrize( - "offset1,offset2", - [ - (BusinessHour(start="09:00"), BusinessHour()), - ( - BusinessHour(start=["23:00", "13:00"], end=["12:00", "17:00"]), - BusinessHour(start=["13:00", "23:00"], end=["17:00", "12:00"]), - ), - ], - ) - def test_eq(self, offset1, offset2): - assert offset1 == offset2 - - @pytest.mark.parametrize( - "offset1,offset2", - [ - (BusinessHour(), BusinessHour(-1)), - (BusinessHour(start="09:00"), BusinessHour(start="09:01")), - ( - BusinessHour(start="09:00", end="17:00"), - BusinessHour(start="17:00", end="09:01"), - ), - ( - BusinessHour(start=["13:00", "23:00"], end=["18:00", "07:00"]), - BusinessHour(start=["13:00", "23:00"], end=["17:00", "12:00"]), - ), - ], - ) - def test_neq(self, offset1, offset2): - assert offset1 != offset2 - - @pytest.mark.parametrize( - "offset_name", - ["offset1", "offset2", "offset3", "offset4", "offset8", "offset9", "offset10"], - ) - def test_hash(self, offset_name): - offset = getattr(self, offset_name) - assert offset == offset - - def test_call(self): - with tm.assert_produces_warning(FutureWarning): - # GH#34171 DateOffset.__call__ is deprecated - assert self.offset1(self.d) == datetime(2014, 7, 1, 11) - assert self.offset2(self.d) == datetime(2014, 7, 1, 13) - assert self.offset3(self.d) == datetime(2014, 6, 30, 17) - assert self.offset4(self.d) == datetime(2014, 6, 30, 14) - assert self.offset8(self.d) == datetime(2014, 7, 1, 11) - assert self.offset9(self.d) == datetime(2014, 7, 1, 22) - assert self.offset10(self.d) == datetime(2014, 7, 1, 1) - - def test_sub(self): - # we have to override test_sub here because self.offset2 is not - # defined as self._offset(2) - off = self.offset2 - msg = "Cannot subtract datetime from offset" - with pytest.raises(TypeError, match=msg): - off - self.d - assert 2 * off - off == off - - assert self.d - self.offset2 == self.d + self._offset(-3) - - def testRollback1(self): - assert self.offset1.rollback(self.d) == self.d - assert self.offset2.rollback(self.d) == self.d - assert self.offset3.rollback(self.d) == self.d - assert self.offset4.rollback(self.d) == self.d - assert self.offset5.rollback(self.d) == datetime(2014, 6, 30, 14, 30) - assert self.offset6.rollback(self.d) == datetime(2014, 7, 1, 5, 0) - assert self.offset7.rollback(self.d) == datetime(2014, 7, 1, 6, 30) - assert self.offset8.rollback(self.d) == self.d - assert self.offset9.rollback(self.d) == self.d - assert self.offset10.rollback(self.d) == datetime(2014, 7, 1, 2) - - d = datetime(2014, 7, 1, 0) - assert self.offset1.rollback(d) == datetime(2014, 6, 30, 17) - assert self.offset2.rollback(d) == datetime(2014, 6, 30, 17) - assert self.offset3.rollback(d) == datetime(2014, 6, 30, 17) - assert self.offset4.rollback(d) == datetime(2014, 6, 30, 17) - assert self.offset5.rollback(d) == datetime(2014, 6, 30, 14, 30) - assert self.offset6.rollback(d) == d - assert self.offset7.rollback(d) == d - assert self.offset8.rollback(d) == datetime(2014, 6, 30, 17) - assert self.offset9.rollback(d) == d - assert self.offset10.rollback(d) == d - - assert self._offset(5).rollback(self.d) == self.d - - def testRollback2(self): - assert self._offset(-3).rollback(datetime(2014, 7, 5, 15, 0)) == datetime( - 2014, 7, 4, 17, 0 - ) - - def testRollforward1(self): - assert self.offset1.rollforward(self.d) == self.d - assert self.offset2.rollforward(self.d) == self.d - assert self.offset3.rollforward(self.d) == self.d - assert self.offset4.rollforward(self.d) == self.d - assert self.offset5.rollforward(self.d) == datetime(2014, 7, 1, 11, 0) - assert self.offset6.rollforward(self.d) == datetime(2014, 7, 1, 20, 0) - assert self.offset7.rollforward(self.d) == datetime(2014, 7, 1, 21, 30) - assert self.offset8.rollforward(self.d) == self.d - assert self.offset9.rollforward(self.d) == self.d - assert self.offset10.rollforward(self.d) == datetime(2014, 7, 1, 13) - - d = datetime(2014, 7, 1, 0) - assert self.offset1.rollforward(d) == datetime(2014, 7, 1, 9) - assert self.offset2.rollforward(d) == datetime(2014, 7, 1, 9) - assert self.offset3.rollforward(d) == datetime(2014, 7, 1, 9) - assert self.offset4.rollforward(d) == datetime(2014, 7, 1, 9) - assert self.offset5.rollforward(d) == datetime(2014, 7, 1, 11) - assert self.offset6.rollforward(d) == d - assert self.offset7.rollforward(d) == d - assert self.offset8.rollforward(d) == datetime(2014, 7, 1, 9) - assert self.offset9.rollforward(d) == d - assert self.offset10.rollforward(d) == d - - assert self._offset(5).rollforward(self.d) == self.d - - def testRollforward2(self): - assert self._offset(-3).rollforward(datetime(2014, 7, 5, 16, 0)) == datetime( - 2014, 7, 7, 9 - ) - - def test_roll_date_object(self): - offset = BusinessHour() - - dt = datetime(2014, 7, 6, 15, 0) - - result = offset.rollback(dt) - assert result == datetime(2014, 7, 4, 17) - - result = offset.rollforward(dt) - assert result == datetime(2014, 7, 7, 9) - - normalize_cases = [] - normalize_cases.append( - ( - BusinessHour(normalize=True), - { - datetime(2014, 7, 1, 8): datetime(2014, 7, 1), - datetime(2014, 7, 1, 17): datetime(2014, 7, 2), - datetime(2014, 7, 1, 16): datetime(2014, 7, 2), - datetime(2014, 7, 1, 23): datetime(2014, 7, 2), - datetime(2014, 7, 1, 0): datetime(2014, 7, 1), - datetime(2014, 7, 4, 15): datetime(2014, 7, 4), - datetime(2014, 7, 4, 15, 59): datetime(2014, 7, 4), - datetime(2014, 7, 4, 16, 30): datetime(2014, 7, 7), - datetime(2014, 7, 5, 23): datetime(2014, 7, 7), - datetime(2014, 7, 6, 10): datetime(2014, 7, 7), - }, - ) - ) - - normalize_cases.append( - ( - BusinessHour(-1, normalize=True), - { - datetime(2014, 7, 1, 8): datetime(2014, 6, 30), - datetime(2014, 7, 1, 17): datetime(2014, 7, 1), - datetime(2014, 7, 1, 16): datetime(2014, 7, 1), - datetime(2014, 7, 1, 10): datetime(2014, 6, 30), - datetime(2014, 7, 1, 0): datetime(2014, 6, 30), - datetime(2014, 7, 7, 10): datetime(2014, 7, 4), - datetime(2014, 7, 7, 10, 1): datetime(2014, 7, 7), - datetime(2014, 7, 5, 23): datetime(2014, 7, 4), - datetime(2014, 7, 6, 10): datetime(2014, 7, 4), - }, - ) - ) - - normalize_cases.append( - ( - BusinessHour(1, normalize=True, start="17:00", end="04:00"), - { - datetime(2014, 7, 1, 8): datetime(2014, 7, 1), - datetime(2014, 7, 1, 17): datetime(2014, 7, 1), - datetime(2014, 7, 1, 23): datetime(2014, 7, 2), - datetime(2014, 7, 2, 2): datetime(2014, 7, 2), - datetime(2014, 7, 2, 3): datetime(2014, 7, 2), - datetime(2014, 7, 4, 23): datetime(2014, 7, 5), - datetime(2014, 7, 5, 2): datetime(2014, 7, 5), - datetime(2014, 7, 7, 2): datetime(2014, 7, 7), - datetime(2014, 7, 7, 17): datetime(2014, 7, 7), - }, - ) - ) - - @pytest.mark.parametrize("case", normalize_cases) - def test_normalize(self, case): - offset, cases = case - for dt, expected in cases.items(): - assert offset.apply(dt) == expected - - on_offset_cases = [] - on_offset_cases.append( - ( - BusinessHour(), - { - datetime(2014, 7, 1, 9): True, - datetime(2014, 7, 1, 8, 59): False, - datetime(2014, 7, 1, 8): False, - datetime(2014, 7, 1, 17): True, - datetime(2014, 7, 1, 17, 1): False, - datetime(2014, 7, 1, 18): False, - datetime(2014, 7, 5, 9): False, - datetime(2014, 7, 6, 12): False, - }, - ) - ) - - on_offset_cases.append( - ( - BusinessHour(start="10:00", end="15:00"), - { - datetime(2014, 7, 1, 9): False, - datetime(2014, 7, 1, 10): True, - datetime(2014, 7, 1, 15): True, - datetime(2014, 7, 1, 15, 1): False, - datetime(2014, 7, 5, 12): False, - datetime(2014, 7, 6, 12): False, - }, - ) - ) - - on_offset_cases.append( - ( - BusinessHour(start="19:00", end="05:00"), - { - datetime(2014, 7, 1, 9, 0): False, - datetime(2014, 7, 1, 10, 0): False, - datetime(2014, 7, 1, 15): False, - datetime(2014, 7, 1, 15, 1): False, - datetime(2014, 7, 5, 12, 0): False, - datetime(2014, 7, 6, 12, 0): False, - datetime(2014, 7, 1, 19, 0): True, - datetime(2014, 7, 2, 0, 0): True, - datetime(2014, 7, 4, 23): True, - datetime(2014, 7, 5, 1): True, - datetime(2014, 7, 5, 5, 0): True, - datetime(2014, 7, 6, 23, 0): False, - datetime(2014, 7, 7, 3, 0): False, - }, - ) - ) - - on_offset_cases.append( - ( - BusinessHour(start=["09:00", "13:00"], end=["12:00", "17:00"]), - { - datetime(2014, 7, 1, 9): True, - datetime(2014, 7, 1, 8, 59): False, - datetime(2014, 7, 1, 8): False, - datetime(2014, 7, 1, 17): True, - datetime(2014, 7, 1, 17, 1): False, - datetime(2014, 7, 1, 18): False, - datetime(2014, 7, 5, 9): False, - datetime(2014, 7, 6, 12): False, - datetime(2014, 7, 1, 12, 30): False, - }, - ) - ) - - on_offset_cases.append( - ( - BusinessHour(start=["19:00", "23:00"], end=["21:00", "05:00"]), - { - datetime(2014, 7, 1, 9, 0): False, - datetime(2014, 7, 1, 10, 0): False, - datetime(2014, 7, 1, 15): False, - datetime(2014, 7, 1, 15, 1): False, - datetime(2014, 7, 5, 12, 0): False, - datetime(2014, 7, 6, 12, 0): False, - datetime(2014, 7, 1, 19, 0): True, - datetime(2014, 7, 2, 0, 0): True, - datetime(2014, 7, 4, 23): True, - datetime(2014, 7, 5, 1): True, - datetime(2014, 7, 5, 5, 0): True, - datetime(2014, 7, 6, 23, 0): False, - datetime(2014, 7, 7, 3, 0): False, - datetime(2014, 7, 4, 22): False, - }, - ) - ) - - @pytest.mark.parametrize("case", on_offset_cases) - def test_is_on_offset(self, case): - offset, cases = case - for dt, expected in cases.items(): - assert offset.is_on_offset(dt) == expected - - opening_time_cases = [] - # opening time should be affected by sign of n, not by n's value and - # end - opening_time_cases.append( - ( - [ - BusinessHour(), - BusinessHour(n=2), - BusinessHour(n=4), - BusinessHour(end="10:00"), - BusinessHour(n=2, end="4:00"), - BusinessHour(n=4, end="15:00"), - ], - { - datetime(2014, 7, 1, 11): ( - datetime(2014, 7, 2, 9), - datetime(2014, 7, 1, 9), - ), - datetime(2014, 7, 1, 18): ( - datetime(2014, 7, 2, 9), - datetime(2014, 7, 1, 9), - ), - datetime(2014, 7, 1, 23): ( - datetime(2014, 7, 2, 9), - datetime(2014, 7, 1, 9), - ), - datetime(2014, 7, 2, 8): ( - datetime(2014, 7, 2, 9), - datetime(2014, 7, 1, 9), - ), - # if timestamp is on opening time, next opening time is - # as it is - datetime(2014, 7, 2, 9): ( - datetime(2014, 7, 2, 9), - datetime(2014, 7, 2, 9), - ), - datetime(2014, 7, 2, 10): ( - datetime(2014, 7, 3, 9), - datetime(2014, 7, 2, 9), - ), - # 2014-07-05 is saturday - datetime(2014, 7, 5, 10): ( - datetime(2014, 7, 7, 9), - datetime(2014, 7, 4, 9), - ), - datetime(2014, 7, 4, 10): ( - datetime(2014, 7, 7, 9), - datetime(2014, 7, 4, 9), - ), - datetime(2014, 7, 4, 23): ( - datetime(2014, 7, 7, 9), - datetime(2014, 7, 4, 9), - ), - datetime(2014, 7, 6, 10): ( - datetime(2014, 7, 7, 9), - datetime(2014, 7, 4, 9), - ), - datetime(2014, 7, 7, 5): ( - datetime(2014, 7, 7, 9), - datetime(2014, 7, 4, 9), - ), - datetime(2014, 7, 7, 9, 1): ( - datetime(2014, 7, 8, 9), - datetime(2014, 7, 7, 9), - ), - }, - ) - ) - - opening_time_cases.append( - ( - [ - BusinessHour(start="11:15"), - BusinessHour(n=2, start="11:15"), - BusinessHour(n=3, start="11:15"), - BusinessHour(start="11:15", end="10:00"), - BusinessHour(n=2, start="11:15", end="4:00"), - BusinessHour(n=3, start="11:15", end="15:00"), - ], - { - datetime(2014, 7, 1, 11): ( - datetime(2014, 7, 1, 11, 15), - datetime(2014, 6, 30, 11, 15), - ), - datetime(2014, 7, 1, 18): ( - datetime(2014, 7, 2, 11, 15), - datetime(2014, 7, 1, 11, 15), - ), - datetime(2014, 7, 1, 23): ( - datetime(2014, 7, 2, 11, 15), - datetime(2014, 7, 1, 11, 15), - ), - datetime(2014, 7, 2, 8): ( - datetime(2014, 7, 2, 11, 15), - datetime(2014, 7, 1, 11, 15), - ), - datetime(2014, 7, 2, 9): ( - datetime(2014, 7, 2, 11, 15), - datetime(2014, 7, 1, 11, 15), - ), - datetime(2014, 7, 2, 10): ( - datetime(2014, 7, 2, 11, 15), - datetime(2014, 7, 1, 11, 15), - ), - datetime(2014, 7, 2, 11, 15): ( - datetime(2014, 7, 2, 11, 15), - datetime(2014, 7, 2, 11, 15), - ), - datetime(2014, 7, 2, 11, 15, 1): ( - datetime(2014, 7, 3, 11, 15), - datetime(2014, 7, 2, 11, 15), - ), - datetime(2014, 7, 5, 10): ( - datetime(2014, 7, 7, 11, 15), - datetime(2014, 7, 4, 11, 15), - ), - datetime(2014, 7, 4, 10): ( - datetime(2014, 7, 4, 11, 15), - datetime(2014, 7, 3, 11, 15), - ), - datetime(2014, 7, 4, 23): ( - datetime(2014, 7, 7, 11, 15), - datetime(2014, 7, 4, 11, 15), - ), - datetime(2014, 7, 6, 10): ( - datetime(2014, 7, 7, 11, 15), - datetime(2014, 7, 4, 11, 15), - ), - datetime(2014, 7, 7, 5): ( - datetime(2014, 7, 7, 11, 15), - datetime(2014, 7, 4, 11, 15), - ), - datetime(2014, 7, 7, 9, 1): ( - datetime(2014, 7, 7, 11, 15), - datetime(2014, 7, 4, 11, 15), - ), - }, - ) - ) - - opening_time_cases.append( - ( - [ - BusinessHour(-1), - BusinessHour(n=-2), - BusinessHour(n=-4), - BusinessHour(n=-1, end="10:00"), - BusinessHour(n=-2, end="4:00"), - BusinessHour(n=-4, end="15:00"), - ], - { - datetime(2014, 7, 1, 11): ( - datetime(2014, 7, 1, 9), - datetime(2014, 7, 2, 9), - ), - datetime(2014, 7, 1, 18): ( - datetime(2014, 7, 1, 9), - datetime(2014, 7, 2, 9), - ), - datetime(2014, 7, 1, 23): ( - datetime(2014, 7, 1, 9), - datetime(2014, 7, 2, 9), - ), - datetime(2014, 7, 2, 8): ( - datetime(2014, 7, 1, 9), - datetime(2014, 7, 2, 9), - ), - datetime(2014, 7, 2, 9): ( - datetime(2014, 7, 2, 9), - datetime(2014, 7, 2, 9), - ), - datetime(2014, 7, 2, 10): ( - datetime(2014, 7, 2, 9), - datetime(2014, 7, 3, 9), - ), - datetime(2014, 7, 5, 10): ( - datetime(2014, 7, 4, 9), - datetime(2014, 7, 7, 9), - ), - datetime(2014, 7, 4, 10): ( - datetime(2014, 7, 4, 9), - datetime(2014, 7, 7, 9), - ), - datetime(2014, 7, 4, 23): ( - datetime(2014, 7, 4, 9), - datetime(2014, 7, 7, 9), - ), - datetime(2014, 7, 6, 10): ( - datetime(2014, 7, 4, 9), - datetime(2014, 7, 7, 9), - ), - datetime(2014, 7, 7, 5): ( - datetime(2014, 7, 4, 9), - datetime(2014, 7, 7, 9), - ), - datetime(2014, 7, 7, 9): ( - datetime(2014, 7, 7, 9), - datetime(2014, 7, 7, 9), - ), - datetime(2014, 7, 7, 9, 1): ( - datetime(2014, 7, 7, 9), - datetime(2014, 7, 8, 9), - ), - }, - ) - ) - - opening_time_cases.append( - ( - [ - BusinessHour(start="17:00", end="05:00"), - BusinessHour(n=3, start="17:00", end="03:00"), - ], - { - datetime(2014, 7, 1, 11): ( - datetime(2014, 7, 1, 17), - datetime(2014, 6, 30, 17), - ), - datetime(2014, 7, 1, 18): ( - datetime(2014, 7, 2, 17), - datetime(2014, 7, 1, 17), - ), - datetime(2014, 7, 1, 23): ( - datetime(2014, 7, 2, 17), - datetime(2014, 7, 1, 17), - ), - datetime(2014, 7, 2, 8): ( - datetime(2014, 7, 2, 17), - datetime(2014, 7, 1, 17), - ), - datetime(2014, 7, 2, 9): ( - datetime(2014, 7, 2, 17), - datetime(2014, 7, 1, 17), - ), - datetime(2014, 7, 4, 17): ( - datetime(2014, 7, 4, 17), - datetime(2014, 7, 4, 17), - ), - datetime(2014, 7, 5, 10): ( - datetime(2014, 7, 7, 17), - datetime(2014, 7, 4, 17), - ), - datetime(2014, 7, 4, 10): ( - datetime(2014, 7, 4, 17), - datetime(2014, 7, 3, 17), - ), - datetime(2014, 7, 4, 23): ( - datetime(2014, 7, 7, 17), - datetime(2014, 7, 4, 17), - ), - datetime(2014, 7, 6, 10): ( - datetime(2014, 7, 7, 17), - datetime(2014, 7, 4, 17), - ), - datetime(2014, 7, 7, 5): ( - datetime(2014, 7, 7, 17), - datetime(2014, 7, 4, 17), - ), - datetime(2014, 7, 7, 17, 1): ( - datetime(2014, 7, 8, 17), - datetime(2014, 7, 7, 17), - ), - }, - ) - ) - - opening_time_cases.append( - ( - [ - BusinessHour(-1, start="17:00", end="05:00"), - BusinessHour(n=-2, start="17:00", end="03:00"), - ], - { - datetime(2014, 7, 1, 11): ( - datetime(2014, 6, 30, 17), - datetime(2014, 7, 1, 17), - ), - datetime(2014, 7, 1, 18): ( - datetime(2014, 7, 1, 17), - datetime(2014, 7, 2, 17), - ), - datetime(2014, 7, 1, 23): ( - datetime(2014, 7, 1, 17), - datetime(2014, 7, 2, 17), - ), - datetime(2014, 7, 2, 8): ( - datetime(2014, 7, 1, 17), - datetime(2014, 7, 2, 17), - ), - datetime(2014, 7, 2, 9): ( - datetime(2014, 7, 1, 17), - datetime(2014, 7, 2, 17), - ), - datetime(2014, 7, 2, 16, 59): ( - datetime(2014, 7, 1, 17), - datetime(2014, 7, 2, 17), - ), - datetime(2014, 7, 5, 10): ( - datetime(2014, 7, 4, 17), - datetime(2014, 7, 7, 17), - ), - datetime(2014, 7, 4, 10): ( - datetime(2014, 7, 3, 17), - datetime(2014, 7, 4, 17), - ), - datetime(2014, 7, 4, 23): ( - datetime(2014, 7, 4, 17), - datetime(2014, 7, 7, 17), - ), - datetime(2014, 7, 6, 10): ( - datetime(2014, 7, 4, 17), - datetime(2014, 7, 7, 17), - ), - datetime(2014, 7, 7, 5): ( - datetime(2014, 7, 4, 17), - datetime(2014, 7, 7, 17), - ), - datetime(2014, 7, 7, 18): ( - datetime(2014, 7, 7, 17), - datetime(2014, 7, 8, 17), - ), - }, - ) - ) - - opening_time_cases.append( - ( - [ - BusinessHour(start=["11:15", "15:00"], end=["13:00", "20:00"]), - BusinessHour(n=3, start=["11:15", "15:00"], end=["12:00", "20:00"]), - BusinessHour(start=["11:15", "15:00"], end=["13:00", "17:00"]), - BusinessHour(n=2, start=["11:15", "15:00"], end=["12:00", "03:00"]), - BusinessHour(n=3, start=["11:15", "15:00"], end=["13:00", "16:00"]), - ], - { - datetime(2014, 7, 1, 11): ( - datetime(2014, 7, 1, 11, 15), - datetime(2014, 6, 30, 15), - ), - datetime(2014, 7, 1, 18): ( - datetime(2014, 7, 2, 11, 15), - datetime(2014, 7, 1, 15), - ), - datetime(2014, 7, 1, 23): ( - datetime(2014, 7, 2, 11, 15), - datetime(2014, 7, 1, 15), - ), - datetime(2014, 7, 2, 8): ( - datetime(2014, 7, 2, 11, 15), - datetime(2014, 7, 1, 15), - ), - datetime(2014, 7, 2, 9): ( - datetime(2014, 7, 2, 11, 15), - datetime(2014, 7, 1, 15), - ), - datetime(2014, 7, 2, 10): ( - datetime(2014, 7, 2, 11, 15), - datetime(2014, 7, 1, 15), - ), - datetime(2014, 7, 2, 11, 15): ( - datetime(2014, 7, 2, 11, 15), - datetime(2014, 7, 2, 11, 15), - ), - datetime(2014, 7, 2, 11, 15, 1): ( - datetime(2014, 7, 2, 15), - datetime(2014, 7, 2, 11, 15), - ), - datetime(2014, 7, 5, 10): ( - datetime(2014, 7, 7, 11, 15), - datetime(2014, 7, 4, 15), - ), - datetime(2014, 7, 4, 10): ( - datetime(2014, 7, 4, 11, 15), - datetime(2014, 7, 3, 15), - ), - datetime(2014, 7, 4, 23): ( - datetime(2014, 7, 7, 11, 15), - datetime(2014, 7, 4, 15), - ), - datetime(2014, 7, 6, 10): ( - datetime(2014, 7, 7, 11, 15), - datetime(2014, 7, 4, 15), - ), - datetime(2014, 7, 7, 5): ( - datetime(2014, 7, 7, 11, 15), - datetime(2014, 7, 4, 15), - ), - datetime(2014, 7, 7, 9, 1): ( - datetime(2014, 7, 7, 11, 15), - datetime(2014, 7, 4, 15), - ), - datetime(2014, 7, 7, 12): ( - datetime(2014, 7, 7, 15), - datetime(2014, 7, 7, 11, 15), - ), - }, - ) - ) - - opening_time_cases.append( - ( - [ - BusinessHour(n=-1, start=["17:00", "08:00"], end=["05:00", "10:00"]), - BusinessHour(n=-2, start=["08:00", "17:00"], end=["10:00", "03:00"]), - ], - { - datetime(2014, 7, 1, 11): ( - datetime(2014, 7, 1, 8), - datetime(2014, 7, 1, 17), - ), - datetime(2014, 7, 1, 18): ( - datetime(2014, 7, 1, 17), - datetime(2014, 7, 2, 8), - ), - datetime(2014, 7, 1, 23): ( - datetime(2014, 7, 1, 17), - datetime(2014, 7, 2, 8), - ), - datetime(2014, 7, 2, 8): ( - datetime(2014, 7, 2, 8), - datetime(2014, 7, 2, 8), - ), - datetime(2014, 7, 2, 9): ( - datetime(2014, 7, 2, 8), - datetime(2014, 7, 2, 17), - ), - datetime(2014, 7, 2, 16, 59): ( - datetime(2014, 7, 2, 8), - datetime(2014, 7, 2, 17), - ), - datetime(2014, 7, 5, 10): ( - datetime(2014, 7, 4, 17), - datetime(2014, 7, 7, 8), - ), - datetime(2014, 7, 4, 10): ( - datetime(2014, 7, 4, 8), - datetime(2014, 7, 4, 17), - ), - datetime(2014, 7, 4, 23): ( - datetime(2014, 7, 4, 17), - datetime(2014, 7, 7, 8), - ), - datetime(2014, 7, 6, 10): ( - datetime(2014, 7, 4, 17), - datetime(2014, 7, 7, 8), - ), - datetime(2014, 7, 7, 5): ( - datetime(2014, 7, 4, 17), - datetime(2014, 7, 7, 8), - ), - datetime(2014, 7, 7, 18): ( - datetime(2014, 7, 7, 17), - datetime(2014, 7, 8, 8), - ), - }, - ) - ) - - @pytest.mark.parametrize("case", opening_time_cases) - def test_opening_time(self, case): - _offsets, cases = case - for offset in _offsets: - for dt, (exp_next, exp_prev) in cases.items(): - assert offset._next_opening_time(dt) == exp_next - assert offset._prev_opening_time(dt) == exp_prev - - apply_cases = [] - apply_cases.append( - ( - BusinessHour(), - { - datetime(2014, 7, 1, 11): datetime(2014, 7, 1, 12), - datetime(2014, 7, 1, 13): datetime(2014, 7, 1, 14), - datetime(2014, 7, 1, 15): datetime(2014, 7, 1, 16), - datetime(2014, 7, 1, 19): datetime(2014, 7, 2, 10), - datetime(2014, 7, 1, 16): datetime(2014, 7, 2, 9), - datetime(2014, 7, 1, 16, 30, 15): datetime(2014, 7, 2, 9, 30, 15), - datetime(2014, 7, 1, 17): datetime(2014, 7, 2, 10), - datetime(2014, 7, 2, 11): datetime(2014, 7, 2, 12), - # out of business hours - datetime(2014, 7, 2, 8): datetime(2014, 7, 2, 10), - datetime(2014, 7, 2, 19): datetime(2014, 7, 3, 10), - datetime(2014, 7, 2, 23): datetime(2014, 7, 3, 10), - datetime(2014, 7, 3, 0): datetime(2014, 7, 3, 10), - # saturday - datetime(2014, 7, 5, 15): datetime(2014, 7, 7, 10), - datetime(2014, 7, 4, 17): datetime(2014, 7, 7, 10), - datetime(2014, 7, 4, 16, 30): datetime(2014, 7, 7, 9, 30), - datetime(2014, 7, 4, 16, 30, 30): datetime(2014, 7, 7, 9, 30, 30), - }, - ) - ) - - apply_cases.append( - ( - BusinessHour(4), - { - datetime(2014, 7, 1, 11): datetime(2014, 7, 1, 15), - datetime(2014, 7, 1, 13): datetime(2014, 7, 2, 9), - datetime(2014, 7, 1, 15): datetime(2014, 7, 2, 11), - datetime(2014, 7, 1, 16): datetime(2014, 7, 2, 12), - datetime(2014, 7, 1, 17): datetime(2014, 7, 2, 13), - datetime(2014, 7, 2, 11): datetime(2014, 7, 2, 15), - datetime(2014, 7, 2, 8): datetime(2014, 7, 2, 13), - datetime(2014, 7, 2, 19): datetime(2014, 7, 3, 13), - datetime(2014, 7, 2, 23): datetime(2014, 7, 3, 13), - datetime(2014, 7, 3, 0): datetime(2014, 7, 3, 13), - datetime(2014, 7, 5, 15): datetime(2014, 7, 7, 13), - datetime(2014, 7, 4, 17): datetime(2014, 7, 7, 13), - datetime(2014, 7, 4, 16, 30): datetime(2014, 7, 7, 12, 30), - datetime(2014, 7, 4, 16, 30, 30): datetime(2014, 7, 7, 12, 30, 30), - }, - ) - ) - - apply_cases.append( - ( - BusinessHour(-1), - { - datetime(2014, 7, 1, 11): datetime(2014, 7, 1, 10), - datetime(2014, 7, 1, 13): datetime(2014, 7, 1, 12), - datetime(2014, 7, 1, 15): datetime(2014, 7, 1, 14), - datetime(2014, 7, 1, 16): datetime(2014, 7, 1, 15), - datetime(2014, 7, 1, 10): datetime(2014, 6, 30, 17), - datetime(2014, 7, 1, 16, 30, 15): datetime(2014, 7, 1, 15, 30, 15), - datetime(2014, 7, 1, 9, 30, 15): datetime(2014, 6, 30, 16, 30, 15), - datetime(2014, 7, 1, 17): datetime(2014, 7, 1, 16), - datetime(2014, 7, 1, 5): datetime(2014, 6, 30, 16), - datetime(2014, 7, 2, 11): datetime(2014, 7, 2, 10), - # out of business hours - datetime(2014, 7, 2, 8): datetime(2014, 7, 1, 16), - datetime(2014, 7, 2, 19): datetime(2014, 7, 2, 16), - datetime(2014, 7, 2, 23): datetime(2014, 7, 2, 16), - datetime(2014, 7, 3, 0): datetime(2014, 7, 2, 16), - # saturday - datetime(2014, 7, 5, 15): datetime(2014, 7, 4, 16), - datetime(2014, 7, 7, 9): datetime(2014, 7, 4, 16), - datetime(2014, 7, 7, 9, 30): datetime(2014, 7, 4, 16, 30), - datetime(2014, 7, 7, 9, 30, 30): datetime(2014, 7, 4, 16, 30, 30), - }, - ) - ) - - apply_cases.append( - ( - BusinessHour(-4), - { - datetime(2014, 7, 1, 11): datetime(2014, 6, 30, 15), - datetime(2014, 7, 1, 13): datetime(2014, 6, 30, 17), - datetime(2014, 7, 1, 15): datetime(2014, 7, 1, 11), - datetime(2014, 7, 1, 16): datetime(2014, 7, 1, 12), - datetime(2014, 7, 1, 17): datetime(2014, 7, 1, 13), - datetime(2014, 7, 2, 11): datetime(2014, 7, 1, 15), - datetime(2014, 7, 2, 8): datetime(2014, 7, 1, 13), - datetime(2014, 7, 2, 19): datetime(2014, 7, 2, 13), - datetime(2014, 7, 2, 23): datetime(2014, 7, 2, 13), - datetime(2014, 7, 3, 0): datetime(2014, 7, 2, 13), - datetime(2014, 7, 5, 15): datetime(2014, 7, 4, 13), - datetime(2014, 7, 4, 18): datetime(2014, 7, 4, 13), - datetime(2014, 7, 7, 9, 30): datetime(2014, 7, 4, 13, 30), - datetime(2014, 7, 7, 9, 30, 30): datetime(2014, 7, 4, 13, 30, 30), - }, - ) - ) - - apply_cases.append( - ( - BusinessHour(start="13:00", end="16:00"), - { - datetime(2014, 7, 1, 11): datetime(2014, 7, 1, 14), - datetime(2014, 7, 1, 13): datetime(2014, 7, 1, 14), - datetime(2014, 7, 1, 15): datetime(2014, 7, 2, 13), - datetime(2014, 7, 1, 19): datetime(2014, 7, 2, 14), - datetime(2014, 7, 1, 16): datetime(2014, 7, 2, 14), - datetime(2014, 7, 1, 15, 30, 15): datetime(2014, 7, 2, 13, 30, 15), - datetime(2014, 7, 5, 15): datetime(2014, 7, 7, 14), - datetime(2014, 7, 4, 17): datetime(2014, 7, 7, 14), - }, - ) - ) - - apply_cases.append( - ( - BusinessHour(n=2, start="13:00", end="16:00"), - { - datetime(2014, 7, 1, 17): datetime(2014, 7, 2, 15), - datetime(2014, 7, 2, 14): datetime(2014, 7, 3, 13), - datetime(2014, 7, 2, 8): datetime(2014, 7, 2, 15), - datetime(2014, 7, 2, 19): datetime(2014, 7, 3, 15), - datetime(2014, 7, 2, 14, 30): datetime(2014, 7, 3, 13, 30), - datetime(2014, 7, 3, 0): datetime(2014, 7, 3, 15), - datetime(2014, 7, 5, 15): datetime(2014, 7, 7, 15), - datetime(2014, 7, 4, 17): datetime(2014, 7, 7, 15), - datetime(2014, 7, 4, 14, 30): datetime(2014, 7, 7, 13, 30), - datetime(2014, 7, 4, 14, 30, 30): datetime(2014, 7, 7, 13, 30, 30), - }, - ) - ) - - apply_cases.append( - ( - BusinessHour(n=-1, start="13:00", end="16:00"), - { - datetime(2014, 7, 2, 11): datetime(2014, 7, 1, 15), - datetime(2014, 7, 2, 13): datetime(2014, 7, 1, 15), - datetime(2014, 7, 2, 14): datetime(2014, 7, 1, 16), - datetime(2014, 7, 2, 15): datetime(2014, 7, 2, 14), - datetime(2014, 7, 2, 19): datetime(2014, 7, 2, 15), - datetime(2014, 7, 2, 16): datetime(2014, 7, 2, 15), - datetime(2014, 7, 2, 13, 30, 15): datetime(2014, 7, 1, 15, 30, 15), - datetime(2014, 7, 5, 15): datetime(2014, 7, 4, 15), - datetime(2014, 7, 7, 11): datetime(2014, 7, 4, 15), - }, - ) - ) - - apply_cases.append( - ( - BusinessHour(n=-3, start="10:00", end="16:00"), - { - datetime(2014, 7, 1, 17): datetime(2014, 7, 1, 13), - datetime(2014, 7, 2, 14): datetime(2014, 7, 2, 11), - datetime(2014, 7, 2, 8): datetime(2014, 7, 1, 13), - datetime(2014, 7, 2, 13): datetime(2014, 7, 1, 16), - datetime(2014, 7, 2, 19): datetime(2014, 7, 2, 13), - datetime(2014, 7, 2, 11, 30): datetime(2014, 7, 1, 14, 30), - datetime(2014, 7, 3, 0): datetime(2014, 7, 2, 13), - datetime(2014, 7, 4, 10): datetime(2014, 7, 3, 13), - datetime(2014, 7, 5, 15): datetime(2014, 7, 4, 13), - datetime(2014, 7, 4, 16): datetime(2014, 7, 4, 13), - datetime(2014, 7, 4, 12, 30): datetime(2014, 7, 3, 15, 30), - datetime(2014, 7, 4, 12, 30, 30): datetime(2014, 7, 3, 15, 30, 30), - }, - ) - ) - - apply_cases.append( - ( - BusinessHour(start="19:00", end="05:00"), - { - datetime(2014, 7, 1, 17): datetime(2014, 7, 1, 20), - datetime(2014, 7, 2, 14): datetime(2014, 7, 2, 20), - datetime(2014, 7, 2, 8): datetime(2014, 7, 2, 20), - datetime(2014, 7, 2, 13): datetime(2014, 7, 2, 20), - datetime(2014, 7, 2, 19): datetime(2014, 7, 2, 20), - datetime(2014, 7, 2, 4, 30): datetime(2014, 7, 2, 19, 30), - datetime(2014, 7, 3, 0): datetime(2014, 7, 3, 1), - datetime(2014, 7, 4, 10): datetime(2014, 7, 4, 20), - datetime(2014, 7, 4, 23): datetime(2014, 7, 5, 0), - datetime(2014, 7, 5, 0): datetime(2014, 7, 5, 1), - datetime(2014, 7, 5, 4): datetime(2014, 7, 7, 19), - datetime(2014, 7, 5, 4, 30): datetime(2014, 7, 7, 19, 30), - datetime(2014, 7, 5, 4, 30, 30): datetime(2014, 7, 7, 19, 30, 30), - }, - ) - ) - - apply_cases.append( - ( - BusinessHour(n=-1, start="19:00", end="05:00"), - { - datetime(2014, 7, 1, 17): datetime(2014, 7, 1, 4), - datetime(2014, 7, 2, 14): datetime(2014, 7, 2, 4), - datetime(2014, 7, 2, 8): datetime(2014, 7, 2, 4), - datetime(2014, 7, 2, 13): datetime(2014, 7, 2, 4), - datetime(2014, 7, 2, 20): datetime(2014, 7, 2, 5), - datetime(2014, 7, 2, 19): datetime(2014, 7, 2, 4), - datetime(2014, 7, 2, 19, 30): datetime(2014, 7, 2, 4, 30), - datetime(2014, 7, 3, 0): datetime(2014, 7, 2, 23), - datetime(2014, 7, 3, 6): datetime(2014, 7, 3, 4), - datetime(2014, 7, 4, 23): datetime(2014, 7, 4, 22), - datetime(2014, 7, 5, 0): datetime(2014, 7, 4, 23), - datetime(2014, 7, 5, 4): datetime(2014, 7, 5, 3), - 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 (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), - 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), - }, - ) - ) - - # multiple business hours - apply_cases.append( - ( - BusinessHour(start=["09:00", "14:00"], end=["12:00", "18:00"]), - { - datetime(2014, 7, 1, 11): datetime(2014, 7, 1, 14), - datetime(2014, 7, 1, 15): datetime(2014, 7, 1, 16), - datetime(2014, 7, 1, 19): datetime(2014, 7, 2, 10), - datetime(2014, 7, 1, 16): datetime(2014, 7, 1, 17), - datetime(2014, 7, 1, 16, 30, 15): datetime(2014, 7, 1, 17, 30, 15), - datetime(2014, 7, 1, 17): datetime(2014, 7, 2, 9), - datetime(2014, 7, 2, 11): datetime(2014, 7, 2, 14), - # out of business hours - datetime(2014, 7, 1, 13): datetime(2014, 7, 1, 15), - datetime(2014, 7, 2, 8): datetime(2014, 7, 2, 10), - datetime(2014, 7, 2, 19): datetime(2014, 7, 3, 10), - datetime(2014, 7, 2, 23): datetime(2014, 7, 3, 10), - datetime(2014, 7, 3, 0): datetime(2014, 7, 3, 10), - # saturday - datetime(2014, 7, 5, 15): datetime(2014, 7, 7, 10), - datetime(2014, 7, 4, 17): datetime(2014, 7, 7, 9), - datetime(2014, 7, 4, 17, 30): datetime(2014, 7, 7, 9, 30), - datetime(2014, 7, 4, 17, 30, 30): datetime(2014, 7, 7, 9, 30, 30), - }, - ) - ) - - apply_cases.append( - ( - BusinessHour(n=4, start=["09:00", "14:00"], end=["12:00", "18:00"]), - { - datetime(2014, 7, 1, 11): datetime(2014, 7, 1, 17), - datetime(2014, 7, 1, 13): datetime(2014, 7, 2, 9), - datetime(2014, 7, 1, 15): datetime(2014, 7, 2, 10), - datetime(2014, 7, 1, 16): datetime(2014, 7, 2, 11), - datetime(2014, 7, 1, 17): datetime(2014, 7, 2, 14), - datetime(2014, 7, 2, 11): datetime(2014, 7, 2, 17), - datetime(2014, 7, 2, 8): datetime(2014, 7, 2, 15), - datetime(2014, 7, 2, 19): datetime(2014, 7, 3, 15), - datetime(2014, 7, 2, 23): datetime(2014, 7, 3, 15), - datetime(2014, 7, 3, 0): datetime(2014, 7, 3, 15), - datetime(2014, 7, 5, 15): datetime(2014, 7, 7, 15), - datetime(2014, 7, 4, 17): datetime(2014, 7, 7, 14), - datetime(2014, 7, 4, 16, 30): datetime(2014, 7, 7, 11, 30), - datetime(2014, 7, 4, 16, 30, 30): datetime(2014, 7, 7, 11, 30, 30), - }, - ) - ) - - apply_cases.append( - ( - BusinessHour(n=-4, start=["09:00", "14:00"], end=["12:00", "18:00"]), - { - datetime(2014, 7, 1, 11): datetime(2014, 6, 30, 16), - datetime(2014, 7, 1, 13): datetime(2014, 6, 30, 17), - datetime(2014, 7, 1, 15): datetime(2014, 6, 30, 18), - datetime(2014, 7, 1, 16): datetime(2014, 7, 1, 10), - datetime(2014, 7, 1, 17): datetime(2014, 7, 1, 11), - datetime(2014, 7, 2, 11): datetime(2014, 7, 1, 16), - datetime(2014, 7, 2, 8): datetime(2014, 7, 1, 12), - datetime(2014, 7, 2, 19): datetime(2014, 7, 2, 12), - datetime(2014, 7, 2, 23): datetime(2014, 7, 2, 12), - datetime(2014, 7, 3, 0): datetime(2014, 7, 2, 12), - datetime(2014, 7, 5, 15): datetime(2014, 7, 4, 12), - datetime(2014, 7, 4, 18): datetime(2014, 7, 4, 12), - datetime(2014, 7, 7, 9, 30): datetime(2014, 7, 4, 14, 30), - datetime(2014, 7, 7, 9, 30, 30): datetime(2014, 7, 4, 14, 30, 30), - }, - ) - ) - - apply_cases.append( - ( - BusinessHour(n=-1, start=["19:00", "03:00"], end=["01:00", "05:00"]), - { - datetime(2014, 7, 1, 17): datetime(2014, 7, 1, 4), - datetime(2014, 7, 2, 14): datetime(2014, 7, 2, 4), - datetime(2014, 7, 2, 8): datetime(2014, 7, 2, 4), - datetime(2014, 7, 2, 13): datetime(2014, 7, 2, 4), - datetime(2014, 7, 2, 20): datetime(2014, 7, 2, 5), - datetime(2014, 7, 2, 19): datetime(2014, 7, 2, 4), - datetime(2014, 7, 2, 4): datetime(2014, 7, 2, 1), - datetime(2014, 7, 2, 19, 30): datetime(2014, 7, 2, 4, 30), - datetime(2014, 7, 3, 0): datetime(2014, 7, 2, 23), - datetime(2014, 7, 3, 6): datetime(2014, 7, 3, 4), - datetime(2014, 7, 4, 23): datetime(2014, 7, 4, 22), - datetime(2014, 7, 5, 0): datetime(2014, 7, 4, 23), - datetime(2014, 7, 5, 4): datetime(2014, 7, 5, 0), - datetime(2014, 7, 7, 3, 30): datetime(2014, 7, 5, 0, 30), - datetime(2014, 7, 7, 19, 30): datetime(2014, 7, 7, 4, 30), - datetime(2014, 7, 7, 19, 30, 30): datetime(2014, 7, 7, 4, 30, 30), - }, - ) - ) - - @pytest.mark.parametrize("case", apply_cases) - def test_apply(self, case): - offset, cases = case - for base, expected in cases.items(): - assert_offset_equal(offset, base, expected) - - apply_large_n_cases = [] - # A week later - apply_large_n_cases.append( - ( - BusinessHour(40), - { - datetime(2014, 7, 1, 11): datetime(2014, 7, 8, 11), - datetime(2014, 7, 1, 13): datetime(2014, 7, 8, 13), - datetime(2014, 7, 1, 15): datetime(2014, 7, 8, 15), - datetime(2014, 7, 1, 16): datetime(2014, 7, 8, 16), - datetime(2014, 7, 1, 17): datetime(2014, 7, 9, 9), - datetime(2014, 7, 2, 11): datetime(2014, 7, 9, 11), - datetime(2014, 7, 2, 8): datetime(2014, 7, 9, 9), - datetime(2014, 7, 2, 19): datetime(2014, 7, 10, 9), - datetime(2014, 7, 2, 23): datetime(2014, 7, 10, 9), - datetime(2014, 7, 3, 0): datetime(2014, 7, 10, 9), - datetime(2014, 7, 5, 15): datetime(2014, 7, 14, 9), - datetime(2014, 7, 4, 18): datetime(2014, 7, 14, 9), - datetime(2014, 7, 7, 9, 30): datetime(2014, 7, 14, 9, 30), - datetime(2014, 7, 7, 9, 30, 30): datetime(2014, 7, 14, 9, 30, 30), - }, - ) - ) - - # 3 days and 1 hour before - apply_large_n_cases.append( - ( - BusinessHour(-25), - { - datetime(2014, 7, 1, 11): datetime(2014, 6, 26, 10), - datetime(2014, 7, 1, 13): datetime(2014, 6, 26, 12), - datetime(2014, 7, 1, 9): datetime(2014, 6, 25, 16), - datetime(2014, 7, 1, 10): datetime(2014, 6, 25, 17), - datetime(2014, 7, 3, 11): datetime(2014, 6, 30, 10), - datetime(2014, 7, 3, 8): datetime(2014, 6, 27, 16), - datetime(2014, 7, 3, 19): datetime(2014, 6, 30, 16), - datetime(2014, 7, 3, 23): datetime(2014, 6, 30, 16), - datetime(2014, 7, 4, 9): datetime(2014, 6, 30, 16), - datetime(2014, 7, 5, 15): datetime(2014, 7, 1, 16), - datetime(2014, 7, 6, 18): datetime(2014, 7, 1, 16), - datetime(2014, 7, 7, 9, 30): datetime(2014, 7, 1, 16, 30), - datetime(2014, 7, 7, 10, 30, 30): datetime(2014, 7, 2, 9, 30, 30), - }, - ) - ) - - # 5 days and 3 hours later - apply_large_n_cases.append( - ( - BusinessHour(28, start="21:00", end="02:00"), - { - datetime(2014, 7, 1, 11): datetime(2014, 7, 9, 0), - datetime(2014, 7, 1, 22): datetime(2014, 7, 9, 1), - datetime(2014, 7, 1, 23): datetime(2014, 7, 9, 21), - datetime(2014, 7, 2, 2): datetime(2014, 7, 10, 0), - datetime(2014, 7, 3, 21): datetime(2014, 7, 11, 0), - datetime(2014, 7, 4, 1): datetime(2014, 7, 11, 23), - datetime(2014, 7, 4, 2): datetime(2014, 7, 12, 0), - datetime(2014, 7, 4, 3): datetime(2014, 7, 12, 0), - datetime(2014, 7, 5, 1): datetime(2014, 7, 14, 23), - datetime(2014, 7, 5, 15): datetime(2014, 7, 15, 0), - datetime(2014, 7, 6, 18): datetime(2014, 7, 15, 0), - datetime(2014, 7, 7, 1): datetime(2014, 7, 15, 0), - datetime(2014, 7, 7, 23, 30): datetime(2014, 7, 15, 21, 30), - }, - ) - ) - - # large n for multiple opening hours (3 days and 1 hour before) - apply_large_n_cases.append( - ( - BusinessHour(n=-25, start=["09:00", "14:00"], end=["12:00", "19:00"]), - { - datetime(2014, 7, 1, 11): datetime(2014, 6, 26, 10), - datetime(2014, 7, 1, 13): datetime(2014, 6, 26, 11), - datetime(2014, 7, 1, 9): datetime(2014, 6, 25, 18), - datetime(2014, 7, 1, 10): datetime(2014, 6, 25, 19), - datetime(2014, 7, 3, 11): datetime(2014, 6, 30, 10), - datetime(2014, 7, 3, 8): datetime(2014, 6, 27, 18), - datetime(2014, 7, 3, 19): datetime(2014, 6, 30, 18), - datetime(2014, 7, 3, 23): datetime(2014, 6, 30, 18), - datetime(2014, 7, 4, 9): datetime(2014, 6, 30, 18), - datetime(2014, 7, 5, 15): datetime(2014, 7, 1, 18), - datetime(2014, 7, 6, 18): datetime(2014, 7, 1, 18), - datetime(2014, 7, 7, 9, 30): datetime(2014, 7, 1, 18, 30), - datetime(2014, 7, 7, 10, 30, 30): datetime(2014, 7, 2, 9, 30, 30), - }, - ) - ) - - # 5 days and 3 hours later - apply_large_n_cases.append( - ( - BusinessHour(28, start=["21:00", "03:00"], end=["01:00", "04:00"]), - { - datetime(2014, 7, 1, 11): datetime(2014, 7, 9, 0), - datetime(2014, 7, 1, 22): datetime(2014, 7, 9, 3), - datetime(2014, 7, 1, 23): datetime(2014, 7, 9, 21), - datetime(2014, 7, 2, 2): datetime(2014, 7, 9, 23), - datetime(2014, 7, 3, 21): datetime(2014, 7, 11, 0), - datetime(2014, 7, 4, 1): datetime(2014, 7, 11, 23), - datetime(2014, 7, 4, 2): datetime(2014, 7, 11, 23), - datetime(2014, 7, 4, 3): datetime(2014, 7, 11, 23), - datetime(2014, 7, 4, 21): datetime(2014, 7, 12, 0), - datetime(2014, 7, 5, 0): datetime(2014, 7, 14, 22), - datetime(2014, 7, 5, 1): datetime(2014, 7, 14, 23), - datetime(2014, 7, 5, 15): datetime(2014, 7, 14, 23), - datetime(2014, 7, 6, 18): datetime(2014, 7, 14, 23), - datetime(2014, 7, 7, 1): datetime(2014, 7, 14, 23), - datetime(2014, 7, 7, 23, 30): datetime(2014, 7, 15, 21, 30), - }, - ) - ) - - @pytest.mark.parametrize("case", apply_large_n_cases) - def test_apply_large_n(self, case): - offset, cases = case - for base, expected in cases.items(): - assert_offset_equal(offset, base, expected) - - def test_apply_nanoseconds(self): - tests = [] - - tests.append( - ( - BusinessHour(), - { - Timestamp("2014-07-04 15:00") - + Nano(5): Timestamp("2014-07-04 16:00") - + Nano(5), - Timestamp("2014-07-04 16:00") - + Nano(5): Timestamp("2014-07-07 09:00") - + Nano(5), - Timestamp("2014-07-04 16:00") - - Nano(5): Timestamp("2014-07-04 17:00") - - Nano(5), - }, - ) - ) - - tests.append( - ( - BusinessHour(-1), - { - Timestamp("2014-07-04 15:00") - + Nano(5): Timestamp("2014-07-04 14:00") - + Nano(5), - Timestamp("2014-07-04 10:00") - + Nano(5): Timestamp("2014-07-04 09:00") - + Nano(5), - Timestamp("2014-07-04 10:00") - - Nano(5): Timestamp("2014-07-03 17:00") - - Nano(5), - }, - ) - ) - - for offset, cases in tests: - for base, expected in cases.items(): - assert_offset_equal(offset, base, expected) - - def test_datetimeindex(self): - idx1 = date_range(start="2014-07-04 15:00", end="2014-07-08 10:00", freq="BH") - idx2 = date_range(start="2014-07-04 15:00", periods=12, freq="BH") - idx3 = date_range(end="2014-07-08 10:00", periods=12, freq="BH") - expected = DatetimeIndex( - [ - "2014-07-04 15:00", - "2014-07-04 16:00", - "2014-07-07 09:00", - "2014-07-07 10:00", - "2014-07-07 11:00", - "2014-07-07 12:00", - "2014-07-07 13:00", - "2014-07-07 14:00", - "2014-07-07 15:00", - "2014-07-07 16:00", - "2014-07-08 09:00", - "2014-07-08 10:00", - ], - freq="BH", - ) - for idx in [idx1, idx2, idx3]: - tm.assert_index_equal(idx, expected) - - idx1 = date_range(start="2014-07-04 15:45", end="2014-07-08 10:45", freq="BH") - idx2 = date_range(start="2014-07-04 15:45", periods=12, freq="BH") - idx3 = date_range(end="2014-07-08 10:45", periods=12, freq="BH") - - expected = DatetimeIndex( - [ - "2014-07-04 15:45", - "2014-07-04 16:45", - "2014-07-07 09:45", - "2014-07-07 10:45", - "2014-07-07 11:45", - "2014-07-07 12:45", - "2014-07-07 13:45", - "2014-07-07 14:45", - "2014-07-07 15:45", - "2014-07-07 16:45", - "2014-07-08 09:45", - "2014-07-08 10:45", - ], - freq="BH", - ) - expected = idx1 - for idx in [idx1, idx2, idx3]: - tm.assert_index_equal(idx, expected) - - def test_bday_ignores_timedeltas(self): - idx = date_range("2010/02/01", "2010/02/10", freq="12H") - t1 = idx + BDay(offset=Timedelta(3, unit="H")) - - expected = DatetimeIndex( - [ - "2010-02-02 03:00:00", - "2010-02-02 15:00:00", - "2010-02-03 03:00:00", - "2010-02-03 15:00:00", - "2010-02-04 03:00:00", - "2010-02-04 15:00:00", - "2010-02-05 03:00:00", - "2010-02-05 15:00:00", - "2010-02-08 03:00:00", - "2010-02-08 15:00:00", - "2010-02-08 03:00:00", - "2010-02-08 15:00:00", - "2010-02-08 03:00:00", - "2010-02-08 15:00:00", - "2010-02-09 03:00:00", - "2010-02-09 15:00:00", - "2010-02-10 03:00:00", - "2010-02-10 15:00:00", - "2010-02-11 03:00:00", - ], - freq=None, - ) - tm.assert_index_equal(t1, expected) - - -class TestCustomBusinessHour(Base): - _offset = CustomBusinessHour - holidays = ["2014-06-27", datetime(2014, 6, 30), np.datetime64("2014-07-02")] - - def setup_method(self, method): - # 2014 Calendar to check custom holidays - # Sun Mon Tue Wed Thu Fri Sat - # 6/22 23 24 25 26 27 28 - # 29 30 7/1 2 3 4 5 - # 6 7 8 9 10 11 12 - self.d = datetime(2014, 7, 1, 10, 00) - self.offset1 = CustomBusinessHour(weekmask="Tue Wed Thu Fri") - - self.offset2 = CustomBusinessHour(holidays=self.holidays) - - def test_constructor_errors(self): - from datetime import time as dt_time - - msg = "time data must be specified only with hour and minute" - with pytest.raises(ValueError, match=msg): - CustomBusinessHour(start=dt_time(11, 0, 5)) - msg = "time data must match '%H:%M' format" - with pytest.raises(ValueError, match=msg): - CustomBusinessHour(start="AAA") - msg = "time data must match '%H:%M' format" - with pytest.raises(ValueError, match=msg): - CustomBusinessHour(start="14:00:05") - - def test_different_normalize_equals(self): - # GH#21404 changed __eq__ to return False when `normalize` does not match - offset = self._offset() - offset2 = self._offset(normalize=True) - assert offset != offset2 - - def test_repr(self): - assert repr(self.offset1) == "" - assert repr(self.offset2) == "" - - def test_with_offset(self): - expected = Timestamp("2014-07-01 13:00") - - assert self.d + CustomBusinessHour() * 3 == expected - assert self.d + CustomBusinessHour(n=3) == expected - - def test_eq(self): - for offset in [self.offset1, self.offset2]: - assert offset == offset - - assert CustomBusinessHour() != CustomBusinessHour(-1) - assert CustomBusinessHour(start="09:00") == CustomBusinessHour() - assert CustomBusinessHour(start="09:00") != CustomBusinessHour(start="09:01") - assert CustomBusinessHour(start="09:00", end="17:00") != CustomBusinessHour( - start="17:00", end="09:01" - ) - - assert CustomBusinessHour(weekmask="Tue Wed Thu Fri") != CustomBusinessHour( - weekmask="Mon Tue Wed Thu Fri" - ) - assert CustomBusinessHour(holidays=["2014-06-27"]) != CustomBusinessHour( - holidays=["2014-06-28"] - ) - - def test_sub(self): - # override the Base.test_sub implementation because self.offset2 is - # defined differently in this class than the test expects - pass - - def test_hash(self): - assert hash(self.offset1) == hash(self.offset1) - assert hash(self.offset2) == hash(self.offset2) - - def test_call(self): - with tm.assert_produces_warning(FutureWarning): - # GH#34171 DateOffset.__call__ is deprecated - assert self.offset1(self.d) == datetime(2014, 7, 1, 11) - assert self.offset2(self.d) == datetime(2014, 7, 1, 11) - - def testRollback1(self): - assert self.offset1.rollback(self.d) == self.d - assert self.offset2.rollback(self.d) == self.d - - d = datetime(2014, 7, 1, 0) - - # 2014/07/01 is Tuesday, 06/30 is Monday(holiday) - assert self.offset1.rollback(d) == datetime(2014, 6, 27, 17) - - # 2014/6/30 and 2014/6/27 are holidays - assert self.offset2.rollback(d) == datetime(2014, 6, 26, 17) - - def testRollback2(self): - assert self._offset(-3).rollback(datetime(2014, 7, 5, 15, 0)) == datetime( - 2014, 7, 4, 17, 0 - ) - - def testRollforward1(self): - assert self.offset1.rollforward(self.d) == self.d - assert self.offset2.rollforward(self.d) == self.d - - d = datetime(2014, 7, 1, 0) - assert self.offset1.rollforward(d) == datetime(2014, 7, 1, 9) - assert self.offset2.rollforward(d) == datetime(2014, 7, 1, 9) - - def testRollforward2(self): - assert self._offset(-3).rollforward(datetime(2014, 7, 5, 16, 0)) == datetime( - 2014, 7, 7, 9 - ) - - def test_roll_date_object(self): - offset = BusinessHour() - - dt = datetime(2014, 7, 6, 15, 0) - - result = offset.rollback(dt) - assert result == datetime(2014, 7, 4, 17) - - result = offset.rollforward(dt) - assert result == datetime(2014, 7, 7, 9) - - normalize_cases = [] - normalize_cases.append( - ( - CustomBusinessHour(normalize=True, holidays=holidays), - { - datetime(2014, 7, 1, 8): datetime(2014, 7, 1), - datetime(2014, 7, 1, 17): datetime(2014, 7, 3), - datetime(2014, 7, 1, 16): datetime(2014, 7, 3), - datetime(2014, 7, 1, 23): datetime(2014, 7, 3), - datetime(2014, 7, 1, 0): datetime(2014, 7, 1), - datetime(2014, 7, 4, 15): datetime(2014, 7, 4), - datetime(2014, 7, 4, 15, 59): datetime(2014, 7, 4), - datetime(2014, 7, 4, 16, 30): datetime(2014, 7, 7), - datetime(2014, 7, 5, 23): datetime(2014, 7, 7), - datetime(2014, 7, 6, 10): datetime(2014, 7, 7), - }, - ) - ) - - normalize_cases.append( - ( - CustomBusinessHour(-1, normalize=True, holidays=holidays), - { - datetime(2014, 7, 1, 8): datetime(2014, 6, 26), - datetime(2014, 7, 1, 17): datetime(2014, 7, 1), - datetime(2014, 7, 1, 16): datetime(2014, 7, 1), - datetime(2014, 7, 1, 10): datetime(2014, 6, 26), - datetime(2014, 7, 1, 0): datetime(2014, 6, 26), - datetime(2014, 7, 7, 10): datetime(2014, 7, 4), - datetime(2014, 7, 7, 10, 1): datetime(2014, 7, 7), - datetime(2014, 7, 5, 23): datetime(2014, 7, 4), - datetime(2014, 7, 6, 10): datetime(2014, 7, 4), - }, - ) - ) - - normalize_cases.append( - ( - CustomBusinessHour( - 1, normalize=True, start="17:00", end="04:00", holidays=holidays - ), - { - datetime(2014, 7, 1, 8): datetime(2014, 7, 1), - datetime(2014, 7, 1, 17): datetime(2014, 7, 1), - datetime(2014, 7, 1, 23): datetime(2014, 7, 2), - datetime(2014, 7, 2, 2): datetime(2014, 7, 2), - datetime(2014, 7, 2, 3): datetime(2014, 7, 3), - datetime(2014, 7, 4, 23): datetime(2014, 7, 5), - datetime(2014, 7, 5, 2): datetime(2014, 7, 5), - datetime(2014, 7, 7, 2): datetime(2014, 7, 7), - datetime(2014, 7, 7, 17): datetime(2014, 7, 7), - }, - ) - ) - - @pytest.mark.parametrize("norm_cases", normalize_cases) - def test_normalize(self, norm_cases): - offset, cases = norm_cases - for dt, expected in cases.items(): - assert offset.apply(dt) == expected - - def test_is_on_offset(self): - tests = [] - - tests.append( - ( - CustomBusinessHour(start="10:00", end="15:00", holidays=self.holidays), - { - datetime(2014, 7, 1, 9): False, - datetime(2014, 7, 1, 10): True, - datetime(2014, 7, 1, 15): True, - datetime(2014, 7, 1, 15, 1): False, - datetime(2014, 7, 5, 12): False, - datetime(2014, 7, 6, 12): False, - }, - ) - ) - - for offset, cases in tests: - for dt, expected in cases.items(): - assert offset.is_on_offset(dt) == expected - - apply_cases = [] - apply_cases.append( - ( - CustomBusinessHour(holidays=holidays), - { - datetime(2014, 7, 1, 11): datetime(2014, 7, 1, 12), - datetime(2014, 7, 1, 13): datetime(2014, 7, 1, 14), - datetime(2014, 7, 1, 15): datetime(2014, 7, 1, 16), - datetime(2014, 7, 1, 19): datetime(2014, 7, 3, 10), - datetime(2014, 7, 1, 16): datetime(2014, 7, 3, 9), - datetime(2014, 7, 1, 16, 30, 15): datetime(2014, 7, 3, 9, 30, 15), - datetime(2014, 7, 1, 17): datetime(2014, 7, 3, 10), - datetime(2014, 7, 2, 11): datetime(2014, 7, 3, 10), - # out of business hours - datetime(2014, 7, 2, 8): datetime(2014, 7, 3, 10), - datetime(2014, 7, 2, 19): datetime(2014, 7, 3, 10), - datetime(2014, 7, 2, 23): datetime(2014, 7, 3, 10), - datetime(2014, 7, 3, 0): datetime(2014, 7, 3, 10), - # saturday - datetime(2014, 7, 5, 15): datetime(2014, 7, 7, 10), - datetime(2014, 7, 4, 17): datetime(2014, 7, 7, 10), - datetime(2014, 7, 4, 16, 30): datetime(2014, 7, 7, 9, 30), - datetime(2014, 7, 4, 16, 30, 30): datetime(2014, 7, 7, 9, 30, 30), - }, - ) - ) - - apply_cases.append( - ( - CustomBusinessHour(4, holidays=holidays), - { - datetime(2014, 7, 1, 11): datetime(2014, 7, 1, 15), - datetime(2014, 7, 1, 13): datetime(2014, 7, 3, 9), - datetime(2014, 7, 1, 15): datetime(2014, 7, 3, 11), - datetime(2014, 7, 1, 16): datetime(2014, 7, 3, 12), - datetime(2014, 7, 1, 17): datetime(2014, 7, 3, 13), - datetime(2014, 7, 2, 11): datetime(2014, 7, 3, 13), - datetime(2014, 7, 2, 8): datetime(2014, 7, 3, 13), - datetime(2014, 7, 2, 19): datetime(2014, 7, 3, 13), - datetime(2014, 7, 2, 23): datetime(2014, 7, 3, 13), - datetime(2014, 7, 3, 0): datetime(2014, 7, 3, 13), - datetime(2014, 7, 5, 15): datetime(2014, 7, 7, 13), - datetime(2014, 7, 4, 17): datetime(2014, 7, 7, 13), - datetime(2014, 7, 4, 16, 30): datetime(2014, 7, 7, 12, 30), - datetime(2014, 7, 4, 16, 30, 30): datetime(2014, 7, 7, 12, 30, 30), - }, - ) - ) - - @pytest.mark.parametrize("apply_case", apply_cases) - def test_apply(self, apply_case): - offset, cases = apply_case - for base, expected in cases.items(): - assert_offset_equal(offset, base, expected) - - nano_cases = [] - nano_cases.append( - ( - CustomBusinessHour(holidays=holidays), - { - Timestamp("2014-07-01 15:00") - + Nano(5): Timestamp("2014-07-01 16:00") - + Nano(5), - Timestamp("2014-07-01 16:00") - + Nano(5): Timestamp("2014-07-03 09:00") - + Nano(5), - Timestamp("2014-07-01 16:00") - - Nano(5): Timestamp("2014-07-01 17:00") - - Nano(5), - }, - ) - ) - - nano_cases.append( - ( - CustomBusinessHour(-1, holidays=holidays), - { - Timestamp("2014-07-01 15:00") - + Nano(5): Timestamp("2014-07-01 14:00") - + Nano(5), - Timestamp("2014-07-01 10:00") - + Nano(5): Timestamp("2014-07-01 09:00") - + Nano(5), - Timestamp("2014-07-01 10:00") - - Nano(5): Timestamp("2014-06-26 17:00") - - Nano(5), - }, - ) - ) - - @pytest.mark.parametrize("nano_case", nano_cases) - def test_apply_nanoseconds(self, nano_case): - offset, cases = nano_case - for base, expected in cases.items(): - assert_offset_equal(offset, base, expected) - - -class TestCustomBusinessDay(Base): - _offset = CDay - - def setup_method(self, method): - self.d = datetime(2008, 1, 1) - self.nd = np_datetime64_compat("2008-01-01 00:00:00Z") - - self.offset = CDay() - self.offset1 = self.offset - self.offset2 = CDay(2) - - def test_different_normalize_equals(self): - # GH#21404 changed __eq__ to return False when `normalize` does not match - offset = self._offset() - offset2 = self._offset(normalize=True) - assert offset != offset2 - - def test_repr(self): - assert repr(self.offset) == "" - assert repr(self.offset2) == "<2 * CustomBusinessDays>" - - expected = "" - assert repr(self.offset + timedelta(1)) == expected - - def test_with_offset(self): - offset = self.offset + timedelta(hours=2) - - assert (self.d + offset) == datetime(2008, 1, 2, 2) - - def test_with_offset_index(self): - dti = DatetimeIndex([self.d]) - result = dti + (self.offset + timedelta(hours=2)) - - expected = DatetimeIndex([datetime(2008, 1, 2, 2)]) - tm.assert_index_equal(result, expected) - - def test_eq(self): - assert self.offset2 == self.offset2 - - def test_mul(self): - pass - - def test_hash(self): - assert hash(self.offset2) == hash(self.offset2) - - def test_call(self): - with tm.assert_produces_warning(FutureWarning): - # GH#34171 DateOffset.__call__ is deprecated - assert self.offset2(self.d) == datetime(2008, 1, 3) - assert self.offset2(self.nd) == datetime(2008, 1, 3) - - def testRollback1(self): - assert CDay(10).rollback(self.d) == self.d - - def testRollback2(self): - assert CDay(10).rollback(datetime(2008, 1, 5)) == datetime(2008, 1, 4) - - def testRollforward1(self): - assert CDay(10).rollforward(self.d) == self.d - - def testRollforward2(self): - assert CDay(10).rollforward(datetime(2008, 1, 5)) == datetime(2008, 1, 7) - - def test_roll_date_object(self): - offset = CDay() - - dt = date(2012, 9, 15) - - result = offset.rollback(dt) - assert result == datetime(2012, 9, 14) - - result = offset.rollforward(dt) - assert result == datetime(2012, 9, 17) - - offset = offsets.Day() - result = offset.rollback(dt) - assert result == datetime(2012, 9, 15) - - result = offset.rollforward(dt) - assert result == datetime(2012, 9, 15) - - on_offset_cases = [ - (CDay(), datetime(2008, 1, 1), True), - (CDay(), datetime(2008, 1, 5), False), - ] - - @pytest.mark.parametrize("case", on_offset_cases) - def test_is_on_offset(self, case): - offset, d, expected = case - assert_is_on_offset(offset, d, expected) - - apply_cases: _ApplyCases = [] - apply_cases.append( - ( - CDay(), - { - datetime(2008, 1, 1): datetime(2008, 1, 2), - datetime(2008, 1, 4): datetime(2008, 1, 7), - datetime(2008, 1, 5): datetime(2008, 1, 7), - datetime(2008, 1, 6): datetime(2008, 1, 7), - datetime(2008, 1, 7): datetime(2008, 1, 8), - }, - ) - ) - - apply_cases.append( - ( - 2 * CDay(), - { - datetime(2008, 1, 1): datetime(2008, 1, 3), - datetime(2008, 1, 4): datetime(2008, 1, 8), - datetime(2008, 1, 5): datetime(2008, 1, 8), - datetime(2008, 1, 6): datetime(2008, 1, 8), - datetime(2008, 1, 7): datetime(2008, 1, 9), - }, - ) - ) - - apply_cases.append( - ( - -CDay(), - { - datetime(2008, 1, 1): datetime(2007, 12, 31), - datetime(2008, 1, 4): datetime(2008, 1, 3), - datetime(2008, 1, 5): datetime(2008, 1, 4), - datetime(2008, 1, 6): datetime(2008, 1, 4), - datetime(2008, 1, 7): datetime(2008, 1, 4), - datetime(2008, 1, 8): datetime(2008, 1, 7), - }, - ) - ) - - apply_cases.append( - ( - -2 * CDay(), - { - datetime(2008, 1, 1): datetime(2007, 12, 28), - datetime(2008, 1, 4): datetime(2008, 1, 2), - datetime(2008, 1, 5): datetime(2008, 1, 3), - datetime(2008, 1, 6): datetime(2008, 1, 3), - datetime(2008, 1, 7): datetime(2008, 1, 3), - datetime(2008, 1, 8): datetime(2008, 1, 4), - datetime(2008, 1, 9): datetime(2008, 1, 7), - }, - ) - ) - - apply_cases.append( - ( - CDay(0), - { - datetime(2008, 1, 1): datetime(2008, 1, 1), - datetime(2008, 1, 4): datetime(2008, 1, 4), - datetime(2008, 1, 5): datetime(2008, 1, 7), - datetime(2008, 1, 6): datetime(2008, 1, 7), - datetime(2008, 1, 7): datetime(2008, 1, 7), - }, - ) - ) - - @pytest.mark.parametrize("case", apply_cases) - def test_apply(self, case): - offset, cases = case - for base, expected in cases.items(): - assert_offset_equal(offset, base, expected) - - def test_apply_large_n(self): - dt = datetime(2012, 10, 23) - - result = dt + CDay(10) - assert result == datetime(2012, 11, 6) - - result = dt + CDay(100) - CDay(100) - assert result == dt - - off = CDay() * 6 - rs = datetime(2012, 1, 1) - off - xp = datetime(2011, 12, 23) - assert rs == xp - - st = datetime(2011, 12, 18) - rs = st + off - xp = datetime(2011, 12, 26) - assert rs == xp - - def test_apply_corner(self): - msg = ( - "Only know how to combine trading day " - "with datetime, datetime64 or timedelta" - ) - with pytest.raises(ApplyTypeError, match=msg): - CDay().apply(BMonthEnd()) - - def test_holidays(self): - # Define a TradingDay offset - holidays = ["2012-05-01", datetime(2013, 5, 1), np.datetime64("2014-05-01")] - tday = CDay(holidays=holidays) - for year in range(2012, 2015): - dt = datetime(year, 4, 30) - xp = datetime(year, 5, 2) - rs = dt + tday - assert rs == xp - - def test_weekmask(self): - weekmask_saudi = "Sat Sun Mon Tue Wed" # Thu-Fri Weekend - weekmask_uae = "1111001" # Fri-Sat Weekend - weekmask_egypt = [1, 1, 1, 1, 0, 0, 1] # Fri-Sat Weekend - bday_saudi = CDay(weekmask=weekmask_saudi) - bday_uae = CDay(weekmask=weekmask_uae) - bday_egypt = CDay(weekmask=weekmask_egypt) - dt = datetime(2013, 5, 1) - xp_saudi = datetime(2013, 5, 4) - xp_uae = datetime(2013, 5, 2) - xp_egypt = datetime(2013, 5, 2) - assert xp_saudi == dt + bday_saudi - assert xp_uae == dt + bday_uae - assert xp_egypt == dt + bday_egypt - xp2 = datetime(2013, 5, 5) - assert xp2 == dt + 2 * bday_saudi - assert xp2 == dt + 2 * bday_uae - assert xp2 == dt + 2 * bday_egypt - - def test_weekmask_and_holidays(self): - weekmask_egypt = "Sun Mon Tue Wed Thu" # Fri-Sat Weekend - holidays = ["2012-05-01", datetime(2013, 5, 1), np.datetime64("2014-05-01")] - bday_egypt = CDay(holidays=holidays, weekmask=weekmask_egypt) - dt = datetime(2013, 4, 30) - xp_egypt = datetime(2013, 5, 5) - assert xp_egypt == dt + 2 * bday_egypt - - @pytest.mark.filterwarnings("ignore:Non:pandas.errors.PerformanceWarning") - def test_calendar(self): - calendar = USFederalHolidayCalendar() - dt = datetime(2014, 1, 17) - assert_offset_equal(CDay(calendar=calendar), dt, datetime(2014, 1, 21)) - - def test_roundtrip_pickle(self): - def _check_roundtrip(obj): - unpickled = tm.round_trip_pickle(obj) - assert unpickled == obj - - _check_roundtrip(self.offset) - _check_roundtrip(self.offset2) - _check_roundtrip(self.offset * 2) - - def test_pickle_compat_0_14_1(self, datapath): - hdays = [datetime(2013, 1, 1) for ele in range(4)] - pth = datapath("tseries", "offsets", "data", "cday-0.14.1.pickle") - cday0_14_1 = read_pickle(pth) - cday = CDay(holidays=hdays) - assert cday == cday0_14_1 - - -class CustomBusinessMonthBase: - def setup_method(self, method): - self.d = datetime(2008, 1, 1) - - self.offset = self._offset() - self.offset1 = self.offset - self.offset2 = self._offset(2) - - def test_eq(self): - assert self.offset2 == self.offset2 - - def test_mul(self): - pass - - def test_hash(self): - assert hash(self.offset2) == hash(self.offset2) - - def test_roundtrip_pickle(self): - def _check_roundtrip(obj): - unpickled = tm.round_trip_pickle(obj) - assert unpickled == obj - - _check_roundtrip(self._offset()) - _check_roundtrip(self._offset(2)) - _check_roundtrip(self._offset() * 2) - - def test_copy(self): - # GH 17452 - off = self._offset(weekmask="Mon Wed Fri") - assert off == off.copy() - - -class TestCustomBusinessMonthEnd(CustomBusinessMonthBase, Base): - _offset = CBMonthEnd - - def test_different_normalize_equals(self): - # GH#21404 changed __eq__ to return False when `normalize` does not match - offset = self._offset() - offset2 = self._offset(normalize=True) - assert offset != offset2 - - def test_repr(self): - assert repr(self.offset) == "" - assert repr(self.offset2) == "<2 * CustomBusinessMonthEnds>" - - def test_call(self): - with tm.assert_produces_warning(FutureWarning): - # GH#34171 DateOffset.__call__ is deprecated - assert self.offset2(self.d) == datetime(2008, 2, 29) - - def testRollback1(self): - assert CDay(10).rollback(datetime(2007, 12, 31)) == datetime(2007, 12, 31) - - def testRollback2(self): - assert CBMonthEnd(10).rollback(self.d) == datetime(2007, 12, 31) - - def testRollforward1(self): - assert CBMonthEnd(10).rollforward(self.d) == datetime(2008, 1, 31) - - def test_roll_date_object(self): - offset = CBMonthEnd() - - dt = date(2012, 9, 15) - - result = offset.rollback(dt) - assert result == datetime(2012, 8, 31) - - result = offset.rollforward(dt) - assert result == datetime(2012, 9, 28) - - offset = offsets.Day() - result = offset.rollback(dt) - assert result == datetime(2012, 9, 15) - - result = offset.rollforward(dt) - assert result == datetime(2012, 9, 15) - - on_offset_cases = [ - (CBMonthEnd(), datetime(2008, 1, 31), True), - (CBMonthEnd(), datetime(2008, 1, 1), False), - ] - - @pytest.mark.parametrize("case", on_offset_cases) - def test_is_on_offset(self, case): - offset, d, expected = case - assert_is_on_offset(offset, d, expected) - - apply_cases: _ApplyCases = [] - apply_cases.append( - ( - CBMonthEnd(), - { - datetime(2008, 1, 1): datetime(2008, 1, 31), - datetime(2008, 2, 7): datetime(2008, 2, 29), - }, - ) - ) - - apply_cases.append( - ( - 2 * CBMonthEnd(), - { - datetime(2008, 1, 1): datetime(2008, 2, 29), - datetime(2008, 2, 7): datetime(2008, 3, 31), - }, - ) - ) - - apply_cases.append( - ( - -CBMonthEnd(), - { - datetime(2008, 1, 1): datetime(2007, 12, 31), - datetime(2008, 2, 8): datetime(2008, 1, 31), - }, - ) - ) - - apply_cases.append( - ( - -2 * CBMonthEnd(), - { - datetime(2008, 1, 1): datetime(2007, 11, 30), - datetime(2008, 2, 9): datetime(2007, 12, 31), - }, - ) - ) - - apply_cases.append( - ( - CBMonthEnd(0), - { - datetime(2008, 1, 1): datetime(2008, 1, 31), - datetime(2008, 2, 7): datetime(2008, 2, 29), - }, - ) - ) - - @pytest.mark.parametrize("case", apply_cases) - def test_apply(self, case): - offset, cases = case - for base, expected in cases.items(): - assert_offset_equal(offset, base, expected) - - def test_apply_large_n(self): - dt = datetime(2012, 10, 23) - - result = dt + CBMonthEnd(10) - assert result == datetime(2013, 7, 31) - - result = dt + CDay(100) - CDay(100) - assert result == dt - - off = CBMonthEnd() * 6 - rs = datetime(2012, 1, 1) - off - xp = datetime(2011, 7, 29) - assert rs == xp - - st = datetime(2011, 12, 18) - rs = st + off - xp = datetime(2012, 5, 31) - assert rs == xp - - def test_holidays(self): - # Define a TradingDay offset - holidays = ["2012-01-31", datetime(2012, 2, 28), np.datetime64("2012-02-29")] - bm_offset = CBMonthEnd(holidays=holidays) - dt = datetime(2012, 1, 1) - assert dt + bm_offset == datetime(2012, 1, 30) - assert dt + 2 * bm_offset == datetime(2012, 2, 27) - - @pytest.mark.filterwarnings("ignore:Non:pandas.errors.PerformanceWarning") - def test_datetimeindex(self): - from pandas.tseries.holiday import USFederalHolidayCalendar - - hcal = USFederalHolidayCalendar() - freq = CBMonthEnd(calendar=hcal) - - assert date_range(start="20120101", end="20130101", freq=freq).tolist()[ - 0 - ] == datetime(2012, 1, 31) - - -class TestCustomBusinessMonthBegin(CustomBusinessMonthBase, Base): - _offset = CBMonthBegin - - def test_different_normalize_equals(self): - # GH#21404 changed __eq__ to return False when `normalize` does not match - offset = self._offset() - offset2 = self._offset(normalize=True) - assert offset != offset2 - - def test_repr(self): - assert repr(self.offset) == "" - assert repr(self.offset2) == "<2 * CustomBusinessMonthBegins>" - - def test_call(self): - with tm.assert_produces_warning(FutureWarning): - # GH#34171 DateOffset.__call__ is deprecated - assert self.offset2(self.d) == datetime(2008, 3, 3) - - def testRollback1(self): - assert CDay(10).rollback(datetime(2007, 12, 31)) == datetime(2007, 12, 31) - - def testRollback2(self): - assert CBMonthBegin(10).rollback(self.d) == datetime(2008, 1, 1) - - def testRollforward1(self): - assert CBMonthBegin(10).rollforward(self.d) == datetime(2008, 1, 1) - - def test_roll_date_object(self): - offset = CBMonthBegin() - - dt = date(2012, 9, 15) - - result = offset.rollback(dt) - assert result == datetime(2012, 9, 3) - - result = offset.rollforward(dt) - assert result == datetime(2012, 10, 1) - - offset = offsets.Day() - result = offset.rollback(dt) - assert result == datetime(2012, 9, 15) - - result = offset.rollforward(dt) - assert result == datetime(2012, 9, 15) - - on_offset_cases = [ - (CBMonthBegin(), datetime(2008, 1, 1), True), - (CBMonthBegin(), datetime(2008, 1, 31), False), - ] - - @pytest.mark.parametrize("case", on_offset_cases) - def test_is_on_offset(self, case): - offset, dt, expected = case - assert_is_on_offset(offset, dt, expected) - - apply_cases: _ApplyCases = [] - apply_cases.append( - ( - CBMonthBegin(), - { - datetime(2008, 1, 1): datetime(2008, 2, 1), - datetime(2008, 2, 7): datetime(2008, 3, 3), - }, - ) - ) - - apply_cases.append( - ( - 2 * CBMonthBegin(), - { - datetime(2008, 1, 1): datetime(2008, 3, 3), - datetime(2008, 2, 7): datetime(2008, 4, 1), - }, - ) - ) - - apply_cases.append( - ( - -CBMonthBegin(), - { - datetime(2008, 1, 1): datetime(2007, 12, 3), - datetime(2008, 2, 8): datetime(2008, 2, 1), - }, - ) - ) - - apply_cases.append( - ( - -2 * CBMonthBegin(), - { - datetime(2008, 1, 1): datetime(2007, 11, 1), - datetime(2008, 2, 9): datetime(2008, 1, 1), - }, - ) - ) - - apply_cases.append( - ( - CBMonthBegin(0), - { - datetime(2008, 1, 1): datetime(2008, 1, 1), - datetime(2008, 1, 7): datetime(2008, 2, 1), - }, - ) - ) - - @pytest.mark.parametrize("case", apply_cases) - def test_apply(self, case): - offset, cases = case - for base, expected in cases.items(): - assert_offset_equal(offset, base, expected) - - def test_apply_large_n(self): - dt = datetime(2012, 10, 23) - - result = dt + CBMonthBegin(10) - assert result == datetime(2013, 8, 1) - - result = dt + CDay(100) - CDay(100) - assert result == dt - - off = CBMonthBegin() * 6 - rs = datetime(2012, 1, 1) - off - xp = datetime(2011, 7, 1) - assert rs == xp - - st = datetime(2011, 12, 18) - rs = st + off - - xp = datetime(2012, 6, 1) - assert rs == xp - - def test_holidays(self): - # Define a TradingDay offset - holidays = ["2012-02-01", datetime(2012, 2, 2), np.datetime64("2012-03-01")] - bm_offset = CBMonthBegin(holidays=holidays) - dt = datetime(2012, 1, 1) - - assert dt + bm_offset == datetime(2012, 1, 2) - assert dt + 2 * bm_offset == datetime(2012, 2, 3) - - @pytest.mark.filterwarnings("ignore:Non:pandas.errors.PerformanceWarning") - def test_datetimeindex(self): - hcal = USFederalHolidayCalendar() - cbmb = CBMonthBegin(calendar=hcal) - assert date_range(start="20120101", end="20130101", freq=cbmb).tolist()[ - 0 - ] == datetime(2012, 1, 3) - - -class TestWeek(Base): - _offset = Week - d = Timestamp(datetime(2008, 1, 2)) - offset1 = _offset() - offset2 = _offset(2) - - def test_repr(self): - assert repr(Week(weekday=0)) == "" - assert repr(Week(n=-1, weekday=0)) == "<-1 * Week: weekday=0>" - assert repr(Week(n=-2, weekday=0)) == "<-2 * Weeks: weekday=0>" - - def test_corner(self): - with pytest.raises(ValueError, match="Day must be"): - Week(weekday=7) - - with pytest.raises(ValueError, match="Day must be"): - Week(weekday=-1) - - def test_is_anchored(self): - assert Week(weekday=0).is_anchored() - assert not Week().is_anchored() - assert not Week(2, weekday=2).is_anchored() - assert not Week(2).is_anchored() - - offset_cases = [] - # not business week - offset_cases.append( - ( - Week(), - { - datetime(2008, 1, 1): datetime(2008, 1, 8), - datetime(2008, 1, 4): datetime(2008, 1, 11), - datetime(2008, 1, 5): datetime(2008, 1, 12), - datetime(2008, 1, 6): datetime(2008, 1, 13), - datetime(2008, 1, 7): datetime(2008, 1, 14), - }, - ) - ) - - # Mon - offset_cases.append( - ( - Week(weekday=0), - { - datetime(2007, 12, 31): datetime(2008, 1, 7), - datetime(2008, 1, 4): datetime(2008, 1, 7), - datetime(2008, 1, 5): datetime(2008, 1, 7), - datetime(2008, 1, 6): datetime(2008, 1, 7), - datetime(2008, 1, 7): datetime(2008, 1, 14), - }, - ) - ) - - # n=0 -> roll forward. Mon - offset_cases.append( - ( - Week(0, weekday=0), - { - datetime(2007, 12, 31): datetime(2007, 12, 31), - datetime(2008, 1, 4): datetime(2008, 1, 7), - datetime(2008, 1, 5): datetime(2008, 1, 7), - datetime(2008, 1, 6): datetime(2008, 1, 7), - datetime(2008, 1, 7): datetime(2008, 1, 7), - }, - ) - ) - - # n=0 -> roll forward. Mon - offset_cases.append( - ( - Week(-2, weekday=1), - { - datetime(2010, 4, 6): datetime(2010, 3, 23), - datetime(2010, 4, 8): datetime(2010, 3, 30), - datetime(2010, 4, 5): datetime(2010, 3, 23), - }, - ) - ) - - @pytest.mark.parametrize("case", offset_cases) - def test_offset(self, case): - offset, cases = case - for base, expected in cases.items(): - assert_offset_equal(offset, base, expected) - - @pytest.mark.parametrize("weekday", range(7)) - def test_is_on_offset(self, weekday): - offset = Week(weekday=weekday) - - for day in range(1, 8): - date = datetime(2008, 1, day) - - if day % 7 == weekday: - expected = True - else: - expected = False - assert_is_on_offset(offset, date, expected) - - -class TestWeekOfMonth(Base): - _offset = WeekOfMonth - offset1 = _offset() - offset2 = _offset(2) - - def test_constructor(self): - with pytest.raises(ValueError, match="^Week"): - WeekOfMonth(n=1, week=4, weekday=0) - - with pytest.raises(ValueError, match="^Week"): - WeekOfMonth(n=1, week=-1, weekday=0) - - with pytest.raises(ValueError, match="^Day"): - WeekOfMonth(n=1, week=0, weekday=-1) - - with pytest.raises(ValueError, match="^Day"): - WeekOfMonth(n=1, week=0, weekday=-7) - - def test_repr(self): - assert ( - repr(WeekOfMonth(weekday=1, week=2)) == "" - ) - - def test_offset(self): - date1 = datetime(2011, 1, 4) # 1st Tuesday of Month - date2 = datetime(2011, 1, 11) # 2nd Tuesday of Month - date3 = datetime(2011, 1, 18) # 3rd Tuesday of Month - date4 = datetime(2011, 1, 25) # 4th Tuesday of Month - - # see for loop for structure - test_cases = [ - (-2, 2, 1, date1, datetime(2010, 11, 16)), - (-2, 2, 1, date2, datetime(2010, 11, 16)), - (-2, 2, 1, date3, datetime(2010, 11, 16)), - (-2, 2, 1, date4, datetime(2010, 12, 21)), - (-1, 2, 1, date1, datetime(2010, 12, 21)), - (-1, 2, 1, date2, datetime(2010, 12, 21)), - (-1, 2, 1, date3, datetime(2010, 12, 21)), - (-1, 2, 1, date4, datetime(2011, 1, 18)), - (0, 0, 1, date1, datetime(2011, 1, 4)), - (0, 0, 1, date2, datetime(2011, 2, 1)), - (0, 0, 1, date3, datetime(2011, 2, 1)), - (0, 0, 1, date4, datetime(2011, 2, 1)), - (0, 1, 1, date1, datetime(2011, 1, 11)), - (0, 1, 1, date2, datetime(2011, 1, 11)), - (0, 1, 1, date3, datetime(2011, 2, 8)), - (0, 1, 1, date4, datetime(2011, 2, 8)), - (0, 0, 1, date1, datetime(2011, 1, 4)), - (0, 1, 1, date2, datetime(2011, 1, 11)), - (0, 2, 1, date3, datetime(2011, 1, 18)), - (0, 3, 1, date4, datetime(2011, 1, 25)), - (1, 0, 0, date1, datetime(2011, 2, 7)), - (1, 0, 0, date2, datetime(2011, 2, 7)), - (1, 0, 0, date3, datetime(2011, 2, 7)), - (1, 0, 0, date4, datetime(2011, 2, 7)), - (1, 0, 1, date1, datetime(2011, 2, 1)), - (1, 0, 1, date2, datetime(2011, 2, 1)), - (1, 0, 1, date3, datetime(2011, 2, 1)), - (1, 0, 1, date4, datetime(2011, 2, 1)), - (1, 0, 2, date1, datetime(2011, 1, 5)), - (1, 0, 2, date2, datetime(2011, 2, 2)), - (1, 0, 2, date3, datetime(2011, 2, 2)), - (1, 0, 2, date4, datetime(2011, 2, 2)), - (1, 2, 1, date1, datetime(2011, 1, 18)), - (1, 2, 1, date2, datetime(2011, 1, 18)), - (1, 2, 1, date3, datetime(2011, 2, 15)), - (1, 2, 1, date4, datetime(2011, 2, 15)), - (2, 2, 1, date1, datetime(2011, 2, 15)), - (2, 2, 1, date2, datetime(2011, 2, 15)), - (2, 2, 1, date3, datetime(2011, 3, 15)), - (2, 2, 1, date4, datetime(2011, 3, 15)), - ] - - for n, week, weekday, dt, expected in test_cases: - offset = WeekOfMonth(n, week=week, weekday=weekday) - assert_offset_equal(offset, dt, expected) - - # try subtracting - result = datetime(2011, 2, 1) - WeekOfMonth(week=1, weekday=2) - assert result == datetime(2011, 1, 12) - - result = datetime(2011, 2, 3) - WeekOfMonth(week=0, weekday=2) - assert result == datetime(2011, 2, 2) - - on_offset_cases = [ - (0, 0, datetime(2011, 2, 7), True), - (0, 0, datetime(2011, 2, 6), False), - (0, 0, datetime(2011, 2, 14), False), - (1, 0, datetime(2011, 2, 14), True), - (0, 1, datetime(2011, 2, 1), True), - (0, 1, datetime(2011, 2, 8), False), - ] - - @pytest.mark.parametrize("case", on_offset_cases) - def test_is_on_offset(self, case): - week, weekday, dt, expected = case - offset = WeekOfMonth(week=week, weekday=weekday) - assert offset.is_on_offset(dt) == expected - - -class TestLastWeekOfMonth(Base): - _offset = LastWeekOfMonth - offset1 = _offset() - offset2 = _offset(2) - - def test_constructor(self): - with pytest.raises(ValueError, match="^N cannot be 0"): - LastWeekOfMonth(n=0, weekday=1) - - with pytest.raises(ValueError, match="^Day"): - LastWeekOfMonth(n=1, weekday=-1) - - with pytest.raises(ValueError, match="^Day"): - LastWeekOfMonth(n=1, weekday=7) - - def test_offset(self): - # Saturday - last_sat = datetime(2013, 8, 31) - next_sat = datetime(2013, 9, 28) - offset_sat = LastWeekOfMonth(n=1, weekday=5) - - one_day_before = last_sat + timedelta(days=-1) - assert one_day_before + offset_sat == last_sat - - one_day_after = last_sat + timedelta(days=+1) - assert one_day_after + offset_sat == next_sat - - # Test On that day - assert last_sat + offset_sat == next_sat - - # Thursday - - offset_thur = LastWeekOfMonth(n=1, weekday=3) - last_thurs = datetime(2013, 1, 31) - next_thurs = datetime(2013, 2, 28) - - one_day_before = last_thurs + timedelta(days=-1) - assert one_day_before + offset_thur == last_thurs - - one_day_after = last_thurs + timedelta(days=+1) - assert one_day_after + offset_thur == next_thurs - - # Test on that day - assert last_thurs + offset_thur == next_thurs - - three_before = last_thurs + timedelta(days=-3) - assert three_before + offset_thur == last_thurs - - two_after = last_thurs + timedelta(days=+2) - assert two_after + offset_thur == next_thurs - - offset_sunday = LastWeekOfMonth(n=1, weekday=WeekDay.SUN) - assert datetime(2013, 7, 31) + offset_sunday == datetime(2013, 8, 25) - - on_offset_cases = [ - (WeekDay.SUN, datetime(2013, 1, 27), True), - (WeekDay.SAT, datetime(2013, 3, 30), True), - (WeekDay.MON, datetime(2013, 2, 18), False), # Not the last Mon - (WeekDay.SUN, datetime(2013, 2, 25), False), # Not a SUN - (WeekDay.MON, datetime(2013, 2, 25), True), - (WeekDay.SAT, datetime(2013, 11, 30), True), - (WeekDay.SAT, datetime(2006, 8, 26), True), - (WeekDay.SAT, datetime(2007, 8, 25), True), - (WeekDay.SAT, datetime(2008, 8, 30), True), - (WeekDay.SAT, datetime(2009, 8, 29), True), - (WeekDay.SAT, datetime(2010, 8, 28), True), - (WeekDay.SAT, datetime(2011, 8, 27), True), - (WeekDay.SAT, datetime(2019, 8, 31), True), - ] - - @pytest.mark.parametrize("case", on_offset_cases) - def test_is_on_offset(self, case): - weekday, dt, expected = case - offset = LastWeekOfMonth(weekday=weekday) - assert offset.is_on_offset(dt) == expected - - def test_repr(self): - assert ( - repr(LastWeekOfMonth(n=2, weekday=1)) == "<2 * LastWeekOfMonths: weekday=1>" - ) - - -class TestSemiMonthEnd(Base): - _offset = SemiMonthEnd - offset1 = _offset() - offset2 = _offset(2) - - def test_offset_whole_year(self): - dates = ( - datetime(2007, 12, 31), - datetime(2008, 1, 15), - datetime(2008, 1, 31), - datetime(2008, 2, 15), - datetime(2008, 2, 29), - datetime(2008, 3, 15), - datetime(2008, 3, 31), - datetime(2008, 4, 15), - datetime(2008, 4, 30), - datetime(2008, 5, 15), - datetime(2008, 5, 31), - datetime(2008, 6, 15), - datetime(2008, 6, 30), - datetime(2008, 7, 15), - datetime(2008, 7, 31), - datetime(2008, 8, 15), - datetime(2008, 8, 31), - datetime(2008, 9, 15), - datetime(2008, 9, 30), - datetime(2008, 10, 15), - datetime(2008, 10, 31), - datetime(2008, 11, 15), - datetime(2008, 11, 30), - datetime(2008, 12, 15), - datetime(2008, 12, 31), - ) - - for base, exp_date in zip(dates[:-1], dates[1:]): - assert_offset_equal(SemiMonthEnd(), base, exp_date) - - # ensure .apply_index works as expected - s = DatetimeIndex(dates[:-1]) - with tm.assert_produces_warning(None): - # GH#22535 check that we don't get a FutureWarning from adding - # an integer array to PeriodIndex - result = SemiMonthEnd() + s - - exp = DatetimeIndex(dates[1:]) - tm.assert_index_equal(result, exp) - - # ensure generating a range with DatetimeIndex gives same result - result = date_range(start=dates[0], end=dates[-1], freq="SM") - exp = DatetimeIndex(dates, freq="SM") - tm.assert_index_equal(result, exp) - - offset_cases = [] - offset_cases.append( - ( - SemiMonthEnd(), - { - datetime(2008, 1, 1): datetime(2008, 1, 15), - datetime(2008, 1, 15): datetime(2008, 1, 31), - datetime(2008, 1, 31): datetime(2008, 2, 15), - datetime(2006, 12, 14): datetime(2006, 12, 15), - datetime(2006, 12, 29): datetime(2006, 12, 31), - datetime(2006, 12, 31): datetime(2007, 1, 15), - datetime(2007, 1, 1): datetime(2007, 1, 15), - datetime(2006, 12, 1): datetime(2006, 12, 15), - datetime(2006, 12, 15): datetime(2006, 12, 31), - }, - ) - ) - - offset_cases.append( - ( - SemiMonthEnd(day_of_month=20), - { - datetime(2008, 1, 1): datetime(2008, 1, 20), - datetime(2008, 1, 15): datetime(2008, 1, 20), - datetime(2008, 1, 21): datetime(2008, 1, 31), - datetime(2008, 1, 31): datetime(2008, 2, 20), - datetime(2006, 12, 14): datetime(2006, 12, 20), - datetime(2006, 12, 29): datetime(2006, 12, 31), - datetime(2006, 12, 31): datetime(2007, 1, 20), - datetime(2007, 1, 1): datetime(2007, 1, 20), - datetime(2006, 12, 1): datetime(2006, 12, 20), - datetime(2006, 12, 15): datetime(2006, 12, 20), - }, - ) - ) - - offset_cases.append( - ( - SemiMonthEnd(0), - { - datetime(2008, 1, 1): datetime(2008, 1, 15), - datetime(2008, 1, 16): datetime(2008, 1, 31), - datetime(2008, 1, 15): datetime(2008, 1, 15), - datetime(2008, 1, 31): datetime(2008, 1, 31), - datetime(2006, 12, 29): datetime(2006, 12, 31), - datetime(2006, 12, 31): datetime(2006, 12, 31), - datetime(2007, 1, 1): datetime(2007, 1, 15), - }, - ) - ) - - offset_cases.append( - ( - SemiMonthEnd(0, day_of_month=16), - { - datetime(2008, 1, 1): datetime(2008, 1, 16), - datetime(2008, 1, 16): datetime(2008, 1, 16), - datetime(2008, 1, 15): datetime(2008, 1, 16), - datetime(2008, 1, 31): datetime(2008, 1, 31), - datetime(2006, 12, 29): datetime(2006, 12, 31), - datetime(2006, 12, 31): datetime(2006, 12, 31), - datetime(2007, 1, 1): datetime(2007, 1, 16), - }, - ) - ) - - offset_cases.append( - ( - SemiMonthEnd(2), - { - datetime(2008, 1, 1): datetime(2008, 1, 31), - datetime(2008, 1, 31): datetime(2008, 2, 29), - datetime(2006, 12, 29): datetime(2007, 1, 15), - datetime(2006, 12, 31): datetime(2007, 1, 31), - datetime(2007, 1, 1): datetime(2007, 1, 31), - datetime(2007, 1, 16): datetime(2007, 2, 15), - datetime(2006, 11, 1): datetime(2006, 11, 30), - }, - ) - ) - - offset_cases.append( - ( - SemiMonthEnd(-1), - { - datetime(2007, 1, 1): datetime(2006, 12, 31), - datetime(2008, 6, 30): datetime(2008, 6, 15), - datetime(2008, 12, 31): datetime(2008, 12, 15), - datetime(2006, 12, 29): datetime(2006, 12, 15), - datetime(2006, 12, 30): datetime(2006, 12, 15), - datetime(2007, 1, 1): datetime(2006, 12, 31), - }, - ) - ) - - offset_cases.append( - ( - SemiMonthEnd(-1, day_of_month=4), - { - datetime(2007, 1, 1): datetime(2006, 12, 31), - datetime(2007, 1, 4): datetime(2006, 12, 31), - datetime(2008, 6, 30): datetime(2008, 6, 4), - datetime(2008, 12, 31): datetime(2008, 12, 4), - datetime(2006, 12, 5): datetime(2006, 12, 4), - datetime(2006, 12, 30): datetime(2006, 12, 4), - datetime(2007, 1, 1): datetime(2006, 12, 31), - }, - ) - ) - - offset_cases.append( - ( - SemiMonthEnd(-2), - { - datetime(2007, 1, 1): datetime(2006, 12, 15), - datetime(2008, 6, 30): datetime(2008, 5, 31), - datetime(2008, 3, 15): datetime(2008, 2, 15), - datetime(2008, 12, 31): datetime(2008, 11, 30), - datetime(2006, 12, 29): datetime(2006, 11, 30), - datetime(2006, 12, 14): datetime(2006, 11, 15), - datetime(2007, 1, 1): datetime(2006, 12, 15), - }, - ) - ) - - @pytest.mark.parametrize("case", offset_cases) - def test_offset(self, case): - offset, cases = case - for base, expected in cases.items(): - assert_offset_equal(offset, base, expected) - - @pytest.mark.parametrize("case", offset_cases) - def test_apply_index(self, case): - # https://github.com/pandas-dev/pandas/issues/34580 - offset, cases = case - s = DatetimeIndex(cases.keys()) - exp = DatetimeIndex(cases.values()) - - with tm.assert_produces_warning(None): - # GH#22535 check that we don't get a FutureWarning from adding - # an integer array to PeriodIndex - result = offset + s - tm.assert_index_equal(result, exp) - - with tm.assert_produces_warning(FutureWarning): - result = offset.apply_index(s) - tm.assert_index_equal(result, exp) - - on_offset_cases = [ - (datetime(2007, 12, 31), True), - (datetime(2007, 12, 15), True), - (datetime(2007, 12, 14), False), - (datetime(2007, 12, 1), False), - (datetime(2008, 2, 29), True), - ] - - @pytest.mark.parametrize("case", on_offset_cases) - def test_is_on_offset(self, case): - dt, expected = case - assert_is_on_offset(SemiMonthEnd(), dt, expected) - - @pytest.mark.parametrize("klass", [Series, DatetimeIndex]) - def test_vectorized_offset_addition(self, klass): - s = klass( - [ - Timestamp("2000-01-15 00:15:00", tz="US/Central"), - Timestamp("2000-02-15", tz="US/Central"), - ], - name="a", - ) - - with tm.assert_produces_warning(None): - # GH#22535 check that we don't get a FutureWarning from adding - # an integer array to PeriodIndex - result = s + SemiMonthEnd() - result2 = SemiMonthEnd() + s - - exp = klass( - [ - Timestamp("2000-01-31 00:15:00", tz="US/Central"), - Timestamp("2000-02-29", tz="US/Central"), - ], - name="a", - ) - tm.assert_equal(result, exp) - tm.assert_equal(result2, exp) - - s = klass( - [ - Timestamp("2000-01-01 00:15:00", tz="US/Central"), - Timestamp("2000-02-01", tz="US/Central"), - ], - name="a", - ) - - with tm.assert_produces_warning(None): - # GH#22535 check that we don't get a FutureWarning from adding - # an integer array to PeriodIndex - result = s + SemiMonthEnd() - result2 = SemiMonthEnd() + s - - exp = klass( - [ - Timestamp("2000-01-15 00:15:00", tz="US/Central"), - Timestamp("2000-02-15", tz="US/Central"), - ], - name="a", - ) - tm.assert_equal(result, exp) - tm.assert_equal(result2, exp) - - -class TestSemiMonthBegin(Base): - _offset = SemiMonthBegin - offset1 = _offset() - offset2 = _offset(2) - - def test_offset_whole_year(self): - dates = ( - datetime(2007, 12, 15), - datetime(2008, 1, 1), - datetime(2008, 1, 15), - datetime(2008, 2, 1), - datetime(2008, 2, 15), - datetime(2008, 3, 1), - datetime(2008, 3, 15), - datetime(2008, 4, 1), - datetime(2008, 4, 15), - datetime(2008, 5, 1), - datetime(2008, 5, 15), - datetime(2008, 6, 1), - datetime(2008, 6, 15), - datetime(2008, 7, 1), - datetime(2008, 7, 15), - datetime(2008, 8, 1), - datetime(2008, 8, 15), - datetime(2008, 9, 1), - datetime(2008, 9, 15), - datetime(2008, 10, 1), - datetime(2008, 10, 15), - datetime(2008, 11, 1), - datetime(2008, 11, 15), - datetime(2008, 12, 1), - datetime(2008, 12, 15), - ) - - for base, exp_date in zip(dates[:-1], dates[1:]): - assert_offset_equal(SemiMonthBegin(), base, exp_date) - - # ensure .apply_index works as expected - s = DatetimeIndex(dates[:-1]) - with tm.assert_produces_warning(None): - # GH#22535 check that we don't get a FutureWarning from adding - # an integer array to PeriodIndex - result = SemiMonthBegin() + s - - exp = DatetimeIndex(dates[1:]) - tm.assert_index_equal(result, exp) - - # ensure generating a range with DatetimeIndex gives same result - result = date_range(start=dates[0], end=dates[-1], freq="SMS") - exp = DatetimeIndex(dates, freq="SMS") - tm.assert_index_equal(result, exp) - - offset_cases = [] - offset_cases.append( - ( - SemiMonthBegin(), - { - datetime(2008, 1, 1): datetime(2008, 1, 15), - datetime(2008, 1, 15): datetime(2008, 2, 1), - datetime(2008, 1, 31): datetime(2008, 2, 1), - datetime(2006, 12, 14): datetime(2006, 12, 15), - datetime(2006, 12, 29): datetime(2007, 1, 1), - datetime(2006, 12, 31): datetime(2007, 1, 1), - datetime(2007, 1, 1): datetime(2007, 1, 15), - datetime(2006, 12, 1): datetime(2006, 12, 15), - datetime(2006, 12, 15): datetime(2007, 1, 1), - }, - ) - ) - - offset_cases.append( - ( - SemiMonthBegin(day_of_month=20), - { - datetime(2008, 1, 1): datetime(2008, 1, 20), - datetime(2008, 1, 15): datetime(2008, 1, 20), - datetime(2008, 1, 21): datetime(2008, 2, 1), - datetime(2008, 1, 31): datetime(2008, 2, 1), - datetime(2006, 12, 14): datetime(2006, 12, 20), - datetime(2006, 12, 29): datetime(2007, 1, 1), - datetime(2006, 12, 31): datetime(2007, 1, 1), - datetime(2007, 1, 1): datetime(2007, 1, 20), - datetime(2006, 12, 1): datetime(2006, 12, 20), - datetime(2006, 12, 15): datetime(2006, 12, 20), - }, - ) - ) - - offset_cases.append( - ( - SemiMonthBegin(0), - { - datetime(2008, 1, 1): datetime(2008, 1, 1), - datetime(2008, 1, 16): datetime(2008, 2, 1), - datetime(2008, 1, 15): datetime(2008, 1, 15), - datetime(2008, 1, 31): datetime(2008, 2, 1), - datetime(2006, 12, 29): datetime(2007, 1, 1), - datetime(2006, 12, 2): datetime(2006, 12, 15), - datetime(2007, 1, 1): datetime(2007, 1, 1), - }, - ) - ) - - offset_cases.append( - ( - SemiMonthBegin(0, day_of_month=16), - { - datetime(2008, 1, 1): datetime(2008, 1, 1), - datetime(2008, 1, 16): datetime(2008, 1, 16), - datetime(2008, 1, 15): datetime(2008, 1, 16), - datetime(2008, 1, 31): datetime(2008, 2, 1), - datetime(2006, 12, 29): datetime(2007, 1, 1), - datetime(2006, 12, 31): datetime(2007, 1, 1), - datetime(2007, 1, 5): datetime(2007, 1, 16), - datetime(2007, 1, 1): datetime(2007, 1, 1), - }, - ) - ) - - offset_cases.append( - ( - SemiMonthBegin(2), - { - datetime(2008, 1, 1): datetime(2008, 2, 1), - datetime(2008, 1, 31): datetime(2008, 2, 15), - datetime(2006, 12, 1): datetime(2007, 1, 1), - datetime(2006, 12, 29): datetime(2007, 1, 15), - datetime(2006, 12, 15): datetime(2007, 1, 15), - datetime(2007, 1, 1): datetime(2007, 2, 1), - datetime(2007, 1, 16): datetime(2007, 2, 15), - datetime(2006, 11, 1): datetime(2006, 12, 1), - }, - ) - ) - - offset_cases.append( - ( - SemiMonthBegin(-1), - { - datetime(2007, 1, 1): datetime(2006, 12, 15), - datetime(2008, 6, 30): datetime(2008, 6, 15), - datetime(2008, 6, 14): datetime(2008, 6, 1), - datetime(2008, 12, 31): datetime(2008, 12, 15), - datetime(2006, 12, 29): datetime(2006, 12, 15), - datetime(2006, 12, 15): datetime(2006, 12, 1), - datetime(2007, 1, 1): datetime(2006, 12, 15), - }, - ) - ) - - offset_cases.append( - ( - SemiMonthBegin(-1, day_of_month=4), - { - datetime(2007, 1, 1): datetime(2006, 12, 4), - datetime(2007, 1, 4): datetime(2007, 1, 1), - datetime(2008, 6, 30): datetime(2008, 6, 4), - datetime(2008, 12, 31): datetime(2008, 12, 4), - datetime(2006, 12, 5): datetime(2006, 12, 4), - datetime(2006, 12, 30): datetime(2006, 12, 4), - datetime(2006, 12, 2): datetime(2006, 12, 1), - datetime(2007, 1, 1): datetime(2006, 12, 4), - }, - ) - ) - - offset_cases.append( - ( - SemiMonthBegin(-2), - { - datetime(2007, 1, 1): datetime(2006, 12, 1), - datetime(2008, 6, 30): datetime(2008, 6, 1), - datetime(2008, 6, 14): datetime(2008, 5, 15), - datetime(2008, 12, 31): datetime(2008, 12, 1), - datetime(2006, 12, 29): datetime(2006, 12, 1), - datetime(2006, 12, 15): datetime(2006, 11, 15), - datetime(2007, 1, 1): datetime(2006, 12, 1), - }, - ) - ) - - @pytest.mark.parametrize("case", offset_cases) - def test_offset(self, case): - offset, cases = case - for base, expected in cases.items(): - assert_offset_equal(offset, base, expected) - - @pytest.mark.parametrize("case", offset_cases) - def test_apply_index(self, case): - offset, cases = case - s = DatetimeIndex(cases.keys()) - - with tm.assert_produces_warning(None): - # GH#22535 check that we don't get a FutureWarning from adding - # an integer array to PeriodIndex - result = offset + s - - exp = DatetimeIndex(cases.values()) - tm.assert_index_equal(result, exp) - - on_offset_cases = [ - (datetime(2007, 12, 1), True), - (datetime(2007, 12, 15), True), - (datetime(2007, 12, 14), False), - (datetime(2007, 12, 31), False), - (datetime(2008, 2, 15), True), - ] - - @pytest.mark.parametrize("case", on_offset_cases) - def test_is_on_offset(self, case): - dt, expected = case - assert_is_on_offset(SemiMonthBegin(), dt, expected) - - @pytest.mark.parametrize("klass", [Series, DatetimeIndex]) - def test_vectorized_offset_addition(self, klass): - s = klass( - [ - Timestamp("2000-01-15 00:15:00", tz="US/Central"), - Timestamp("2000-02-15", tz="US/Central"), - ], - name="a", - ) - with tm.assert_produces_warning(None): - # GH#22535 check that we don't get a FutureWarning from adding - # an integer array to PeriodIndex - result = s + SemiMonthBegin() - result2 = SemiMonthBegin() + s - - exp = klass( - [ - Timestamp("2000-02-01 00:15:00", tz="US/Central"), - Timestamp("2000-03-01", tz="US/Central"), - ], - name="a", - ) - tm.assert_equal(result, exp) - tm.assert_equal(result2, exp) - - s = klass( - [ - Timestamp("2000-01-01 00:15:00", tz="US/Central"), - Timestamp("2000-02-01", tz="US/Central"), - ], - name="a", - ) - with tm.assert_produces_warning(None): - # GH#22535 check that we don't get a FutureWarning from adding - # an integer array to PeriodIndex - result = s + SemiMonthBegin() - result2 = SemiMonthBegin() + s - - exp = klass( - [ - Timestamp("2000-01-15 00:15:00", tz="US/Central"), - Timestamp("2000-02-15", tz="US/Central"), - ], - name="a", - ) - tm.assert_equal(result, exp) - tm.assert_equal(result2, exp) - - def test_Easter(): assert_offset_equal(Easter(), datetime(2010, 1, 1), datetime(2010, 4, 4)) assert_offset_equal(Easter(), datetime(2010, 4, 5), datetime(2011, 4, 24)) @@ -4208,153 +707,6 @@ def get_utc_offset_hours(ts): return (o.days * 24 * 3600 + o.seconds) / 3600.0 -class TestDST: - """ - test DateOffset additions over Daylight Savings Time - """ - - # one microsecond before the DST transition - ts_pre_fallback = "2013-11-03 01:59:59.999999" - ts_pre_springfwd = "2013-03-10 01:59:59.999999" - - # test both basic names and dateutil timezones - timezone_utc_offsets = { - "US/Eastern": {"utc_offset_daylight": -4, "utc_offset_standard": -5}, - "dateutil/US/Pacific": {"utc_offset_daylight": -7, "utc_offset_standard": -8}, - } - valid_date_offsets_singular = [ - "weekday", - "day", - "hour", - "minute", - "second", - "microsecond", - ] - valid_date_offsets_plural = [ - "weeks", - "days", - "hours", - "minutes", - "seconds", - "milliseconds", - "microseconds", - ] - - def _test_all_offsets(self, n, **kwds): - valid_offsets = ( - self.valid_date_offsets_plural - if n > 1 - else self.valid_date_offsets_singular - ) - - for name in valid_offsets: - self._test_offset(offset_name=name, offset_n=n, **kwds) - - def _test_offset(self, offset_name, offset_n, tstart, expected_utc_offset): - offset = DateOffset(**{offset_name: offset_n}) - - t = tstart + offset - if expected_utc_offset is not None: - assert get_utc_offset_hours(t) == expected_utc_offset - - if offset_name == "weeks": - # dates should match - assert t.date() == timedelta(days=7 * offset.kwds["weeks"]) + tstart.date() - # expect the same day of week, hour of day, minute, second, ... - assert ( - t.dayofweek == tstart.dayofweek - and t.hour == tstart.hour - and t.minute == tstart.minute - and t.second == tstart.second - ) - elif offset_name == "days": - # dates should match - assert timedelta(offset.kwds["days"]) + tstart.date() == t.date() - # expect the same hour of day, minute, second, ... - assert ( - t.hour == tstart.hour - and t.minute == tstart.minute - and t.second == tstart.second - ) - elif offset_name in self.valid_date_offsets_singular: - # expect the singular offset value to match between tstart and t - datepart_offset = getattr( - t, offset_name if offset_name != "weekday" else "dayofweek" - ) - assert datepart_offset == offset.kwds[offset_name] - else: - # the offset should be the same as if it was done in UTC - assert t == (tstart.tz_convert("UTC") + offset).tz_convert("US/Pacific") - - def _make_timestamp(self, string, hrs_offset, tz): - if hrs_offset >= 0: - offset_string = f"{hrs_offset:02d}00" - else: - offset_string = f"-{(hrs_offset * -1):02}00" - return Timestamp(string + offset_string).tz_convert(tz) - - def test_springforward_plural(self): - # test moving from standard to daylight savings - for tz, utc_offsets in self.timezone_utc_offsets.items(): - hrs_pre = utc_offsets["utc_offset_standard"] - hrs_post = utc_offsets["utc_offset_daylight"] - self._test_all_offsets( - n=3, - tstart=self._make_timestamp(self.ts_pre_springfwd, hrs_pre, tz), - expected_utc_offset=hrs_post, - ) - - def test_fallback_singular(self): - # in the case of singular offsets, we don't necessarily know which utc - # offset the new Timestamp will wind up in (the tz for 1 month may be - # different from 1 second) so we don't specify an expected_utc_offset - for tz, utc_offsets in self.timezone_utc_offsets.items(): - hrs_pre = utc_offsets["utc_offset_standard"] - self._test_all_offsets( - n=1, - tstart=self._make_timestamp(self.ts_pre_fallback, hrs_pre, tz), - expected_utc_offset=None, - ) - - def test_springforward_singular(self): - for tz, utc_offsets in self.timezone_utc_offsets.items(): - hrs_pre = utc_offsets["utc_offset_standard"] - self._test_all_offsets( - n=1, - tstart=self._make_timestamp(self.ts_pre_springfwd, hrs_pre, tz), - expected_utc_offset=None, - ) - - offset_classes = { - MonthBegin: ["11/2/2012", "12/1/2012"], - MonthEnd: ["11/2/2012", "11/30/2012"], - BMonthBegin: ["11/2/2012", "12/3/2012"], - BMonthEnd: ["11/2/2012", "11/30/2012"], - CBMonthBegin: ["11/2/2012", "12/3/2012"], - CBMonthEnd: ["11/2/2012", "11/30/2012"], - SemiMonthBegin: ["11/2/2012", "11/15/2012"], - SemiMonthEnd: ["11/2/2012", "11/15/2012"], - Week: ["11/2/2012", "11/9/2012"], - YearBegin: ["11/2/2012", "1/1/2013"], - YearEnd: ["11/2/2012", "12/31/2012"], - BYearBegin: ["11/2/2012", "1/1/2013"], - BYearEnd: ["11/2/2012", "12/31/2012"], - QuarterBegin: ["11/2/2012", "12/1/2012"], - QuarterEnd: ["11/2/2012", "12/31/2012"], - BQuarterBegin: ["11/2/2012", "12/3/2012"], - BQuarterEnd: ["11/2/2012", "12/31/2012"], - Day: ["11/4/2012", "11/4/2012 23:00"], - }.items() - - @pytest.mark.parametrize("tup", offset_classes) - def test_all_offset_classes(self, tup): - offset, test_values = tup - - first = Timestamp(test_values[0], tz="US/Eastern") + offset() - second = Timestamp(test_values[1], tz="US/Eastern") - assert first == second - - # --------------------------------------------------------------------- diff --git a/pandas/tests/tseries/offsets/test_opening_times.py b/pandas/tests/tseries/offsets/test_opening_times.py new file mode 100644 index 0000000000000..b8d1646e59b63 --- /dev/null +++ b/pandas/tests/tseries/offsets/test_opening_times.py @@ -0,0 +1,460 @@ +""" +Test offset.BusinessHour._next_opening_time and offset.BusinessHour._prev_opening_time +""" +from datetime import datetime + +import pytest + +from pandas._libs.tslibs.offsets import BusinessHour + +from pandas.tests.tseries.offsets.common import Base + + +class TestOpeningTimes(Base): + _offset = BusinessHour + + # opening time should be affected by sign of n, not by n's value and end + opening_time_cases = [ + ( + [ + BusinessHour(), + BusinessHour(n=2), + BusinessHour(n=4), + BusinessHour(end="10:00"), + BusinessHour(n=2, end="4:00"), + BusinessHour(n=4, end="15:00"), + ], + { + datetime(2014, 7, 1, 11): ( + datetime(2014, 7, 2, 9), + datetime(2014, 7, 1, 9), + ), + datetime(2014, 7, 1, 18): ( + datetime(2014, 7, 2, 9), + datetime(2014, 7, 1, 9), + ), + datetime(2014, 7, 1, 23): ( + datetime(2014, 7, 2, 9), + datetime(2014, 7, 1, 9), + ), + datetime(2014, 7, 2, 8): ( + datetime(2014, 7, 2, 9), + datetime(2014, 7, 1, 9), + ), + # if timestamp is on opening time, next opening time is + # as it is + datetime(2014, 7, 2, 9): ( + datetime(2014, 7, 2, 9), + datetime(2014, 7, 2, 9), + ), + datetime(2014, 7, 2, 10): ( + datetime(2014, 7, 3, 9), + datetime(2014, 7, 2, 9), + ), + # 2014-07-05 is saturday + datetime(2014, 7, 5, 10): ( + datetime(2014, 7, 7, 9), + datetime(2014, 7, 4, 9), + ), + datetime(2014, 7, 4, 10): ( + datetime(2014, 7, 7, 9), + datetime(2014, 7, 4, 9), + ), + datetime(2014, 7, 4, 23): ( + datetime(2014, 7, 7, 9), + datetime(2014, 7, 4, 9), + ), + datetime(2014, 7, 6, 10): ( + datetime(2014, 7, 7, 9), + datetime(2014, 7, 4, 9), + ), + datetime(2014, 7, 7, 5): ( + datetime(2014, 7, 7, 9), + datetime(2014, 7, 4, 9), + ), + datetime(2014, 7, 7, 9, 1): ( + datetime(2014, 7, 8, 9), + datetime(2014, 7, 7, 9), + ), + }, + ), + ( + [ + BusinessHour(start="11:15"), + BusinessHour(n=2, start="11:15"), + BusinessHour(n=3, start="11:15"), + BusinessHour(start="11:15", end="10:00"), + BusinessHour(n=2, start="11:15", end="4:00"), + BusinessHour(n=3, start="11:15", end="15:00"), + ], + { + datetime(2014, 7, 1, 11): ( + datetime(2014, 7, 1, 11, 15), + datetime(2014, 6, 30, 11, 15), + ), + datetime(2014, 7, 1, 18): ( + datetime(2014, 7, 2, 11, 15), + datetime(2014, 7, 1, 11, 15), + ), + datetime(2014, 7, 1, 23): ( + datetime(2014, 7, 2, 11, 15), + datetime(2014, 7, 1, 11, 15), + ), + datetime(2014, 7, 2, 8): ( + datetime(2014, 7, 2, 11, 15), + datetime(2014, 7, 1, 11, 15), + ), + datetime(2014, 7, 2, 9): ( + datetime(2014, 7, 2, 11, 15), + datetime(2014, 7, 1, 11, 15), + ), + datetime(2014, 7, 2, 10): ( + datetime(2014, 7, 2, 11, 15), + datetime(2014, 7, 1, 11, 15), + ), + datetime(2014, 7, 2, 11, 15): ( + datetime(2014, 7, 2, 11, 15), + datetime(2014, 7, 2, 11, 15), + ), + datetime(2014, 7, 2, 11, 15, 1): ( + datetime(2014, 7, 3, 11, 15), + datetime(2014, 7, 2, 11, 15), + ), + datetime(2014, 7, 5, 10): ( + datetime(2014, 7, 7, 11, 15), + datetime(2014, 7, 4, 11, 15), + ), + datetime(2014, 7, 4, 10): ( + datetime(2014, 7, 4, 11, 15), + datetime(2014, 7, 3, 11, 15), + ), + datetime(2014, 7, 4, 23): ( + datetime(2014, 7, 7, 11, 15), + datetime(2014, 7, 4, 11, 15), + ), + datetime(2014, 7, 6, 10): ( + datetime(2014, 7, 7, 11, 15), + datetime(2014, 7, 4, 11, 15), + ), + datetime(2014, 7, 7, 5): ( + datetime(2014, 7, 7, 11, 15), + datetime(2014, 7, 4, 11, 15), + ), + datetime(2014, 7, 7, 9, 1): ( + datetime(2014, 7, 7, 11, 15), + datetime(2014, 7, 4, 11, 15), + ), + }, + ), + ( + [ + BusinessHour(-1), + BusinessHour(n=-2), + BusinessHour(n=-4), + BusinessHour(n=-1, end="10:00"), + BusinessHour(n=-2, end="4:00"), + BusinessHour(n=-4, end="15:00"), + ], + { + datetime(2014, 7, 1, 11): ( + datetime(2014, 7, 1, 9), + datetime(2014, 7, 2, 9), + ), + datetime(2014, 7, 1, 18): ( + datetime(2014, 7, 1, 9), + datetime(2014, 7, 2, 9), + ), + datetime(2014, 7, 1, 23): ( + datetime(2014, 7, 1, 9), + datetime(2014, 7, 2, 9), + ), + datetime(2014, 7, 2, 8): ( + datetime(2014, 7, 1, 9), + datetime(2014, 7, 2, 9), + ), + datetime(2014, 7, 2, 9): ( + datetime(2014, 7, 2, 9), + datetime(2014, 7, 2, 9), + ), + datetime(2014, 7, 2, 10): ( + datetime(2014, 7, 2, 9), + datetime(2014, 7, 3, 9), + ), + datetime(2014, 7, 5, 10): ( + datetime(2014, 7, 4, 9), + datetime(2014, 7, 7, 9), + ), + datetime(2014, 7, 4, 10): ( + datetime(2014, 7, 4, 9), + datetime(2014, 7, 7, 9), + ), + datetime(2014, 7, 4, 23): ( + datetime(2014, 7, 4, 9), + datetime(2014, 7, 7, 9), + ), + datetime(2014, 7, 6, 10): ( + datetime(2014, 7, 4, 9), + datetime(2014, 7, 7, 9), + ), + datetime(2014, 7, 7, 5): ( + datetime(2014, 7, 4, 9), + datetime(2014, 7, 7, 9), + ), + datetime(2014, 7, 7, 9): ( + datetime(2014, 7, 7, 9), + datetime(2014, 7, 7, 9), + ), + datetime(2014, 7, 7, 9, 1): ( + datetime(2014, 7, 7, 9), + datetime(2014, 7, 8, 9), + ), + }, + ), + ( + [ + BusinessHour(start="17:00", end="05:00"), + BusinessHour(n=3, start="17:00", end="03:00"), + ], + { + datetime(2014, 7, 1, 11): ( + datetime(2014, 7, 1, 17), + datetime(2014, 6, 30, 17), + ), + datetime(2014, 7, 1, 18): ( + datetime(2014, 7, 2, 17), + datetime(2014, 7, 1, 17), + ), + datetime(2014, 7, 1, 23): ( + datetime(2014, 7, 2, 17), + datetime(2014, 7, 1, 17), + ), + datetime(2014, 7, 2, 8): ( + datetime(2014, 7, 2, 17), + datetime(2014, 7, 1, 17), + ), + datetime(2014, 7, 2, 9): ( + datetime(2014, 7, 2, 17), + datetime(2014, 7, 1, 17), + ), + datetime(2014, 7, 4, 17): ( + datetime(2014, 7, 4, 17), + datetime(2014, 7, 4, 17), + ), + datetime(2014, 7, 5, 10): ( + datetime(2014, 7, 7, 17), + datetime(2014, 7, 4, 17), + ), + datetime(2014, 7, 4, 10): ( + datetime(2014, 7, 4, 17), + datetime(2014, 7, 3, 17), + ), + datetime(2014, 7, 4, 23): ( + datetime(2014, 7, 7, 17), + datetime(2014, 7, 4, 17), + ), + datetime(2014, 7, 6, 10): ( + datetime(2014, 7, 7, 17), + datetime(2014, 7, 4, 17), + ), + datetime(2014, 7, 7, 5): ( + datetime(2014, 7, 7, 17), + datetime(2014, 7, 4, 17), + ), + datetime(2014, 7, 7, 17, 1): ( + datetime(2014, 7, 8, 17), + datetime(2014, 7, 7, 17), + ), + }, + ), + ( + [ + BusinessHour(-1, start="17:00", end="05:00"), + BusinessHour(n=-2, start="17:00", end="03:00"), + ], + { + datetime(2014, 7, 1, 11): ( + datetime(2014, 6, 30, 17), + datetime(2014, 7, 1, 17), + ), + datetime(2014, 7, 1, 18): ( + datetime(2014, 7, 1, 17), + datetime(2014, 7, 2, 17), + ), + datetime(2014, 7, 1, 23): ( + datetime(2014, 7, 1, 17), + datetime(2014, 7, 2, 17), + ), + datetime(2014, 7, 2, 8): ( + datetime(2014, 7, 1, 17), + datetime(2014, 7, 2, 17), + ), + datetime(2014, 7, 2, 9): ( + datetime(2014, 7, 1, 17), + datetime(2014, 7, 2, 17), + ), + datetime(2014, 7, 2, 16, 59): ( + datetime(2014, 7, 1, 17), + datetime(2014, 7, 2, 17), + ), + datetime(2014, 7, 5, 10): ( + datetime(2014, 7, 4, 17), + datetime(2014, 7, 7, 17), + ), + datetime(2014, 7, 4, 10): ( + datetime(2014, 7, 3, 17), + datetime(2014, 7, 4, 17), + ), + datetime(2014, 7, 4, 23): ( + datetime(2014, 7, 4, 17), + datetime(2014, 7, 7, 17), + ), + datetime(2014, 7, 6, 10): ( + datetime(2014, 7, 4, 17), + datetime(2014, 7, 7, 17), + ), + datetime(2014, 7, 7, 5): ( + datetime(2014, 7, 4, 17), + datetime(2014, 7, 7, 17), + ), + datetime(2014, 7, 7, 18): ( + datetime(2014, 7, 7, 17), + datetime(2014, 7, 8, 17), + ), + }, + ), + ( + [ + BusinessHour(start=["11:15", "15:00"], end=["13:00", "20:00"]), + BusinessHour(n=3, start=["11:15", "15:00"], end=["12:00", "20:00"]), + BusinessHour(start=["11:15", "15:00"], end=["13:00", "17:00"]), + BusinessHour(n=2, start=["11:15", "15:00"], end=["12:00", "03:00"]), + BusinessHour(n=3, start=["11:15", "15:00"], end=["13:00", "16:00"]), + ], + { + datetime(2014, 7, 1, 11): ( + datetime(2014, 7, 1, 11, 15), + datetime(2014, 6, 30, 15), + ), + datetime(2014, 7, 1, 18): ( + datetime(2014, 7, 2, 11, 15), + datetime(2014, 7, 1, 15), + ), + datetime(2014, 7, 1, 23): ( + datetime(2014, 7, 2, 11, 15), + datetime(2014, 7, 1, 15), + ), + datetime(2014, 7, 2, 8): ( + datetime(2014, 7, 2, 11, 15), + datetime(2014, 7, 1, 15), + ), + datetime(2014, 7, 2, 9): ( + datetime(2014, 7, 2, 11, 15), + datetime(2014, 7, 1, 15), + ), + datetime(2014, 7, 2, 10): ( + datetime(2014, 7, 2, 11, 15), + datetime(2014, 7, 1, 15), + ), + datetime(2014, 7, 2, 11, 15): ( + datetime(2014, 7, 2, 11, 15), + datetime(2014, 7, 2, 11, 15), + ), + datetime(2014, 7, 2, 11, 15, 1): ( + datetime(2014, 7, 2, 15), + datetime(2014, 7, 2, 11, 15), + ), + datetime(2014, 7, 5, 10): ( + datetime(2014, 7, 7, 11, 15), + datetime(2014, 7, 4, 15), + ), + datetime(2014, 7, 4, 10): ( + datetime(2014, 7, 4, 11, 15), + datetime(2014, 7, 3, 15), + ), + datetime(2014, 7, 4, 23): ( + datetime(2014, 7, 7, 11, 15), + datetime(2014, 7, 4, 15), + ), + datetime(2014, 7, 6, 10): ( + datetime(2014, 7, 7, 11, 15), + datetime(2014, 7, 4, 15), + ), + datetime(2014, 7, 7, 5): ( + datetime(2014, 7, 7, 11, 15), + datetime(2014, 7, 4, 15), + ), + datetime(2014, 7, 7, 9, 1): ( + datetime(2014, 7, 7, 11, 15), + datetime(2014, 7, 4, 15), + ), + datetime(2014, 7, 7, 12): ( + datetime(2014, 7, 7, 15), + datetime(2014, 7, 7, 11, 15), + ), + }, + ), + ( + [ + BusinessHour(n=-1, start=["17:00", "08:00"], end=["05:00", "10:00"]), + BusinessHour(n=-2, start=["08:00", "17:00"], end=["10:00", "03:00"]), + ], + { + datetime(2014, 7, 1, 11): ( + datetime(2014, 7, 1, 8), + datetime(2014, 7, 1, 17), + ), + datetime(2014, 7, 1, 18): ( + datetime(2014, 7, 1, 17), + datetime(2014, 7, 2, 8), + ), + datetime(2014, 7, 1, 23): ( + datetime(2014, 7, 1, 17), + datetime(2014, 7, 2, 8), + ), + datetime(2014, 7, 2, 8): ( + datetime(2014, 7, 2, 8), + datetime(2014, 7, 2, 8), + ), + datetime(2014, 7, 2, 9): ( + datetime(2014, 7, 2, 8), + datetime(2014, 7, 2, 17), + ), + datetime(2014, 7, 2, 16, 59): ( + datetime(2014, 7, 2, 8), + datetime(2014, 7, 2, 17), + ), + datetime(2014, 7, 5, 10): ( + datetime(2014, 7, 4, 17), + datetime(2014, 7, 7, 8), + ), + datetime(2014, 7, 4, 10): ( + datetime(2014, 7, 4, 8), + datetime(2014, 7, 4, 17), + ), + datetime(2014, 7, 4, 23): ( + datetime(2014, 7, 4, 17), + datetime(2014, 7, 7, 8), + ), + datetime(2014, 7, 6, 10): ( + datetime(2014, 7, 4, 17), + datetime(2014, 7, 7, 8), + ), + datetime(2014, 7, 7, 5): ( + datetime(2014, 7, 4, 17), + datetime(2014, 7, 7, 8), + ), + datetime(2014, 7, 7, 18): ( + datetime(2014, 7, 7, 17), + datetime(2014, 7, 8, 8), + ), + }, + ), + ] + + @pytest.mark.parametrize("case", opening_time_cases) + def test_opening_time(self, case): + _offsets, cases = case + for offset in _offsets: + for dt, (exp_next, exp_prev) in cases.items(): + assert offset._next_opening_time(dt) == exp_next + assert offset._prev_opening_time(dt) == exp_prev diff --git a/pandas/tests/tseries/offsets/test_week.py b/pandas/tests/tseries/offsets/test_week.py new file mode 100644 index 0000000000000..54751a70b151d --- /dev/null +++ b/pandas/tests/tseries/offsets/test_week.py @@ -0,0 +1,297 @@ +""" +Tests for offset.Week, offset.WeekofMonth and offset.LastWeekofMonth +""" +from datetime import datetime, timedelta + +import pytest + +from pandas._libs.tslibs import Timestamp +from pandas._libs.tslibs.offsets import LastWeekOfMonth, Week, WeekOfMonth + +from pandas.tests.tseries.offsets.common import ( + Base, + WeekDay, + assert_is_on_offset, + assert_offset_equal, +) + + +class TestWeek(Base): + _offset = Week + d = Timestamp(datetime(2008, 1, 2)) + offset1 = _offset() + offset2 = _offset(2) + + def test_repr(self): + assert repr(Week(weekday=0)) == "" + assert repr(Week(n=-1, weekday=0)) == "<-1 * Week: weekday=0>" + assert repr(Week(n=-2, weekday=0)) == "<-2 * Weeks: weekday=0>" + + def test_corner(self): + with pytest.raises(ValueError, match="Day must be"): + Week(weekday=7) + + with pytest.raises(ValueError, match="Day must be"): + Week(weekday=-1) + + def test_is_anchored(self): + assert Week(weekday=0).is_anchored() + assert not Week().is_anchored() + assert not Week(2, weekday=2).is_anchored() + assert not Week(2).is_anchored() + + offset_cases = [] + # not business week + offset_cases.append( + ( + Week(), + { + datetime(2008, 1, 1): datetime(2008, 1, 8), + datetime(2008, 1, 4): datetime(2008, 1, 11), + datetime(2008, 1, 5): datetime(2008, 1, 12), + datetime(2008, 1, 6): datetime(2008, 1, 13), + datetime(2008, 1, 7): datetime(2008, 1, 14), + }, + ) + ) + + # Mon + offset_cases.append( + ( + Week(weekday=0), + { + datetime(2007, 12, 31): datetime(2008, 1, 7), + datetime(2008, 1, 4): datetime(2008, 1, 7), + datetime(2008, 1, 5): datetime(2008, 1, 7), + datetime(2008, 1, 6): datetime(2008, 1, 7), + datetime(2008, 1, 7): datetime(2008, 1, 14), + }, + ) + ) + + # n=0 -> roll forward. Mon + offset_cases.append( + ( + Week(0, weekday=0), + { + datetime(2007, 12, 31): datetime(2007, 12, 31), + datetime(2008, 1, 4): datetime(2008, 1, 7), + datetime(2008, 1, 5): datetime(2008, 1, 7), + datetime(2008, 1, 6): datetime(2008, 1, 7), + datetime(2008, 1, 7): datetime(2008, 1, 7), + }, + ) + ) + + # n=0 -> roll forward. Mon + offset_cases.append( + ( + Week(-2, weekday=1), + { + datetime(2010, 4, 6): datetime(2010, 3, 23), + datetime(2010, 4, 8): datetime(2010, 3, 30), + datetime(2010, 4, 5): datetime(2010, 3, 23), + }, + ) + ) + + @pytest.mark.parametrize("case", offset_cases) + def test_offset(self, case): + offset, cases = case + for base, expected in cases.items(): + assert_offset_equal(offset, base, expected) + + @pytest.mark.parametrize("weekday", range(7)) + def test_is_on_offset(self, weekday): + offset = Week(weekday=weekday) + + for day in range(1, 8): + date = datetime(2008, 1, day) + + if day % 7 == weekday: + expected = True + else: + expected = False + assert_is_on_offset(offset, date, expected) + + +class TestWeekOfMonth(Base): + _offset = WeekOfMonth + offset1 = _offset() + offset2 = _offset(2) + + def test_constructor(self): + with pytest.raises(ValueError, match="^Week"): + WeekOfMonth(n=1, week=4, weekday=0) + + with pytest.raises(ValueError, match="^Week"): + WeekOfMonth(n=1, week=-1, weekday=0) + + with pytest.raises(ValueError, match="^Day"): + WeekOfMonth(n=1, week=0, weekday=-1) + + with pytest.raises(ValueError, match="^Day"): + WeekOfMonth(n=1, week=0, weekday=-7) + + def test_repr(self): + assert ( + repr(WeekOfMonth(weekday=1, week=2)) == "" + ) + + def test_offset(self): + date1 = datetime(2011, 1, 4) # 1st Tuesday of Month + date2 = datetime(2011, 1, 11) # 2nd Tuesday of Month + date3 = datetime(2011, 1, 18) # 3rd Tuesday of Month + date4 = datetime(2011, 1, 25) # 4th Tuesday of Month + + # see for loop for structure + test_cases = [ + (-2, 2, 1, date1, datetime(2010, 11, 16)), + (-2, 2, 1, date2, datetime(2010, 11, 16)), + (-2, 2, 1, date3, datetime(2010, 11, 16)), + (-2, 2, 1, date4, datetime(2010, 12, 21)), + (-1, 2, 1, date1, datetime(2010, 12, 21)), + (-1, 2, 1, date2, datetime(2010, 12, 21)), + (-1, 2, 1, date3, datetime(2010, 12, 21)), + (-1, 2, 1, date4, datetime(2011, 1, 18)), + (0, 0, 1, date1, datetime(2011, 1, 4)), + (0, 0, 1, date2, datetime(2011, 2, 1)), + (0, 0, 1, date3, datetime(2011, 2, 1)), + (0, 0, 1, date4, datetime(2011, 2, 1)), + (0, 1, 1, date1, datetime(2011, 1, 11)), + (0, 1, 1, date2, datetime(2011, 1, 11)), + (0, 1, 1, date3, datetime(2011, 2, 8)), + (0, 1, 1, date4, datetime(2011, 2, 8)), + (0, 0, 1, date1, datetime(2011, 1, 4)), + (0, 1, 1, date2, datetime(2011, 1, 11)), + (0, 2, 1, date3, datetime(2011, 1, 18)), + (0, 3, 1, date4, datetime(2011, 1, 25)), + (1, 0, 0, date1, datetime(2011, 2, 7)), + (1, 0, 0, date2, datetime(2011, 2, 7)), + (1, 0, 0, date3, datetime(2011, 2, 7)), + (1, 0, 0, date4, datetime(2011, 2, 7)), + (1, 0, 1, date1, datetime(2011, 2, 1)), + (1, 0, 1, date2, datetime(2011, 2, 1)), + (1, 0, 1, date3, datetime(2011, 2, 1)), + (1, 0, 1, date4, datetime(2011, 2, 1)), + (1, 0, 2, date1, datetime(2011, 1, 5)), + (1, 0, 2, date2, datetime(2011, 2, 2)), + (1, 0, 2, date3, datetime(2011, 2, 2)), + (1, 0, 2, date4, datetime(2011, 2, 2)), + (1, 2, 1, date1, datetime(2011, 1, 18)), + (1, 2, 1, date2, datetime(2011, 1, 18)), + (1, 2, 1, date3, datetime(2011, 2, 15)), + (1, 2, 1, date4, datetime(2011, 2, 15)), + (2, 2, 1, date1, datetime(2011, 2, 15)), + (2, 2, 1, date2, datetime(2011, 2, 15)), + (2, 2, 1, date3, datetime(2011, 3, 15)), + (2, 2, 1, date4, datetime(2011, 3, 15)), + ] + + for n, week, weekday, dt, expected in test_cases: + offset = WeekOfMonth(n, week=week, weekday=weekday) + assert_offset_equal(offset, dt, expected) + + # try subtracting + result = datetime(2011, 2, 1) - WeekOfMonth(week=1, weekday=2) + assert result == datetime(2011, 1, 12) + + result = datetime(2011, 2, 3) - WeekOfMonth(week=0, weekday=2) + assert result == datetime(2011, 2, 2) + + on_offset_cases = [ + (0, 0, datetime(2011, 2, 7), True), + (0, 0, datetime(2011, 2, 6), False), + (0, 0, datetime(2011, 2, 14), False), + (1, 0, datetime(2011, 2, 14), True), + (0, 1, datetime(2011, 2, 1), True), + (0, 1, datetime(2011, 2, 8), False), + ] + + @pytest.mark.parametrize("case", on_offset_cases) + def test_is_on_offset(self, case): + week, weekday, dt, expected = case + offset = WeekOfMonth(week=week, weekday=weekday) + assert offset.is_on_offset(dt) == expected + + +class TestLastWeekOfMonth(Base): + _offset = LastWeekOfMonth + offset1 = _offset() + offset2 = _offset(2) + + def test_constructor(self): + with pytest.raises(ValueError, match="^N cannot be 0"): + LastWeekOfMonth(n=0, weekday=1) + + with pytest.raises(ValueError, match="^Day"): + LastWeekOfMonth(n=1, weekday=-1) + + with pytest.raises(ValueError, match="^Day"): + LastWeekOfMonth(n=1, weekday=7) + + def test_offset(self): + # Saturday + last_sat = datetime(2013, 8, 31) + next_sat = datetime(2013, 9, 28) + offset_sat = LastWeekOfMonth(n=1, weekday=5) + + one_day_before = last_sat + timedelta(days=-1) + assert one_day_before + offset_sat == last_sat + + one_day_after = last_sat + timedelta(days=+1) + assert one_day_after + offset_sat == next_sat + + # Test On that day + assert last_sat + offset_sat == next_sat + + # Thursday + + offset_thur = LastWeekOfMonth(n=1, weekday=3) + last_thurs = datetime(2013, 1, 31) + next_thurs = datetime(2013, 2, 28) + + one_day_before = last_thurs + timedelta(days=-1) + assert one_day_before + offset_thur == last_thurs + + one_day_after = last_thurs + timedelta(days=+1) + assert one_day_after + offset_thur == next_thurs + + # Test on that day + assert last_thurs + offset_thur == next_thurs + + three_before = last_thurs + timedelta(days=-3) + assert three_before + offset_thur == last_thurs + + two_after = last_thurs + timedelta(days=+2) + assert two_after + offset_thur == next_thurs + + offset_sunday = LastWeekOfMonth(n=1, weekday=WeekDay.SUN) + assert datetime(2013, 7, 31) + offset_sunday == datetime(2013, 8, 25) + + on_offset_cases = [ + (WeekDay.SUN, datetime(2013, 1, 27), True), + (WeekDay.SAT, datetime(2013, 3, 30), True), + (WeekDay.MON, datetime(2013, 2, 18), False), # Not the last Mon + (WeekDay.SUN, datetime(2013, 2, 25), False), # Not a SUN + (WeekDay.MON, datetime(2013, 2, 25), True), + (WeekDay.SAT, datetime(2013, 11, 30), True), + (WeekDay.SAT, datetime(2006, 8, 26), True), + (WeekDay.SAT, datetime(2007, 8, 25), True), + (WeekDay.SAT, datetime(2008, 8, 30), True), + (WeekDay.SAT, datetime(2009, 8, 29), True), + (WeekDay.SAT, datetime(2010, 8, 28), True), + (WeekDay.SAT, datetime(2011, 8, 27), True), + (WeekDay.SAT, datetime(2019, 8, 31), True), + ] + + @pytest.mark.parametrize("case", on_offset_cases) + def test_is_on_offset(self, case): + weekday, dt, expected = case + offset = LastWeekOfMonth(weekday=weekday) + assert offset.is_on_offset(dt) == expected + + def test_repr(self): + assert ( + repr(LastWeekOfMonth(n=2, weekday=1)) == "<2 * LastWeekOfMonths: weekday=1>" + ) diff --git a/pandas/tests/tseries/offsets/test_yqm_offsets.py b/pandas/tests/tseries/offsets/test_yqm_offsets.py index 9921355bdf2ee..260f7368123a4 100644 --- a/pandas/tests/tseries/offsets/test_yqm_offsets.py +++ b/pandas/tests/tseries/offsets/test_yqm_offsets.py @@ -7,6 +7,11 @@ import pandas as pd from pandas import Timestamp +from pandas.tests.tseries.offsets.common import ( + Base, + assert_is_on_offset, + assert_offset_equal, +) from pandas.tseries.offsets import ( BMonthBegin, @@ -23,9 +28,6 @@ YearEnd, ) -from .common import assert_is_on_offset, assert_offset_equal -from .test_offsets import Base - # -------------------------------------------------------------------- # Misc From 185ad637c71c9f712106c16a8d9d2ba574382580 Mon Sep 17 00:00:00 2001 From: moink Date: Sun, 3 Jan 2021 19:36:03 +0100 Subject: [PATCH 2/3] TST: 26807 remove 25 duplicate tests by having TestOpeningTimes not inherit from Base --- pandas/tests/tseries/offsets/test_opening_times.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pandas/tests/tseries/offsets/test_opening_times.py b/pandas/tests/tseries/offsets/test_opening_times.py index b8d1646e59b63..218a5a6337c5a 100644 --- a/pandas/tests/tseries/offsets/test_opening_times.py +++ b/pandas/tests/tseries/offsets/test_opening_times.py @@ -7,10 +7,8 @@ from pandas._libs.tslibs.offsets import BusinessHour -from pandas.tests.tseries.offsets.common import Base - -class TestOpeningTimes(Base): +class TestOpeningTimes: _offset = BusinessHour # opening time should be affected by sign of n, not by n's value and end From 2477f029d8b296a9a9f3164684a529d2fbf004b8 Mon Sep 17 00:00:00 2001 From: moink Date: Sun, 3 Jan 2021 19:38:53 +0100 Subject: [PATCH 3/3] TST: 26807 remove unneeded class attribute from TestOpeningTimes --- pandas/tests/tseries/offsets/test_opening_times.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pandas/tests/tseries/offsets/test_opening_times.py b/pandas/tests/tseries/offsets/test_opening_times.py index 218a5a6337c5a..107436e4b3343 100644 --- a/pandas/tests/tseries/offsets/test_opening_times.py +++ b/pandas/tests/tseries/offsets/test_opening_times.py @@ -9,8 +9,6 @@ class TestOpeningTimes: - _offset = BusinessHour - # opening time should be affected by sign of n, not by n's value and end opening_time_cases = [ (