386 lines
16 KiB
Python
386 lines
16 KiB
Python
|
import pytest
|
||
|
|
||
|
from datetime import datetime
|
||
|
import pandas.util.testing as tm
|
||
|
from pandas import compat
|
||
|
from pandas import DatetimeIndex
|
||
|
from pandas.tseries.holiday import (USFederalHolidayCalendar, USMemorialDay,
|
||
|
USThanksgivingDay, nearest_workday,
|
||
|
next_monday_or_tuesday, next_monday,
|
||
|
previous_friday, sunday_to_monday, Holiday,
|
||
|
DateOffset, MO, SA, Timestamp,
|
||
|
AbstractHolidayCalendar, get_calendar,
|
||
|
HolidayCalendarFactory, next_workday,
|
||
|
previous_workday, before_nearest_workday,
|
||
|
EasterMonday, GoodFriday,
|
||
|
after_nearest_workday, weekend_to_monday,
|
||
|
USLaborDay, USColumbusDay,
|
||
|
USMartinLutherKingJr, USPresidentsDay)
|
||
|
from pytz import utc
|
||
|
|
||
|
|
||
|
class TestCalendar(object):
|
||
|
|
||
|
def setup_method(self, method):
|
||
|
self.holiday_list = [
|
||
|
datetime(2012, 1, 2),
|
||
|
datetime(2012, 1, 16),
|
||
|
datetime(2012, 2, 20),
|
||
|
datetime(2012, 5, 28),
|
||
|
datetime(2012, 7, 4),
|
||
|
datetime(2012, 9, 3),
|
||
|
datetime(2012, 10, 8),
|
||
|
datetime(2012, 11, 12),
|
||
|
datetime(2012, 11, 22),
|
||
|
datetime(2012, 12, 25)]
|
||
|
|
||
|
self.start_date = datetime(2012, 1, 1)
|
||
|
self.end_date = datetime(2012, 12, 31)
|
||
|
|
||
|
def test_calendar(self):
|
||
|
|
||
|
calendar = USFederalHolidayCalendar()
|
||
|
holidays = calendar.holidays(self.start_date, self.end_date)
|
||
|
|
||
|
holidays_1 = calendar.holidays(
|
||
|
self.start_date.strftime('%Y-%m-%d'),
|
||
|
self.end_date.strftime('%Y-%m-%d'))
|
||
|
holidays_2 = calendar.holidays(
|
||
|
Timestamp(self.start_date),
|
||
|
Timestamp(self.end_date))
|
||
|
|
||
|
assert list(holidays.to_pydatetime()) == self.holiday_list
|
||
|
assert list(holidays_1.to_pydatetime()) == self.holiday_list
|
||
|
assert list(holidays_2.to_pydatetime()) == self.holiday_list
|
||
|
|
||
|
def test_calendar_caching(self):
|
||
|
# Test for issue #9552
|
||
|
|
||
|
class TestCalendar(AbstractHolidayCalendar):
|
||
|
|
||
|
def __init__(self, name=None, rules=None):
|
||
|
super(TestCalendar, self).__init__(name=name, rules=rules)
|
||
|
|
||
|
jan1 = TestCalendar(rules=[Holiday('jan1', year=2015, month=1, day=1)])
|
||
|
jan2 = TestCalendar(rules=[Holiday('jan2', year=2015, month=1, day=2)])
|
||
|
|
||
|
tm.assert_index_equal(jan1.holidays(), DatetimeIndex(['01-Jan-2015']))
|
||
|
tm.assert_index_equal(jan2.holidays(), DatetimeIndex(['02-Jan-2015']))
|
||
|
|
||
|
def test_calendar_observance_dates(self):
|
||
|
# Test for issue 11477
|
||
|
USFedCal = get_calendar('USFederalHolidayCalendar')
|
||
|
holidays0 = USFedCal.holidays(datetime(2015, 7, 3), datetime(
|
||
|
2015, 7, 3)) # <-- same start and end dates
|
||
|
holidays1 = USFedCal.holidays(datetime(2015, 7, 3), datetime(
|
||
|
2015, 7, 6)) # <-- different start and end dates
|
||
|
holidays2 = USFedCal.holidays(datetime(2015, 7, 3), datetime(
|
||
|
2015, 7, 3)) # <-- same start and end dates
|
||
|
|
||
|
tm.assert_index_equal(holidays0, holidays1)
|
||
|
tm.assert_index_equal(holidays0, holidays2)
|
||
|
|
||
|
def test_rule_from_name(self):
|
||
|
USFedCal = get_calendar('USFederalHolidayCalendar')
|
||
|
assert USFedCal.rule_from_name('Thanksgiving') == USThanksgivingDay
|
||
|
|
||
|
|
||
|
class TestHoliday(object):
|
||
|
|
||
|
def setup_method(self, method):
|
||
|
self.start_date = datetime(2011, 1, 1)
|
||
|
self.end_date = datetime(2020, 12, 31)
|
||
|
|
||
|
def check_results(self, holiday, start, end, expected):
|
||
|
assert list(holiday.dates(start, end)) == expected
|
||
|
|
||
|
# Verify that timezone info is preserved.
|
||
|
assert (list(holiday.dates(utc.localize(Timestamp(start)),
|
||
|
utc.localize(Timestamp(end)))) ==
|
||
|
[utc.localize(dt) for dt in expected])
|
||
|
|
||
|
def test_usmemorialday(self):
|
||
|
self.check_results(holiday=USMemorialDay,
|
||
|
start=self.start_date,
|
||
|
end=self.end_date,
|
||
|
expected=[
|
||
|
datetime(2011, 5, 30),
|
||
|
datetime(2012, 5, 28),
|
||
|
datetime(2013, 5, 27),
|
||
|
datetime(2014, 5, 26),
|
||
|
datetime(2015, 5, 25),
|
||
|
datetime(2016, 5, 30),
|
||
|
datetime(2017, 5, 29),
|
||
|
datetime(2018, 5, 28),
|
||
|
datetime(2019, 5, 27),
|
||
|
datetime(2020, 5, 25),
|
||
|
], )
|
||
|
|
||
|
def test_non_observed_holiday(self):
|
||
|
|
||
|
self.check_results(
|
||
|
Holiday('July 4th Eve', month=7, day=3),
|
||
|
start="2001-01-01",
|
||
|
end="2003-03-03",
|
||
|
expected=[
|
||
|
Timestamp('2001-07-03 00:00:00'),
|
||
|
Timestamp('2002-07-03 00:00:00')
|
||
|
]
|
||
|
)
|
||
|
|
||
|
self.check_results(
|
||
|
Holiday('July 4th Eve', month=7, day=3, days_of_week=(0, 1, 2, 3)),
|
||
|
start="2001-01-01",
|
||
|
end="2008-03-03",
|
||
|
expected=[
|
||
|
Timestamp('2001-07-03 00:00:00'),
|
||
|
Timestamp('2002-07-03 00:00:00'),
|
||
|
Timestamp('2003-07-03 00:00:00'),
|
||
|
Timestamp('2006-07-03 00:00:00'),
|
||
|
Timestamp('2007-07-03 00:00:00'),
|
||
|
]
|
||
|
)
|
||
|
|
||
|
def test_easter(self):
|
||
|
|
||
|
self.check_results(EasterMonday,
|
||
|
start=self.start_date,
|
||
|
end=self.end_date,
|
||
|
expected=[
|
||
|
Timestamp('2011-04-25 00:00:00'),
|
||
|
Timestamp('2012-04-09 00:00:00'),
|
||
|
Timestamp('2013-04-01 00:00:00'),
|
||
|
Timestamp('2014-04-21 00:00:00'),
|
||
|
Timestamp('2015-04-06 00:00:00'),
|
||
|
Timestamp('2016-03-28 00:00:00'),
|
||
|
Timestamp('2017-04-17 00:00:00'),
|
||
|
Timestamp('2018-04-02 00:00:00'),
|
||
|
Timestamp('2019-04-22 00:00:00'),
|
||
|
Timestamp('2020-04-13 00:00:00'),
|
||
|
], )
|
||
|
self.check_results(GoodFriday,
|
||
|
start=self.start_date,
|
||
|
end=self.end_date,
|
||
|
expected=[
|
||
|
Timestamp('2011-04-22 00:00:00'),
|
||
|
Timestamp('2012-04-06 00:00:00'),
|
||
|
Timestamp('2013-03-29 00:00:00'),
|
||
|
Timestamp('2014-04-18 00:00:00'),
|
||
|
Timestamp('2015-04-03 00:00:00'),
|
||
|
Timestamp('2016-03-25 00:00:00'),
|
||
|
Timestamp('2017-04-14 00:00:00'),
|
||
|
Timestamp('2018-03-30 00:00:00'),
|
||
|
Timestamp('2019-04-19 00:00:00'),
|
||
|
Timestamp('2020-04-10 00:00:00'),
|
||
|
], )
|
||
|
|
||
|
def test_usthanksgivingday(self):
|
||
|
|
||
|
self.check_results(USThanksgivingDay,
|
||
|
start=self.start_date,
|
||
|
end=self.end_date,
|
||
|
expected=[
|
||
|
datetime(2011, 11, 24),
|
||
|
datetime(2012, 11, 22),
|
||
|
datetime(2013, 11, 28),
|
||
|
datetime(2014, 11, 27),
|
||
|
datetime(2015, 11, 26),
|
||
|
datetime(2016, 11, 24),
|
||
|
datetime(2017, 11, 23),
|
||
|
datetime(2018, 11, 22),
|
||
|
datetime(2019, 11, 28),
|
||
|
datetime(2020, 11, 26),
|
||
|
], )
|
||
|
|
||
|
def test_holidays_within_dates(self):
|
||
|
# Fix holiday behavior found in #11477
|
||
|
# where holiday.dates returned dates outside start/end date
|
||
|
# or observed rules could not be applied as the holiday
|
||
|
# was not in the original date range (e.g., 7/4/2015 -> 7/3/2015)
|
||
|
start_date = datetime(2015, 7, 1)
|
||
|
end_date = datetime(2015, 7, 1)
|
||
|
|
||
|
calendar = get_calendar('USFederalHolidayCalendar')
|
||
|
new_years = calendar.rule_from_name('New Years Day')
|
||
|
july_4th = calendar.rule_from_name('July 4th')
|
||
|
veterans_day = calendar.rule_from_name('Veterans Day')
|
||
|
christmas = calendar.rule_from_name('Christmas')
|
||
|
|
||
|
# Holiday: (start/end date, holiday)
|
||
|
holidays = {USMemorialDay: ("2015-05-25", "2015-05-25"),
|
||
|
USLaborDay: ("2015-09-07", "2015-09-07"),
|
||
|
USColumbusDay: ("2015-10-12", "2015-10-12"),
|
||
|
USThanksgivingDay: ("2015-11-26", "2015-11-26"),
|
||
|
USMartinLutherKingJr: ("2015-01-19", "2015-01-19"),
|
||
|
USPresidentsDay: ("2015-02-16", "2015-02-16"),
|
||
|
GoodFriday: ("2015-04-03", "2015-04-03"),
|
||
|
EasterMonday: [("2015-04-06", "2015-04-06"),
|
||
|
("2015-04-05", [])],
|
||
|
new_years: [("2015-01-01", "2015-01-01"),
|
||
|
("2011-01-01", []),
|
||
|
("2010-12-31", "2010-12-31")],
|
||
|
july_4th: [("2015-07-03", "2015-07-03"),
|
||
|
("2015-07-04", [])],
|
||
|
veterans_day: [("2012-11-11", []),
|
||
|
("2012-11-12", "2012-11-12")],
|
||
|
christmas: [("2011-12-25", []),
|
||
|
("2011-12-26", "2011-12-26")]}
|
||
|
|
||
|
for rule, dates in compat.iteritems(holidays):
|
||
|
empty_dates = rule.dates(start_date, end_date)
|
||
|
assert empty_dates.tolist() == []
|
||
|
|
||
|
if isinstance(dates, tuple):
|
||
|
dates = [dates]
|
||
|
|
||
|
for start, expected in dates:
|
||
|
if len(expected):
|
||
|
expected = [Timestamp(expected)]
|
||
|
self.check_results(rule, start, start, expected)
|
||
|
|
||
|
def test_argument_types(self):
|
||
|
holidays = USThanksgivingDay.dates(self.start_date, self.end_date)
|
||
|
|
||
|
holidays_1 = USThanksgivingDay.dates(
|
||
|
self.start_date.strftime('%Y-%m-%d'),
|
||
|
self.end_date.strftime('%Y-%m-%d'))
|
||
|
|
||
|
holidays_2 = USThanksgivingDay.dates(
|
||
|
Timestamp(self.start_date),
|
||
|
Timestamp(self.end_date))
|
||
|
|
||
|
tm.assert_index_equal(holidays, holidays_1)
|
||
|
tm.assert_index_equal(holidays, holidays_2)
|
||
|
|
||
|
def test_special_holidays(self):
|
||
|
base_date = [datetime(2012, 5, 28)]
|
||
|
holiday_1 = Holiday('One-Time', year=2012, month=5, day=28)
|
||
|
holiday_2 = Holiday('Range', month=5, day=28,
|
||
|
start_date=datetime(2012, 1, 1),
|
||
|
end_date=datetime(2012, 12, 31),
|
||
|
offset=DateOffset(weekday=MO(1)))
|
||
|
|
||
|
assert base_date == holiday_1.dates(self.start_date, self.end_date)
|
||
|
assert base_date == holiday_2.dates(self.start_date, self.end_date)
|
||
|
|
||
|
def test_get_calendar(self):
|
||
|
class TestCalendar(AbstractHolidayCalendar):
|
||
|
rules = []
|
||
|
|
||
|
calendar = get_calendar('TestCalendar')
|
||
|
assert TestCalendar == calendar.__class__
|
||
|
|
||
|
def test_factory(self):
|
||
|
class_1 = HolidayCalendarFactory('MemorialDay',
|
||
|
AbstractHolidayCalendar,
|
||
|
USMemorialDay)
|
||
|
class_2 = HolidayCalendarFactory('Thansksgiving',
|
||
|
AbstractHolidayCalendar,
|
||
|
USThanksgivingDay)
|
||
|
class_3 = HolidayCalendarFactory('Combined', class_1, class_2)
|
||
|
|
||
|
assert len(class_1.rules) == 1
|
||
|
assert len(class_2.rules) == 1
|
||
|
assert len(class_3.rules) == 2
|
||
|
|
||
|
|
||
|
class TestObservanceRules(object):
|
||
|
|
||
|
def setup_method(self, method):
|
||
|
self.we = datetime(2014, 4, 9)
|
||
|
self.th = datetime(2014, 4, 10)
|
||
|
self.fr = datetime(2014, 4, 11)
|
||
|
self.sa = datetime(2014, 4, 12)
|
||
|
self.su = datetime(2014, 4, 13)
|
||
|
self.mo = datetime(2014, 4, 14)
|
||
|
self.tu = datetime(2014, 4, 15)
|
||
|
|
||
|
def test_next_monday(self):
|
||
|
assert next_monday(self.sa) == self.mo
|
||
|
assert next_monday(self.su) == self.mo
|
||
|
|
||
|
def test_next_monday_or_tuesday(self):
|
||
|
assert next_monday_or_tuesday(self.sa) == self.mo
|
||
|
assert next_monday_or_tuesday(self.su) == self.tu
|
||
|
assert next_monday_or_tuesday(self.mo) == self.tu
|
||
|
|
||
|
def test_previous_friday(self):
|
||
|
assert previous_friday(self.sa) == self.fr
|
||
|
assert previous_friday(self.su) == self.fr
|
||
|
|
||
|
def test_sunday_to_monday(self):
|
||
|
assert sunday_to_monday(self.su) == self.mo
|
||
|
|
||
|
def test_nearest_workday(self):
|
||
|
assert nearest_workday(self.sa) == self.fr
|
||
|
assert nearest_workday(self.su) == self.mo
|
||
|
assert nearest_workday(self.mo) == self.mo
|
||
|
|
||
|
def test_weekend_to_monday(self):
|
||
|
assert weekend_to_monday(self.sa) == self.mo
|
||
|
assert weekend_to_monday(self.su) == self.mo
|
||
|
assert weekend_to_monday(self.mo) == self.mo
|
||
|
|
||
|
def test_next_workday(self):
|
||
|
assert next_workday(self.sa) == self.mo
|
||
|
assert next_workday(self.su) == self.mo
|
||
|
assert next_workday(self.mo) == self.tu
|
||
|
|
||
|
def test_previous_workday(self):
|
||
|
assert previous_workday(self.sa) == self.fr
|
||
|
assert previous_workday(self.su) == self.fr
|
||
|
assert previous_workday(self.tu) == self.mo
|
||
|
|
||
|
def test_before_nearest_workday(self):
|
||
|
assert before_nearest_workday(self.sa) == self.th
|
||
|
assert before_nearest_workday(self.su) == self.fr
|
||
|
assert before_nearest_workday(self.tu) == self.mo
|
||
|
|
||
|
def test_after_nearest_workday(self):
|
||
|
assert after_nearest_workday(self.sa) == self.mo
|
||
|
assert after_nearest_workday(self.su) == self.tu
|
||
|
assert after_nearest_workday(self.fr) == self.mo
|
||
|
|
||
|
|
||
|
class TestFederalHolidayCalendar(object):
|
||
|
|
||
|
def test_no_mlk_before_1986(self):
|
||
|
# see gh-10278
|
||
|
class MLKCalendar(AbstractHolidayCalendar):
|
||
|
rules = [USMartinLutherKingJr]
|
||
|
|
||
|
holidays = MLKCalendar().holidays(start='1984',
|
||
|
end='1988').to_pydatetime().tolist()
|
||
|
|
||
|
# Testing to make sure holiday is not incorrectly observed before 1986
|
||
|
assert holidays == [datetime(1986, 1, 20, 0, 0),
|
||
|
datetime(1987, 1, 19, 0, 0)]
|
||
|
|
||
|
def test_memorial_day(self):
|
||
|
class MemorialDay(AbstractHolidayCalendar):
|
||
|
rules = [USMemorialDay]
|
||
|
|
||
|
holidays = MemorialDay().holidays(start='1971',
|
||
|
end='1980').to_pydatetime().tolist()
|
||
|
|
||
|
# Fixes 5/31 error and checked manually against Wikipedia
|
||
|
assert holidays == [datetime(1971, 5, 31, 0, 0),
|
||
|
datetime(1972, 5, 29, 0, 0),
|
||
|
datetime(1973, 5, 28, 0, 0),
|
||
|
datetime(1974, 5, 27, 0, 0),
|
||
|
datetime(1975, 5, 26, 0, 0),
|
||
|
datetime(1976, 5, 31, 0, 0),
|
||
|
datetime(1977, 5, 30, 0, 0),
|
||
|
datetime(1978, 5, 29, 0, 0),
|
||
|
datetime(1979, 5, 28, 0, 0)]
|
||
|
|
||
|
|
||
|
class TestHolidayConflictingArguments(object):
|
||
|
|
||
|
def test_both_offset_observance_raises(self):
|
||
|
# see gh-10217
|
||
|
with pytest.raises(NotImplementedError):
|
||
|
Holiday("Cyber Monday", month=11, day=1,
|
||
|
offset=[DateOffset(weekday=SA(4))],
|
||
|
observance=next_monday)
|