Skip to content

Commit

Permalink
Bugfix: USA calendar would take the shift_exceptions into account, …
Browse files Browse the repository at this point in the history
…even if the exceptions are set "next/previous year"

refs #610
  • Loading branch information
brunobord committed Mar 5, 2021
1 parent 9263688 commit 5d56069
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 15 deletions.
2 changes: 1 addition & 1 deletion Changelog.md
Expand Up @@ -2,7 +2,7 @@

## master (unreleased)

Nothing here yet.
- Bugfix: USA calendar would take the `shift_exceptions` into account, even if the exceptions are set "next/previous year" (#610).

## v15.0.1 (2021-02-26)

Expand Down
26 changes: 26 additions & 0 deletions workalendar/tests/test_usa.py
Expand Up @@ -1905,3 +1905,29 @@ def test_shift_2010(self):
self.assertIn(fourth_july, holiday_dict)
self.assertEqual(holiday_dict[fourth_july], "Independence Day")
self.assertNotIn(observed, holiday_dict)


class TestCaseShiftExceptionsNextYear(UnitedStatesTest):
class CalendarShiftException(UnitedStates):
"Shift exception happens on jan 1st and XMas"
shift_exceptions = (
(1, 1), # January 1st
(12, 25), # Christmas Day
)

cal_class = CalendarShiftException

def test_shift_2021(self):
# January 1st is a saturday in 2022
# As a consequence, Dec 31st should be a holiday.
# BUT here, January 1st is in the shift_exceptions,
# so 2021-12-31 should be a working day
holidays = self.cal.holidays_set(2021)
# XMas is a holiday
self.assertIn(date(2021, 12, 25), holidays)
# XMas eve is a working day
self.assertNotIn(date(2021, 12, 24), holidays)

# January 1st is a non-shift, Dec 31st should be a working day
self.assertNotIn(date(2021, 12, 31), holidays)
self.assertTrue(self.cal.is_working_day(date(2021, 12, 31)))
37 changes: 23 additions & 14 deletions workalendar/usa/core.py
Expand Up @@ -70,11 +70,18 @@ class UnitedStates(WesternCalendar):
)

def shift(self, holidays, year):
"""
Shift all holidays of the year, according to the shifting rules.
"""
new_holidays = []
holiday_lookup = [x[0] for x in holidays]
exceptions = [
date(year, month, day) for month, day in self.shift_exceptions
]
exceptions = []
if self.shift_exceptions:
exceptions = [
*[date(year - 1, m, d) for m, d in self.shift_exceptions],
*[date(year, m, d) for m, d in self.shift_exceptions],
*[date(year + 1, m, d) for m, d in self.shift_exceptions]
]

# For each holiday available:
# * if it falls on SUN, add the observed on MON
Expand All @@ -84,16 +91,18 @@ def shift(self, holidays, year):
if day in exceptions:
continue
if day.weekday() == SAT:
new_holidays.append((day - timedelta(days=1),
f"{label} (Observed)"))
new_holidays.append(
(day - timedelta(days=1), f"{label} (Observed)"))
elif day.weekday() == SUN:
new_holidays.append((day + timedelta(days=1),
f"{label} (Observed)"))
new_holidays.append(
(day + timedelta(days=1), f"{label} (Observed)"))

# If year+1 January the 1st is on SAT, add the FRI before to observed
if date(year + 1, 1, 1).weekday() == SAT:
new_holidays.append((date(year, 12, 31,),
"New Years Day (Observed)"))
next_year_jan_1st = date(year + 1, 1, 1)
if next_year_jan_1st.weekday() == SAT and \
next_year_jan_1st not in exceptions:
new_holidays.append(
(date(year, 12, 31,), "New Years Day (Observed)"))

# Special rules for XMas and XMas Eve
christmas = date(year, 12, 25)
Expand All @@ -106,16 +115,16 @@ def shift(self, holidays, year):
new_holidays.remove(
(christmas_eve, "Christmas Day (Observed)")
)
new_holidays.append((date(year, 12, 23),
"Christmas Eve (Observed)"))
new_holidays.append(
(date(year, 12, 23), "Christmas Eve (Observed)"))
# You are observing the 26th (TUE)
elif christmas.weekday() == MON:
# Remove the "fake" XMAS Eve shift, done before
new_holidays.remove(
(christmas, "Christmas Eve (Observed)")
)
new_holidays.append((date(year, 12, 26),
"Christmas Day (Observed)"))
new_holidays.append(
(date(year, 12, 26), "Christmas Day (Observed)"))
return holidays + new_holidays

@staticmethod
Expand Down

0 comments on commit 5d56069

Please sign in to comment.