test_holiday.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. from datetime import datetime
  2. import pytest
  3. from pytz import utc
  4. from pandas import DatetimeIndex, compat
  5. import pandas.util.testing as tm
  6. from pandas.tseries.holiday import (
  7. MO, SA, AbstractHolidayCalendar, DateOffset, EasterMonday, GoodFriday,
  8. Holiday, HolidayCalendarFactory, Timestamp, USColumbusDay,
  9. USFederalHolidayCalendar, USLaborDay, USMartinLutherKingJr, USMemorialDay,
  10. USPresidentsDay, USThanksgivingDay, after_nearest_workday,
  11. before_nearest_workday, get_calendar, nearest_workday, next_monday,
  12. next_monday_or_tuesday, next_workday, previous_friday, previous_workday,
  13. sunday_to_monday, weekend_to_monday)
  14. class TestCalendar(object):
  15. def setup_method(self, method):
  16. self.holiday_list = [
  17. datetime(2012, 1, 2),
  18. datetime(2012, 1, 16),
  19. datetime(2012, 2, 20),
  20. datetime(2012, 5, 28),
  21. datetime(2012, 7, 4),
  22. datetime(2012, 9, 3),
  23. datetime(2012, 10, 8),
  24. datetime(2012, 11, 12),
  25. datetime(2012, 11, 22),
  26. datetime(2012, 12, 25)]
  27. self.start_date = datetime(2012, 1, 1)
  28. self.end_date = datetime(2012, 12, 31)
  29. def test_calendar(self):
  30. calendar = USFederalHolidayCalendar()
  31. holidays = calendar.holidays(self.start_date, self.end_date)
  32. holidays_1 = calendar.holidays(
  33. self.start_date.strftime('%Y-%m-%d'),
  34. self.end_date.strftime('%Y-%m-%d'))
  35. holidays_2 = calendar.holidays(
  36. Timestamp(self.start_date),
  37. Timestamp(self.end_date))
  38. assert list(holidays.to_pydatetime()) == self.holiday_list
  39. assert list(holidays_1.to_pydatetime()) == self.holiday_list
  40. assert list(holidays_2.to_pydatetime()) == self.holiday_list
  41. def test_calendar_caching(self):
  42. # Test for issue #9552
  43. class TestCalendar(AbstractHolidayCalendar):
  44. def __init__(self, name=None, rules=None):
  45. super(TestCalendar, self).__init__(name=name, rules=rules)
  46. jan1 = TestCalendar(rules=[Holiday('jan1', year=2015, month=1, day=1)])
  47. jan2 = TestCalendar(rules=[Holiday('jan2', year=2015, month=1, day=2)])
  48. tm.assert_index_equal(jan1.holidays(), DatetimeIndex(['01-Jan-2015']))
  49. tm.assert_index_equal(jan2.holidays(), DatetimeIndex(['02-Jan-2015']))
  50. def test_calendar_observance_dates(self):
  51. # Test for issue 11477
  52. USFedCal = get_calendar('USFederalHolidayCalendar')
  53. holidays0 = USFedCal.holidays(datetime(2015, 7, 3), datetime(
  54. 2015, 7, 3)) # <-- same start and end dates
  55. holidays1 = USFedCal.holidays(datetime(2015, 7, 3), datetime(
  56. 2015, 7, 6)) # <-- different start and end dates
  57. holidays2 = USFedCal.holidays(datetime(2015, 7, 3), datetime(
  58. 2015, 7, 3)) # <-- same start and end dates
  59. tm.assert_index_equal(holidays0, holidays1)
  60. tm.assert_index_equal(holidays0, holidays2)
  61. def test_rule_from_name(self):
  62. USFedCal = get_calendar('USFederalHolidayCalendar')
  63. assert USFedCal.rule_from_name('Thanksgiving') == USThanksgivingDay
  64. class TestHoliday(object):
  65. def setup_method(self, method):
  66. self.start_date = datetime(2011, 1, 1)
  67. self.end_date = datetime(2020, 12, 31)
  68. def check_results(self, holiday, start, end, expected):
  69. assert list(holiday.dates(start, end)) == expected
  70. # Verify that timezone info is preserved.
  71. assert (list(holiday.dates(utc.localize(Timestamp(start)),
  72. utc.localize(Timestamp(end)))) ==
  73. [utc.localize(dt) for dt in expected])
  74. def test_usmemorialday(self):
  75. self.check_results(holiday=USMemorialDay,
  76. start=self.start_date,
  77. end=self.end_date,
  78. expected=[
  79. datetime(2011, 5, 30),
  80. datetime(2012, 5, 28),
  81. datetime(2013, 5, 27),
  82. datetime(2014, 5, 26),
  83. datetime(2015, 5, 25),
  84. datetime(2016, 5, 30),
  85. datetime(2017, 5, 29),
  86. datetime(2018, 5, 28),
  87. datetime(2019, 5, 27),
  88. datetime(2020, 5, 25),
  89. ], )
  90. def test_non_observed_holiday(self):
  91. self.check_results(
  92. Holiday('July 4th Eve', month=7, day=3),
  93. start="2001-01-01",
  94. end="2003-03-03",
  95. expected=[
  96. Timestamp('2001-07-03 00:00:00'),
  97. Timestamp('2002-07-03 00:00:00')
  98. ]
  99. )
  100. self.check_results(
  101. Holiday('July 4th Eve', month=7, day=3, days_of_week=(0, 1, 2, 3)),
  102. start="2001-01-01",
  103. end="2008-03-03",
  104. expected=[
  105. Timestamp('2001-07-03 00:00:00'),
  106. Timestamp('2002-07-03 00:00:00'),
  107. Timestamp('2003-07-03 00:00:00'),
  108. Timestamp('2006-07-03 00:00:00'),
  109. Timestamp('2007-07-03 00:00:00'),
  110. ]
  111. )
  112. def test_easter(self):
  113. self.check_results(EasterMonday,
  114. start=self.start_date,
  115. end=self.end_date,
  116. expected=[
  117. Timestamp('2011-04-25 00:00:00'),
  118. Timestamp('2012-04-09 00:00:00'),
  119. Timestamp('2013-04-01 00:00:00'),
  120. Timestamp('2014-04-21 00:00:00'),
  121. Timestamp('2015-04-06 00:00:00'),
  122. Timestamp('2016-03-28 00:00:00'),
  123. Timestamp('2017-04-17 00:00:00'),
  124. Timestamp('2018-04-02 00:00:00'),
  125. Timestamp('2019-04-22 00:00:00'),
  126. Timestamp('2020-04-13 00:00:00'),
  127. ], )
  128. self.check_results(GoodFriday,
  129. start=self.start_date,
  130. end=self.end_date,
  131. expected=[
  132. Timestamp('2011-04-22 00:00:00'),
  133. Timestamp('2012-04-06 00:00:00'),
  134. Timestamp('2013-03-29 00:00:00'),
  135. Timestamp('2014-04-18 00:00:00'),
  136. Timestamp('2015-04-03 00:00:00'),
  137. Timestamp('2016-03-25 00:00:00'),
  138. Timestamp('2017-04-14 00:00:00'),
  139. Timestamp('2018-03-30 00:00:00'),
  140. Timestamp('2019-04-19 00:00:00'),
  141. Timestamp('2020-04-10 00:00:00'),
  142. ], )
  143. def test_usthanksgivingday(self):
  144. self.check_results(USThanksgivingDay,
  145. start=self.start_date,
  146. end=self.end_date,
  147. expected=[
  148. datetime(2011, 11, 24),
  149. datetime(2012, 11, 22),
  150. datetime(2013, 11, 28),
  151. datetime(2014, 11, 27),
  152. datetime(2015, 11, 26),
  153. datetime(2016, 11, 24),
  154. datetime(2017, 11, 23),
  155. datetime(2018, 11, 22),
  156. datetime(2019, 11, 28),
  157. datetime(2020, 11, 26),
  158. ], )
  159. def test_holidays_within_dates(self):
  160. # Fix holiday behavior found in #11477
  161. # where holiday.dates returned dates outside start/end date
  162. # or observed rules could not be applied as the holiday
  163. # was not in the original date range (e.g., 7/4/2015 -> 7/3/2015)
  164. start_date = datetime(2015, 7, 1)
  165. end_date = datetime(2015, 7, 1)
  166. calendar = get_calendar('USFederalHolidayCalendar')
  167. new_years = calendar.rule_from_name('New Years Day')
  168. july_4th = calendar.rule_from_name('July 4th')
  169. veterans_day = calendar.rule_from_name('Veterans Day')
  170. christmas = calendar.rule_from_name('Christmas')
  171. # Holiday: (start/end date, holiday)
  172. holidays = {USMemorialDay: ("2015-05-25", "2015-05-25"),
  173. USLaborDay: ("2015-09-07", "2015-09-07"),
  174. USColumbusDay: ("2015-10-12", "2015-10-12"),
  175. USThanksgivingDay: ("2015-11-26", "2015-11-26"),
  176. USMartinLutherKingJr: ("2015-01-19", "2015-01-19"),
  177. USPresidentsDay: ("2015-02-16", "2015-02-16"),
  178. GoodFriday: ("2015-04-03", "2015-04-03"),
  179. EasterMonday: [("2015-04-06", "2015-04-06"),
  180. ("2015-04-05", [])],
  181. new_years: [("2015-01-01", "2015-01-01"),
  182. ("2011-01-01", []),
  183. ("2010-12-31", "2010-12-31")],
  184. july_4th: [("2015-07-03", "2015-07-03"),
  185. ("2015-07-04", [])],
  186. veterans_day: [("2012-11-11", []),
  187. ("2012-11-12", "2012-11-12")],
  188. christmas: [("2011-12-25", []),
  189. ("2011-12-26", "2011-12-26")]}
  190. for rule, dates in compat.iteritems(holidays):
  191. empty_dates = rule.dates(start_date, end_date)
  192. assert empty_dates.tolist() == []
  193. if isinstance(dates, tuple):
  194. dates = [dates]
  195. for start, expected in dates:
  196. if len(expected):
  197. expected = [Timestamp(expected)]
  198. self.check_results(rule, start, start, expected)
  199. def test_argument_types(self):
  200. holidays = USThanksgivingDay.dates(self.start_date, self.end_date)
  201. holidays_1 = USThanksgivingDay.dates(
  202. self.start_date.strftime('%Y-%m-%d'),
  203. self.end_date.strftime('%Y-%m-%d'))
  204. holidays_2 = USThanksgivingDay.dates(
  205. Timestamp(self.start_date),
  206. Timestamp(self.end_date))
  207. tm.assert_index_equal(holidays, holidays_1)
  208. tm.assert_index_equal(holidays, holidays_2)
  209. def test_special_holidays(self):
  210. base_date = [datetime(2012, 5, 28)]
  211. holiday_1 = Holiday('One-Time', year=2012, month=5, day=28)
  212. holiday_2 = Holiday('Range', month=5, day=28,
  213. start_date=datetime(2012, 1, 1),
  214. end_date=datetime(2012, 12, 31),
  215. offset=DateOffset(weekday=MO(1)))
  216. assert base_date == holiday_1.dates(self.start_date, self.end_date)
  217. assert base_date == holiday_2.dates(self.start_date, self.end_date)
  218. def test_get_calendar(self):
  219. class TestCalendar(AbstractHolidayCalendar):
  220. rules = []
  221. calendar = get_calendar('TestCalendar')
  222. assert TestCalendar == calendar.__class__
  223. def test_factory(self):
  224. class_1 = HolidayCalendarFactory('MemorialDay',
  225. AbstractHolidayCalendar,
  226. USMemorialDay)
  227. class_2 = HolidayCalendarFactory('Thansksgiving',
  228. AbstractHolidayCalendar,
  229. USThanksgivingDay)
  230. class_3 = HolidayCalendarFactory('Combined', class_1, class_2)
  231. assert len(class_1.rules) == 1
  232. assert len(class_2.rules) == 1
  233. assert len(class_3.rules) == 2
  234. class TestObservanceRules(object):
  235. def setup_method(self, method):
  236. self.we = datetime(2014, 4, 9)
  237. self.th = datetime(2014, 4, 10)
  238. self.fr = datetime(2014, 4, 11)
  239. self.sa = datetime(2014, 4, 12)
  240. self.su = datetime(2014, 4, 13)
  241. self.mo = datetime(2014, 4, 14)
  242. self.tu = datetime(2014, 4, 15)
  243. def test_next_monday(self):
  244. assert next_monday(self.sa) == self.mo
  245. assert next_monday(self.su) == self.mo
  246. def test_next_monday_or_tuesday(self):
  247. assert next_monday_or_tuesday(self.sa) == self.mo
  248. assert next_monday_or_tuesday(self.su) == self.tu
  249. assert next_monday_or_tuesday(self.mo) == self.tu
  250. def test_previous_friday(self):
  251. assert previous_friday(self.sa) == self.fr
  252. assert previous_friday(self.su) == self.fr
  253. def test_sunday_to_monday(self):
  254. assert sunday_to_monday(self.su) == self.mo
  255. def test_nearest_workday(self):
  256. assert nearest_workday(self.sa) == self.fr
  257. assert nearest_workday(self.su) == self.mo
  258. assert nearest_workday(self.mo) == self.mo
  259. def test_weekend_to_monday(self):
  260. assert weekend_to_monday(self.sa) == self.mo
  261. assert weekend_to_monday(self.su) == self.mo
  262. assert weekend_to_monday(self.mo) == self.mo
  263. def test_next_workday(self):
  264. assert next_workday(self.sa) == self.mo
  265. assert next_workday(self.su) == self.mo
  266. assert next_workday(self.mo) == self.tu
  267. def test_previous_workday(self):
  268. assert previous_workday(self.sa) == self.fr
  269. assert previous_workday(self.su) == self.fr
  270. assert previous_workday(self.tu) == self.mo
  271. def test_before_nearest_workday(self):
  272. assert before_nearest_workday(self.sa) == self.th
  273. assert before_nearest_workday(self.su) == self.fr
  274. assert before_nearest_workday(self.tu) == self.mo
  275. def test_after_nearest_workday(self):
  276. assert after_nearest_workday(self.sa) == self.mo
  277. assert after_nearest_workday(self.su) == self.tu
  278. assert after_nearest_workday(self.fr) == self.mo
  279. class TestFederalHolidayCalendar(object):
  280. def test_no_mlk_before_1986(self):
  281. # see gh-10278
  282. class MLKCalendar(AbstractHolidayCalendar):
  283. rules = [USMartinLutherKingJr]
  284. holidays = MLKCalendar().holidays(start='1984',
  285. end='1988').to_pydatetime().tolist()
  286. # Testing to make sure holiday is not incorrectly observed before 1986
  287. assert holidays == [datetime(1986, 1, 20, 0, 0),
  288. datetime(1987, 1, 19, 0, 0)]
  289. def test_memorial_day(self):
  290. class MemorialDay(AbstractHolidayCalendar):
  291. rules = [USMemorialDay]
  292. holidays = MemorialDay().holidays(start='1971',
  293. end='1980').to_pydatetime().tolist()
  294. # Fixes 5/31 error and checked manually against Wikipedia
  295. assert holidays == [datetime(1971, 5, 31, 0, 0),
  296. datetime(1972, 5, 29, 0, 0),
  297. datetime(1973, 5, 28, 0, 0),
  298. datetime(1974, 5, 27, 0, 0),
  299. datetime(1975, 5, 26, 0, 0),
  300. datetime(1976, 5, 31, 0, 0),
  301. datetime(1977, 5, 30, 0, 0),
  302. datetime(1978, 5, 29, 0, 0),
  303. datetime(1979, 5, 28, 0, 0)]
  304. class TestHolidayConflictingArguments(object):
  305. def test_both_offset_observance_raises(self):
  306. # see gh-10217
  307. with pytest.raises(NotImplementedError):
  308. Holiday("Cyber Monday", month=11, day=1,
  309. offset=[DateOffset(weekday=SA(4))],
  310. observance=next_monday)