Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add special holidays per subdivisions support #1520

Merged
merged 5 commits into from
Oct 23, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions holidays/countries/australia.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from datetime import date
from datetime import timedelta as td

from holidays.calendars.gregorian import APR, AUG, SEP, OCT, FRI, _get_nth_weekday_from
from holidays.calendars.gregorian import APR, JUN, AUG, SEP, OCT, FRI, _get_nth_weekday_from
from holidays.groups import ChristianHolidays, InternationalHolidays, StaticHolidays
from holidays.observed_holiday_base import (
ObservedHolidayBase,
Expand Down Expand Up @@ -208,8 +208,6 @@ def _add_subdiv_qld_holidays(self):
self._add_holiday_2nd_mon_of_jun(self.sovereign_birthday)
else:
self._add_holiday_1st_mon_of_oct(self.sovereign_birthday)
if self._year == 2012:
self._add_holiday_jun_11("Queen's Diamond Jubilee")

# Anzac Day
if self._year >= 1921:
Expand Down Expand Up @@ -321,3 +319,7 @@ class AustraliaStaticHolidays:
special_holidays = {
2022: (SEP, 22, "National Day of Mourning for Queen Elizabeth II"),
}

special_qld_holidays = {
2012: (JUN, 11, "Queen's Diamond Jubilee"),
}
60 changes: 33 additions & 27 deletions holidays/countries/canada.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
from datetime import date
from gettext import gettext as tr

from holidays.calendars.gregorian import MAR, APR, JUN, JUL
from holidays.calendars.gregorian import MAR, APR, JUN, JUL, SEP
from holidays.constants import GOVERNMENT, OPTIONAL, PUBLIC
from holidays.groups import ChristianHolidays, InternationalHolidays
from holidays.groups import ChristianHolidays, InternationalHolidays, StaticHolidays
from holidays.observed_holiday_base import (
ObservedHolidayBase,
ALL_TO_NEAREST_MON,
Expand All @@ -25,7 +25,7 @@
)


class Canada(ObservedHolidayBase, ChristianHolidays, InternationalHolidays):
class Canada(ObservedHolidayBase, ChristianHolidays, InternationalHolidays, StaticHolidays):
"""
References:
- https://en.wikipedia.org/wiki/Public_holidays_in_Canada
Expand Down Expand Up @@ -64,6 +64,7 @@ class Canada(ObservedHolidayBase, ChristianHolidays, InternationalHolidays):
def __init__(self, *args, **kwargs):
ChristianHolidays.__init__(self)
InternationalHolidays.__init__(self)
StaticHolidays.__init__(self, CanadaStaticHolidays)
super().__init__(observed_rule=SAT_SUN_TO_NEXT_MON, *args, **kwargs)

def _get_nearest_monday(self, *args) -> date:
Expand Down Expand Up @@ -216,10 +217,6 @@ def _add_subdiv_bc_public_holidays(self):
# British Columbia Day.
self._add_holiday_1st_mon_of_aug(tr("British Columbia Day"))

if self._year == 2022:
# Funeral of Queen Elizabeth II.
self._add_holiday_sep_19(tr("Funeral of Her Majesty the Queen Elizabeth II"))

if self._year >= 2023:
# National Day for Truth and Reconciliation.
self._add_holiday_sep_30(tr("National Day for Truth and Reconciliation"))
Expand Down Expand Up @@ -266,10 +263,6 @@ def _add_subdiv_nb_public_holidays(self):
# New Brunswick Day.
self._add_holiday_1st_mon_of_aug(tr("New Brunswick Day"))

if self._year == 2022:
# Funeral of Queen Elizabeth II.
self._add_holiday_sep_19(tr("Funeral of Her Majesty the Queen Elizabeth II"))

if self._year >= 1931:
# Remembrance Day.
self._add_remembrance_day(tr("Remembrance Day"))
Expand All @@ -292,10 +285,6 @@ def _add_subdiv_nl_public_holidays(self):
if self._year >= 1879:
self._add_observed(self._canada_day)

if self._year == 2022:
# Funeral of Queen Elizabeth II.
self._add_holiday_sep_19(tr("Funeral of Her Majesty the Queen Elizabeth II"))

if self._year >= 1931:
# Remembrance Day.
self._add_observed(self._add_remembrance_day(tr("Remembrance Day")))
Expand Down Expand Up @@ -332,10 +321,6 @@ def _add_subdiv_ns_public_holidays(self):
# Heritage Day.
self._add_holiday_3rd_mon_of_feb(tr("Heritage Day"))

if self._year == 2022:
# Funeral of Queen Elizabeth II.
self._add_holiday_sep_19(tr("Funeral of Her Majesty the Queen Elizabeth II"))

if self._year >= 1981:
# Remembrance Day.
self._add_observed(self._add_remembrance_day(tr("Remembrance Day")))
Expand Down Expand Up @@ -426,10 +411,6 @@ def _add_subdiv_pe_public_holidays(self):
if self._year >= 1879:
self._add_observed(self._canada_day)

if self._year == 2022:
# Funeral of Queen Elizabeth II.
self._add_holiday_sep_19(tr("Funeral of Her Majesty the Queen Elizabeth II"))

if self._year >= 2022:
# National Day for Truth and Reconciliation.
self._add_holiday_sep_30(tr("National Day for Truth and Reconciliation"))
Expand Down Expand Up @@ -498,10 +479,6 @@ def _add_subdiv_yt_public_holidays(self):
# Discovery Day.
self._add_holiday_3rd_mon_of_aug(tr("Discovery Day"))

if self._year == 2022:
# Funeral of Queen Elizabeth II.
self._add_holiday_sep_19(tr("Funeral of Her Majesty the Queen Elizabeth II"))

if self._year >= 2023:
# National Day for Truth and Reconciliation.
self._add_holiday_sep_30(tr("National Day for Truth and Reconciliation"))
Expand All @@ -524,3 +501,32 @@ class CA(Canada):

class CAN(Canada):
pass


class CanadaStaticHolidays:
# Funeral of Queen Elizabeth II.
queen_funeral = tr("Funeral of Her Majesty the Queen Elizabeth II")

special_bc_public_holidays = {
2022: (SEP, 19, queen_funeral),
}

special_nb_public_holidays = {
2022: (SEP, 19, queen_funeral),
}

special_nl_public_holidays = {
2022: (SEP, 19, queen_funeral),
}

special_ns_public_holidays = {
2022: (SEP, 19, queen_funeral),
}

special_pe_public_holidays = {
2022: (SEP, 19, queen_funeral),
}

special_yt_public_holidays = {
2022: (SEP, 19, queen_funeral),
}
56 changes: 33 additions & 23 deletions holidays/holiday_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -649,19 +649,22 @@ def _add_holiday(self, name: str, *args) -> Optional[date]:
self[dt] = self.tr(name)
return dt

def _add_subdiv_category_holidays(self, category: str = None):
"""Populate subdivision holidays by category."""
if self.subdiv is not None:
subdiv = self.subdiv.replace("-", "_").replace(" ", "_").lower()
method_name = f"_add_subdiv_{subdiv}{f'_{category}' if category else ''}_holidays"
def _add_subdiv_holidays(self):
"""Populate subdivision holidays."""
if self.subdiv is None:
return None

subdiv = self.subdiv.replace("-", "_").replace(" ", "_").lower()
subdiv_holidays_names = [f"_add_subdiv_{subdiv}_holidays"]
KJhellico marked this conversation as resolved.
Show resolved Hide resolved

for category in sorted(self.categories):
subdiv_holidays_names.append(f"_add_subdiv_{subdiv}_{category}_holidays")

for method_name in subdiv_holidays_names:
add_subdiv_holidays = getattr(self, method_name, None)
if add_subdiv_holidays and callable(add_subdiv_holidays):
add_subdiv_holidays()

def _add_subdiv_holidays(self):
"""Populate subdivision holidays."""
self._add_subdiv_category_holidays()

def _add_substituted_holidays(self):
"""Populate substituted holidays."""
if len(self.substituted_holidays) == 0:
Expand Down Expand Up @@ -737,36 +740,43 @@ def _populate(self, year: int) -> None:

self._year = year

# Populate items from the special holidays list.
for month, day, name in _normalize_tuple(self.special_holidays.get(year, ())):
self._add_holiday(name, date(self._year, month, day))
# Populate special holidays.
self._add_special_holidays()

# Populate categories holidays.
self._populate_categories()
# Populate non-static holidays.
self._add_non_static_holidays()

# Populate subdivision holidays.
# Populate subdivision non-static holidays.
self._add_subdiv_holidays()

# Populate substituted holidays.
self._add_substituted_holidays()

def _populate_categories(self):
def _add_special_holidays(self):
special_holidays_mapping_names = ["special_holidays"]
KJhellico marked this conversation as resolved.
Show resolved Hide resolved
if self.subdiv is not None:
KJhellico marked this conversation as resolved.
Show resolved Hide resolved
subdiv = self.subdiv.replace("-", "_").replace(" ", "_").lower()
special_holidays_mapping_names.append(f"special_{subdiv}_holidays")

KJhellico marked this conversation as resolved.
Show resolved Hide resolved
for category in sorted(self.categories):
# Populate items from the special holidays list for all categories.
special_category_holidays = getattr(self, f"special_{category}_holidays", None)
if special_category_holidays:
special_holidays_mapping_names.append(f"special_{category}_holidays")
if self.subdiv is not None:
special_holidays_mapping_names.append(f"special_{subdiv}_{category}_holidays")

for mapping_name in special_holidays_mapping_names:
special_holidays_mapping = getattr(self, mapping_name, None)
if special_holidays_mapping:
for month, day, name in _normalize_tuple(
special_category_holidays.get(self._year, ())
special_holidays_mapping.get(self._year, ())
):
self._add_holiday(name, date(self._year, month, day))

def _add_non_static_holidays(self):
KJhellico marked this conversation as resolved.
Show resolved Hide resolved
for category in sorted(self.categories):
populate_category_holidays = getattr(self, f"_populate_{category}_holidays", None)
if populate_category_holidays and callable(populate_category_holidays):
populate_category_holidays()

# Populate subdivision holidays for all categories.
self._add_subdiv_category_holidays(category)

def append(self, *args: Union[Dict[DateLike, str], List[DateLike], DateLike]) -> None:
"""Alias for :meth:`update` to mimic list type."""
return self.update(*args)
Expand Down