From 110e3cc6a7856f1064b4d27b08a96418285349b5 Mon Sep 17 00:00:00 2001 From: Arkadii Yakovets Date: Mon, 4 Sep 2023 14:35:56 -0700 Subject: [PATCH 01/21] Initialize v0.33 --- CHANGES | 5 +++++ holidays/__init__.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 05df9b91a..2bfa44803 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,8 @@ +Version 0.33 +============ + +Released ???????? ??, ???? + Version 0.32 ============ diff --git a/holidays/__init__.py b/holidays/__init__.py index f00e4eb95..5639dd652 100644 --- a/holidays/__init__.py +++ b/holidays/__init__.py @@ -16,7 +16,7 @@ from holidays.registry import EntityLoader from holidays.utils import * -__version__ = "0.32" +__version__ = "0.33" EntityLoader.load("countries", globals()) From 14a8cfa583f044b285125a46a54ccd9fb2e4a9fd Mon Sep 17 00:00:00 2001 From: ~Jhellico Date: Tue, 5 Sep 2023 20:57:06 +0300 Subject: [PATCH 02/21] Introduce ObservedHolidays class (#1444) Co-authored-by: Arkadii Yakovets --- holidays/countries/albania.py | 58 +++--- holidays/countries/angola.py | 39 ++-- holidays/countries/argentina.py | 61 ++---- holidays/countries/australia.py | 34 ++- holidays/countries/azerbaijan.py | 69 +++--- holidays/countries/belize.py | 52 ++--- holidays/countries/bolivia.py | 31 ++- holidays/countries/bosnia_and_herzegovina.py | 60 +++--- holidays/countries/botswana.py | 23 +- holidays/countries/brunei.py | 94 ++++----- holidays/countries/burkina_faso.py | 16 +- holidays/countries/burundi.py | 18 +- holidays/countries/cameroon.py | 22 +- holidays/countries/canada.py | 71 ++++--- holidays/countries/chad.py | 16 +- holidays/countries/chile.py | 36 ++-- holidays/countries/colombia.py | 45 ++-- holidays/countries/costa_rica.py | 59 +++--- holidays/countries/cuba.py | 20 +- holidays/countries/dominican_republic.py | 41 ++-- holidays/countries/ecuador.py | 93 +++++---- holidays/countries/eswatini.py | 29 ++- holidays/countries/greece.py | 24 +-- holidays/countries/guatemala.py | 35 ++-- holidays/countries/hungary.py | 51 ++--- holidays/countries/ireland.py | 24 +-- holidays/countries/isle_of_man.py | 7 +- holidays/countries/jamaica.py | 31 ++- holidays/countries/kazakhstan.py | 25 +-- holidays/countries/kenya.py | 30 ++- holidays/countries/latvia.py | 16 +- holidays/countries/malawi.py | 24 +-- holidays/countries/malaysia.py | 58 +++--- holidays/countries/marshall_islands.py | 18 +- holidays/countries/monaco.py | 15 +- holidays/countries/montenegro.py | 28 +-- holidays/countries/mozambique.py | 19 +- holidays/countries/namibia.py | 26 +-- holidays/countries/new_zealand.py | 45 ++-- holidays/countries/nigeria.py | 25 +-- holidays/countries/panama.py | 14 +- holidays/countries/saudi_arabia.py | 50 ++--- holidays/countries/serbia.py | 27 ++- holidays/countries/singapore.py | 61 +++--- holidays/countries/south_africa.py | 16 +- holidays/countries/spain.py | 76 +++---- holidays/countries/taiwan.py | 34 +-- holidays/countries/thailand.py | 208 ++++++++----------- holidays/countries/ukraine.py | 37 ++-- holidays/countries/united_kingdom.py | 54 ++--- holidays/countries/united_states.py | 94 ++++----- holidays/countries/uruguay.py | 32 ++- holidays/countries/vietnam.py | 29 +-- holidays/countries/zambia.py | 20 +- holidays/countries/zimbabwe.py | 28 ++- holidays/locale/ar/LC_MESSAGES/CA.po | 1 + holidays/locale/ar/LC_MESSAGES/UA.po | 1 + holidays/locale/bs/LC_MESSAGES/BA.po | 1 + holidays/locale/en/LC_MESSAGES/CA.po | 1 + holidays/locale/en_US/LC_MESSAGES/AO.po | 2 +- holidays/locale/en_US/LC_MESSAGES/AR.po | 1 + holidays/locale/en_US/LC_MESSAGES/BA.po | 1 + holidays/locale/en_US/LC_MESSAGES/CO.po | 1 + holidays/locale/en_US/LC_MESSAGES/CR.po | 1 + holidays/locale/en_US/LC_MESSAGES/CU.po | 1 + holidays/locale/en_US/LC_MESSAGES/EC.po | 1 + holidays/locale/en_US/LC_MESSAGES/LV.po | 1 + holidays/locale/en_US/LC_MESSAGES/MC.po | 2 + holidays/locale/en_US/LC_MESSAGES/RS.po | 1 + holidays/locale/en_US/LC_MESSAGES/TH.po | 2 +- holidays/locale/en_US/LC_MESSAGES/UA.po | 1 + holidays/locale/es/LC_MESSAGES/AR.po | 1 + holidays/locale/es/LC_MESSAGES/CO.po | 1 + holidays/locale/es/LC_MESSAGES/CR.po | 1 + holidays/locale/es/LC_MESSAGES/CU.po | 1 + holidays/locale/es/LC_MESSAGES/EC.po | 1 + holidays/locale/fr/LC_MESSAGES/CA.po | 1 + holidays/locale/fr/LC_MESSAGES/MC.po | 2 + holidays/locale/lv/LC_MESSAGES/LV.po | 1 + holidays/locale/pt_AO/LC_MESSAGES/AO.po | 2 +- holidays/locale/sr/LC_MESSAGES/BA.po | 1 + holidays/locale/sr/LC_MESSAGES/RS.po | 1 + holidays/locale/th/LC_MESSAGES/CA.po | 1 + holidays/locale/th/LC_MESSAGES/TH.po | 2 +- holidays/locale/uk/LC_MESSAGES/AO.po | 2 +- holidays/locale/uk/LC_MESSAGES/AR.po | 1 + holidays/locale/uk/LC_MESSAGES/BA.po | 1 + holidays/locale/uk/LC_MESSAGES/CO.po | 1 + holidays/locale/uk/LC_MESSAGES/CR.po | 1 + holidays/locale/uk/LC_MESSAGES/CU.po | 1 + holidays/locale/uk/LC_MESSAGES/EC.po | 1 + holidays/locale/uk/LC_MESSAGES/LV.po | 1 + holidays/locale/uk/LC_MESSAGES/MC.po | 2 + holidays/locale/uk/LC_MESSAGES/UA.po | 1 + holidays/observed_holiday_base.py | 155 ++++++++++++++ tests/countries/test_saudi_arabia.py | 1 - 96 files changed, 1169 insertions(+), 1302 deletions(-) create mode 100644 holidays/observed_holiday_base.py diff --git a/holidays/countries/albania.py b/holidays/countries/albania.py index c83ce0b8c..54214f012 100644 --- a/holidays/countries/albania.py +++ b/holidays/countries/albania.py @@ -9,21 +9,20 @@ # Website: https://github.com/dr-prodigy/python-holidays # License: MIT (see LICENSE file) -from datetime import timedelta as td - from holidays.calendars.gregorian import MAR from holidays.calendars.julian import JULIAN_CALENDAR -from holidays.groups import ChristianHolidays, IslamicHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase +from holidays.groups import ChristianHolidays, InternationalHolidays, IslamicHolidays +from holidays.observed_holiday_base import ObservedHolidayBase, SAT_SUN_TO_NEXT_WORKDAY -class Albania(HolidayBase, ChristianHolidays, InternationalHolidays, IslamicHolidays): +class Albania(ObservedHolidayBase, ChristianHolidays, InternationalHolidays, IslamicHolidays): """ References: - https://en.wikipedia.org/wiki/Public_holidays_in_Albania """ country = "AL" + observed_label = "%s (Observed)" special_holidays = { 2022: (MAR, 21, "Public Holiday"), } @@ -32,70 +31,63 @@ def __init__(self, *args, **kwargs): ChristianHolidays.__init__(self) InternationalHolidays.__init__(self) IslamicHolidays.__init__(self) - super().__init__(*args, **kwargs) + super().__init__(observed_rule=SAT_SUN_TO_NEXT_WORKDAY, *args, **kwargs) def _populate(self, year): super()._populate(year) - observed_dates = set() + dts_observed = set() # New Year's Day. name = "New Year's Day" - observed_dates.add(self._add_new_years_day(name)) - observed_dates.add(self._add_new_years_day_two(name)) + dts_observed.add(self._add_new_years_day(name)) + dts_observed.add(self._add_new_years_day_two(name)) # Summer Day. if year >= 2004: - observed_dates.add(self._add_holiday_mar_14("Summer Day")) + dts_observed.add(self._add_holiday_mar_14("Summer Day")) # Nevruz. if year >= 1996: - observed_dates.add(self._add_holiday_mar_22("Nevruz")) + dts_observed.add(self._add_holiday_mar_22("Nevruz")) # Easter. - observed_dates.add(self._add_easter_sunday("Catholic Easter")) - observed_dates.add(self._add_easter_sunday("Orthodox Easter", JULIAN_CALENDAR)) + dts_observed.add(self._add_easter_sunday("Catholic Easter")) + dts_observed.add(self._add_easter_sunday("Orthodox Easter", JULIAN_CALENDAR)) # May Day. - observed_dates.add(self._add_labor_day("May Day")) + dts_observed.add(self._add_labor_day("May Day")) # Mother Teresa Day. if 2004 <= year <= 2017: - observed_dates.add(self._add_holiday_oct_19("Mother Teresa Beatification Day")) + dts_observed.add(self._add_holiday_oct_19("Mother Teresa Beatification Day")) elif year >= 2018: - observed_dates.add(self._add_holiday_sep_5("Mother Teresa Canonization Day")) + dts_observed.add(self._add_holiday_sep_5("Mother Teresa Canonization Day")) # Independence Day. - observed_dates.add(self._add_holiday_nov_28("Independence Day")) + dts_observed.add(self._add_holiday_nov_28("Independence Day")) # Liberation Day. - observed_dates.add(self._add_holiday_nov_29("Liberation Day")) + dts_observed.add(self._add_holiday_nov_29("Liberation Day")) # National Youth Day. if year >= 2009: - observed_dates.add(self._add_holiday_dec_8("National Youth Day")) + dts_observed.add(self._add_holiday_dec_8("National Youth Day")) # Christmas Day. - observed_dates.add(self._add_christmas_day("Christmas Day")) + dts_observed.add(self._add_christmas_day("Christmas Day")) # Eid al-Fitr. - observed_dates.update(self._add_eid_al_fitr_day("Eid al-Fitr")) + dts_observed.update(self._add_eid_al_fitr_day("Eid al-Fitr")) # Eid al-Adha. - observed_dates.update(self._add_eid_al_adha_day("Eid al-Adha")) + dts_observed.update(self._add_eid_al_adha_day("Eid al-Adha")) if self.observed: - for dt in sorted(observed_dates): - if not self._is_weekend(dt): - continue - dt_observed = dt + td(days=+1) - while self._is_weekend(dt_observed) or dt_observed in observed_dates: - dt_observed += td(days=+1) - for name in self.get_list(dt): - observed_dates.add(self._add_holiday("%s (Observed)" % name, dt_observed)) - - # observed holidays special cases + self._populate_observed(dts_observed) + + # Observed holidays special cases. if year == 2007: - self._add_holiday_jan_3("Eid al-Adha (Observed)") + self._add_holiday_jan_3(self.observed_label % "Eid al-Adha") class AL(Albania): diff --git a/holidays/countries/angola.py b/holidays/countries/angola.py index 59e2975de..a718eb38b 100644 --- a/holidays/countries/angola.py +++ b/holidays/countries/angola.py @@ -10,15 +10,20 @@ # License: MIT (see LICENSE file) from datetime import date -from datetime import timedelta as td from gettext import gettext as tr +from typing import Tuple from holidays.calendars.gregorian import AUG, SEP, DEC from holidays.groups import ChristianHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ( + ObservedHolidayBase, + TUE_TO_PREV_MON, + THU_TO_NEXT_FRI, + SUN_TO_NEXT_MON, +) -class Angola(HolidayBase, ChristianHolidays, InternationalHolidays): +class Angola(ObservedHolidayBase, ChristianHolidays, InternationalHolidays): """ References: - https://en.wikipedia.org/wiki/Public_holidays_in_Angola @@ -38,6 +43,8 @@ class Angola(HolidayBase, ChristianHolidays, InternationalHolidays): country = "AO" default_language = "pt_AO" supported_languages = ("en_US", "pt_AO", "uk") + # %s (Observed). + observed_label = tr("%s (Ponte)") special_holidays = { # General Election Day. 2017: (AUG, 23, tr("Dia de eleições gerais")), @@ -46,23 +53,19 @@ class Angola(HolidayBase, ChristianHolidays, InternationalHolidays): def __init__(self, *args, **kwargs): ChristianHolidays.__init__(self) InternationalHolidays.__init__(self) - super().__init__(*args, **kwargs) + super().__init__(observed_rule=TUE_TO_PREV_MON + THU_TO_NEXT_FRI, *args, **kwargs) - def _add_observed(self, dt: date) -> None: - if not self.observed: - return None - # As per Law # #11/18, from 2018/9/10, when public holiday falls on Tuesday or Thursday, - # the Monday or Friday is also a holiday. - if dt >= date(2018, SEP, 10): - if self._is_tuesday(dt): - # Day off for %s. - self._add_holiday(self.tr("%s (Ponte)") % self[dt], dt + td(days=-1)) - elif self._is_thursday(dt): - self._add_holiday(self.tr("%s (Ponte)") % self[dt], dt + td(days=+1)) + def _is_observed(self, dt: date) -> bool: # As per Law # 16/96, from 1996/9/27, when public holiday falls on Sunday, # it rolls over to the following Monday. - elif dt >= date(1996, SEP, 27) and self._is_sunday(dt): - self._add_holiday(self.tr("%s (Ponte)") % self[dt], dt + td(days=+1)) + return dt >= date(1996, SEP, 27) + + def _add_observed(self, dt: date, *args) -> Tuple[bool, date]: + # As per Law # #11/18, from 2018/9/10, when public holiday falls on Tuesday or Thursday, + # the Monday or Friday is also a holiday. + return super()._add_observed( + dt, rule=SUN_TO_NEXT_MON if dt < date(2018, SEP, 10) else self._observed_rule + ) def _populate(self, year): # Decree #5/75. @@ -78,7 +81,7 @@ def _populate(self, year): self._add_observed(dt) if self.observed and self._is_monday(DEC, 31) and year >= 2018: - self._add_holiday(self.tr("%s (Ponte)") % name, DEC, 31) + self._add_holiday_dec_31(self.tr(self.observed_label) % name) # Law #16/96. if 1997 <= year <= 2011: diff --git a/holidays/countries/argentina.py b/holidays/countries/argentina.py index 84d2e14bd..af8a4c1b8 100644 --- a/holidays/countries/argentina.py +++ b/holidays/countries/argentina.py @@ -9,30 +9,19 @@ # Website: https://github.com/dr-prodigy/python-holidays # License: MIT (see LICENSE file) -from datetime import date from gettext import gettext as tr -from holidays.calendars.gregorian import ( - JAN, - FEB, - MAR, - APR, - MAY, - JUN, - JUL, - AUG, - SEP, - OCT, - NOV, - DEC, - MON, - _get_nth_weekday_from, -) +from holidays.calendars.gregorian import JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC from holidays.groups import ChristianHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ( + ObservedHolidayBase, + THU_TO_NEXT_MON, + TUE_WED_TO_PREV_MON, + THU_FRI_TO_NEXT_MON, +) -class Argentina(HolidayBase, ChristianHolidays, InternationalHolidays): +class Argentina(ObservedHolidayBase, ChristianHolidays, InternationalHolidays): """ A subclass of :py:class:`HolidayBase` representing public holidays in Argentina. @@ -69,11 +58,20 @@ class Argentina(HolidayBase, ChristianHolidays, InternationalHolidays): https://servicios.lanacion.com.ar/app-mobile/feriados/2017 https://servicios.lanacion.com.ar/app-mobile/feriados/2016 https://servicios.lanacion.com.ar/app-mobile/feriados/2015 + + Movable Holidays Laws: + - Decreto 1584/2010: 2010-11-03 + - AUG 17, OCT 12, NOV 20 Holidays will always be on MON + - Decreto 52/2017: 2017-01-23 (Reconfirmed in Ley 27399) + - If TUE/WED - observed on previous MON + - If THU/FRI - observed on next MON """ country = "AR" default_language = "es" supported_languages = ("en_US", "es", "uk") + # %s (Observed). + observed_label = tr("%s (Observado)") # Special Bridge Holidays are given upto 3 days a year # as long as it's declared 50 days before calendar year's end @@ -166,27 +164,7 @@ class Argentina(HolidayBase, ChristianHolidays, InternationalHolidays): def __init__(self, *args, **kwargs): ChristianHolidays.__init__(self) InternationalHolidays.__init__(self) - super().__init__(*args, **kwargs) - - def _move_holiday(self, dt: date) -> None: - """ - Movable Holidays Laws: - - Decreto 1584/2010: 2010-11-03 - - AUG 17, OCT 12, NOV 20 Holidays will always be on MON - - Decreto 52/2017: 2017-01-23 (Reconfirmed in Ley 27399) - - If TUE/WED - observed on previous MON - - If THU/FRI - observed on next MON - """ - if self.observed: - dt_observed = None - if self._is_tuesday(dt) or self._is_wednesday(dt): - dt_observed = _get_nth_weekday_from(-1, MON, dt) - elif self._is_thursday(dt) or self._is_friday(dt): - dt_observed = _get_nth_weekday_from(+1, MON, dt) - - if dt_observed: - self._add_holiday(self.tr("%s (Observado)") % self[dt], dt_observed) - self.pop(dt) + super().__init__(observed_rule=TUE_WED_TO_PREV_MON + THU_FRI_TO_NEXT_MON, *args, **kwargs) def _populate(self, year): super()._populate(year) @@ -301,8 +279,7 @@ def _populate(self, year): ) # If Jun 17 is Friday, then it should move to Mon, Jun 20 # but Jun 20 is Gen. Belgrano holiday - if not self._is_friday(jun_17): - self._move_holiday(jun_17) + self._move_holiday(jun_17, rule=TUE_WED_TO_PREV_MON + THU_TO_NEXT_MON) # Status: In-Use. # Started in 1938 via Ley 12387 on Aug 17. diff --git a/holidays/countries/australia.py b/holidays/countries/australia.py index 8e6a76d89..bbdac2b2a 100644 --- a/holidays/countries/australia.py +++ b/holidays/countries/australia.py @@ -14,16 +14,22 @@ from holidays.calendars.gregorian import APR, AUG, SEP, OCT, FRI, _get_nth_weekday_from from holidays.groups import ChristianHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ( + ObservedHolidayBase, + SUN_TO_NEXT_MON, + SAT_SUN_TO_NEXT_MON, + SAT_SUN_TO_NEXT_MON_TUE, +) -class Australia(HolidayBase, ChristianHolidays, InternationalHolidays): +class Australia(ObservedHolidayBase, ChristianHolidays, InternationalHolidays): """ References: - https://www.qld.gov.au/recreation/travel/holidays """ country = "AU" + observed_label = "%s (Observed)" special_holidays = { 2022: (SEP, 22, "National Day of Mourning for Queen Elizabeth II"), } @@ -41,15 +47,7 @@ def sovereign_birthday(self) -> str: def __init__(self, *args, **kwargs): ChristianHolidays.__init__(self) InternationalHolidays.__init__(self) - super().__init__(*args, **kwargs) - - def _add_observed(self, dt: date, include_sat: bool = True, days: int = +1) -> None: - if not self.observed: - return None - if self._is_sunday(dt) or (include_sat and self._is_saturday(dt)): - self._add_holiday( - "%s (Observed)" % self[dt], dt + td(days=+2 if self._is_saturday(dt) else days) - ) + super().__init__(observed_rule=SAT_SUN_TO_NEXT_MON, *args, **kwargs) def _populate(self, year): super()._populate(year) @@ -98,11 +96,11 @@ def _add_subdiv_holidays(self): self._add_holiday_apr_25("Anzac Day") # Christmas Day - self._add_observed(self._add_christmas_day("Christmas Day"), days=+2) + self._add_observed(self._add_christmas_day("Christmas Day"), rule=SAT_SUN_TO_NEXT_MON_TUE) # Boxing Day name = "Proclamation Day" if self.subdiv == "SA" else "Boxing Day" - self._add_observed(self._add_christmas_day_two(name), days=+2) + self._add_observed(self._add_christmas_day_two(name), rule=SAT_SUN_TO_NEXT_MON_TUE) super()._add_subdiv_holidays() @@ -120,7 +118,7 @@ def _add_subdiv_act_holidays(self): # Anzac Day if self._year >= 1921: - self._add_observed(date(self._year, APR, 25), include_sat=False) + self._add_observed(date(self._year, APR, 25), rule=SUN_TO_NEXT_MON) # Canberra Day # Info from https://www.timeanddate.com/holidays/australia/canberra-day @@ -217,15 +215,15 @@ def _add_subdiv_qld_holidays(self): # Anzac Day if self._year >= 1921: - self._add_observed(date(self._year, APR, 25), include_sat=False) + self._add_observed((APR, 25), rule=SUN_TO_NEXT_MON) # The Royal Queensland Show (Ekka) # The Show starts on the first Friday of August - providing this is # not prior to the 5th - in which case it will begin on the second # Friday. The Wednesday during the show is a public holiday. ekka_dates = { - 2020: date(2020, AUG, 14), - 2021: date(2021, OCT, 29), + 2020: (AUG, 14), + 2021: (OCT, 29), } name = "The Royal Queensland Show" if self._year in ekka_dates: @@ -248,7 +246,7 @@ def _add_subdiv_sa_holidays(self): # Anzac Day if self._year >= 1921: - self._add_observed(date(self._year, APR, 25), include_sat=False) + self._add_observed((APR, 25), rule=SUN_TO_NEXT_MON) # Adelaide Cup name = "Adelaide Cup" diff --git a/holidays/countries/azerbaijan.py b/holidays/countries/azerbaijan.py index 438961915..18ab57764 100644 --- a/holidays/countries/azerbaijan.py +++ b/holidays/countries/azerbaijan.py @@ -10,48 +10,40 @@ # License: MIT (see LICENSE file) from datetime import date -from datetime import timedelta as td from holidays.calendars import _CustomIslamicCalendar from holidays.calendars.gregorian import JAN, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC from holidays.groups import InternationalHolidays, IslamicHolidays -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ( + ObservedHolidayBase, + WORKDAY_TO_NEXT_WORKDAY, + SAT_SUN_TO_NEXT_WORKDAY, +) -class Azerbaijan(HolidayBase, InternationalHolidays, IslamicHolidays): +class Azerbaijan(ObservedHolidayBase, InternationalHolidays, IslamicHolidays): # [1] https://en.wikipedia.org/wiki/Public_holidays_in_Azerbaijan # [2] https://az.wikipedia.org/wiki/Az%C9%99rbaycan%C4%B1n_d%C3%B6vl%C9%99t_bayramlar%C4%B1_v%C9%99_x%C3%BCsusi_g%C3%BCnl%C9%99ri # noqa: E501 # [3] https://www.sosial.gov.az/en/prod-calendar country = "AZ" + observed_label = "%s (Observed)" def __init__(self, *args, **kwargs): InternationalHolidays.__init__(self) IslamicHolidays.__init__(self, calendar=AzerbaijanIslamicCalendar()) - super().__init__(*args, **kwargs) + super().__init__( + observed_rule=SAT_SUN_TO_NEXT_WORKDAY, observed_since=2006, *args, **kwargs + ) def _populate(self, year): - def _add_observed(dt: date, name: str = None): - """ - Add observed holiday on next working day after specified date. - """ - - next_workday = dt + td(days=+1) - while next_workday in dts_all or self._is_weekend(next_workday): - next_workday += td(days=+1) - if name: - self._add_holiday(f"{name} (Observed)", next_workday) - else: - for h_name in self.get_list(dt): - self._add_holiday(f"{h_name} (Observed)", next_workday) - dts_all.add(next_workday) - if year <= 1989: return None super()._populate(year) dts_observed = set() dts_non_observed = set() + dts_bairami = set() # New Year name = "New Year's Day" @@ -112,47 +104,44 @@ def _add_observed(dt: date, name: str = None): if year >= 1993: solidarity_name = "International Solidarity Day of Azerbaijanis" self._add_new_years_eve(solidarity_name) + self._add_observed(date(year - 1, DEC, 31), name=solidarity_name) if year >= 1993: name = "Ramazan Bayrami" - dts_observed.update(self._add_eid_al_fitr_day(name)) - dts_observed.update(self._add_eid_al_fitr_day_two(name)) + dts_bairami.update(self._add_eid_al_fitr_day(name)) + dts_bairami.update(self._add_eid_al_fitr_day_two(name)) name = "Gurban Bayrami" - dts_observed.update(self._add_eid_al_adha_day(name)) - dts_observed.update(self._add_eid_al_adha_day_two(name)) + dts_bairami.update(self._add_eid_al_adha_day(name)) + dts_bairami.update(self._add_eid_al_adha_day_two(name)) # Article 105 of the Labor Code of the Republic of Azerbaijan states: # 5. If interweekly rest days and holidays that are not considered # working days overlap, that rest day is immediately transferred to # the next working day. if self.observed and year >= 2006: - dts_all = dts_observed.union(dts_non_observed) - - dt = date(year - 1, DEC, 31) - if self._is_weekend(dt): - _add_observed(dt, solidarity_name) - # observed holidays special cases special_dates_obs = {2007: (JAN, 3), 2072: (JAN, 5)} if year in special_dates_obs: - dts_all.add( - self._add_holiday( - "Gurban Bayrami* (*estimated) (Observed)", special_dates_obs[year] - ) + self._add_holiday( + "Gurban Bayrami* (*estimated) (Observed)", special_dates_obs[year] ) - for dt_observed in sorted(dts_observed): - if self._is_weekend(dt_observed): - _add_observed(dt_observed) + self._populate_observed(dts_observed.union(dts_bairami)) + for dt_observed in sorted(dts_bairami.difference(dts_non_observed)): # 6. If the holidays of Qurban and Ramadan coincide with # another holiday that is not considered a working day, # the next working day is considered a rest day. - elif len(self.get_list(dt_observed)) > 1 and dt_observed not in dts_non_observed: - for name in self.get_list(dt_observed): - if "Bayrami" in name: - _add_observed(dt_observed, name) + if len(self.get_list(dt_observed)) == 1: + continue + for name in self.get_list(dt_observed): + if "Bayrami" in name: + self._add_observed( + dt_observed, + name=name, + rule=WORKDAY_TO_NEXT_WORKDAY, + ) class AZ(Azerbaijan): diff --git a/holidays/countries/belize.py b/holidays/countries/belize.py index 36fee74b6..708cb3ec3 100644 --- a/holidays/countries/belize.py +++ b/holidays/countries/belize.py @@ -9,14 +9,16 @@ # Website: https://github.com/dr-prodigy/python-holidays # License: MIT (see LICENSE file) -from datetime import date - -from holidays.calendars.gregorian import MON, _get_nth_weekday_from from holidays.groups import ChristianHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ( + ObservedHolidayBase, + SUN_TO_NEXT_MON, + TUE_WED_THU_TO_PREV_MON, + FRI_SUN_TO_NEXT_MON, +) -class Belize(HolidayBase, ChristianHolidays, InternationalHolidays): +class Belize(ObservedHolidayBase, ChristianHolidays, InternationalHolidays): """ References: - https://en.wikipedia.org/wiki/Public_holidays_in_Belize @@ -26,33 +28,16 @@ class Belize(HolidayBase, ChristianHolidays, InternationalHolidays): """ country = "BZ" + observed_label = "%s (Observed)" - def __init__(self, *args, **kwargs) -> None: + def __init__(self, *args, **kwargs): ChristianHolidays.__init__(self) InternationalHolidays.__init__(self) - super().__init__(*args, **kwargs) - - def _move_holiday(self, dt: date, sunday_only: bool = True) -> None: # Chapter 289 of the laws of Belize states that if the holiday falls # on a Sunday or a Friday, the following Monday is observed as public # holiday; further, if the holiday falls on a Tuesday, Wednesday or # Thursday, the preceding Monday is observed as public holiday - if not self.observed: - return None - - dt_observed = None - if sunday_only: - if self._is_sunday(dt): - dt_observed = _get_nth_weekday_from(+1, MON, dt) - else: - if self._is_friday(dt) or self._is_sunday(dt): - dt_observed = _get_nth_weekday_from(+1, MON, dt) - elif self._is_tuesday(dt) or self._is_wednesday(dt) or self._is_thursday(dt): - dt_observed = _get_nth_weekday_from(-1, MON, dt) - - if dt_observed: - self._add_holiday("%s (Observed)" % self[dt], dt_observed) - self.pop(dt) + super().__init__(observed_rule=SUN_TO_NEXT_MON, *args, **kwargs) def _populate(self, year): # Belize was granted independence on 21.09.1981. @@ -69,7 +54,8 @@ def _populate(self, year): # National Heroes and Benefactors Day. self._move_holiday( - self._add_holiday_mar_9("National Heroes and Benefactors Day"), sunday_only=False + self._add_holiday_mar_9("National Heroes and Benefactors Day"), + rule=TUE_WED_THU_TO_PREV_MON + FRI_SUN_TO_NEXT_MON, ) # Good Friday. @@ -86,11 +72,17 @@ def _populate(self, year): if year <= 2021: # Commonwealth Day. - self._move_holiday(self._add_holiday_may_24("Commonwealth Day"), sunday_only=False) + self._move_holiday( + self._add_holiday_may_24("Commonwealth Day"), + rule=TUE_WED_THU_TO_PREV_MON + FRI_SUN_TO_NEXT_MON, + ) if year >= 2021: # Emancipation Day. - self._move_holiday(self._add_holiday_aug_1("Emancipation Day"), sunday_only=False) + self._move_holiday( + self._add_holiday_aug_1("Emancipation Day"), + rule=TUE_WED_THU_TO_PREV_MON + FRI_SUN_TO_NEXT_MON, + ) # Saint George's Caye Day. self._move_holiday(self._add_holiday_sep_10("Saint George's Caye Day")) @@ -100,7 +92,9 @@ def _populate(self, year): # Indigenous Peoples' Resistance Day / Pan American Day. name = "Indigenous Peoples' Resistance Day" if year >= 2021 else "Pan American Day" - self._move_holiday(self._add_columbus_day(name), sunday_only=False) + self._move_holiday( + self._add_columbus_day(name), rule=TUE_WED_THU_TO_PREV_MON + FRI_SUN_TO_NEXT_MON + ) # Garifuna Settlement Day. self._move_holiday(self._add_holiday_nov_19("Garifuna Settlement Day")) diff --git a/holidays/countries/bolivia.py b/holidays/countries/bolivia.py index a1435fe10..42ae9ce34 100644 --- a/holidays/countries/bolivia.py +++ b/holidays/countries/bolivia.py @@ -10,15 +10,19 @@ # License: MIT (see LICENSE file) # Copyright: Kateryna Golovanova , 2022 -from datetime import date from datetime import timedelta as td from gettext import gettext as tr from holidays.groups import ChristianHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ( + ObservedHolidayBase, + TUE_TO_PREV_MON, + THU_TO_NEXT_FRI, + SUN_TO_NEXT_MON, +) -class Bolivia(HolidayBase, ChristianHolidays, InternationalHolidays): +class Bolivia(ObservedHolidayBase, ChristianHolidays, InternationalHolidays): """ References: - [Supreme Decree #14260] https://bolivia.infoleyes.com/norma/1141/decreto-supremo-14260 @@ -49,17 +53,11 @@ class Bolivia(HolidayBase, ChristianHolidays, InternationalHolidays): "T", # Tarija ) - def __init__(self, *args, **kwargs) -> None: + def __init__(self, *args, **kwargs): ChristianHolidays.__init__(self) InternationalHolidays.__init__(self) - super().__init__(*args, **kwargs) - - def _add_observed(self, dt: date) -> None: # Supreme Decree #14260. - # whenever a public holiday falls on a Sunday, - # it rolls over to the following Monday. - if self.observed and self._year >= 1977 and self._is_sunday(dt): - self._add_holiday(self.tr(self.observed_label) % self[dt], dt + td(days=+1)) + super().__init__(observed_rule=SUN_TO_NEXT_MON, observed_since=1977, *args, **kwargs) def _populate(self, year): if year <= 1824: @@ -88,15 +86,10 @@ def _populate(self, year): self._add_good_friday(tr("Viernes Santo")) # Labor Day. - name = self.tr("Día del Trabajo") - may_1 = self._add_labor_day(name) - self._add_observed(may_1) + self._add_observed(may_1 := self._add_labor_day(self.tr("Día del Trabajo"))) # Supreme Decree #1210. - if self.observed and 2012 <= year <= 2015: - if self._is_tuesday(may_1): - self._add_holiday(self.tr(self.observed_label) % name, may_1 + td(days=-1)) - elif self._is_thursday(may_1): - self._add_holiday(self.tr(self.observed_label) % name, may_1 + td(days=+1)) + if 2012 <= year <= 2015: + self._add_observed(may_1, rule=TUE_TO_PREV_MON + THU_TO_NEXT_FRI) # Corpus Christi. self._add_corpus_christi_day(tr("Corpus Christi")) diff --git a/holidays/countries/bosnia_and_herzegovina.py b/holidays/countries/bosnia_and_herzegovina.py index 1ce97690f..d9e310047 100644 --- a/holidays/countries/bosnia_and_herzegovina.py +++ b/holidays/countries/bosnia_and_herzegovina.py @@ -10,8 +10,6 @@ # License: MIT (see LICENSE file) # Copyright: Kateryna Golovanova , 2022 -from datetime import date -from datetime import timedelta as td from gettext import gettext as tr from holidays.calendars import _CustomIslamicCalendar @@ -32,20 +30,35 @@ ) from holidays.calendars.julian import JULIAN_CALENDAR from holidays.groups import ChristianHolidays, IslamicHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ( + ObservedHolidayBase, + SAT_TO_NEXT_MON, + SUN_TO_NEXT_MON, + SUN_TO_NEXT_TUE, + SAT_SUN_TO_NEXT_MON_TUE, +) -class BosniaAndHerzegovina(HolidayBase, ChristianHolidays, InternationalHolidays, IslamicHolidays): +class BosniaAndHerzegovina( + ObservedHolidayBase, ChristianHolidays, InternationalHolidays, IslamicHolidays +): """ https://en.wikipedia.org/wiki/Public_holidays_in_Bosnia_and_Herzegovina https://www.paragraf.ba/neradni-dani-fbih.html https://www.paragraf.ba/neradni-dani-republike-srpske.html https://www.paragraf.ba/neradni-dani-brcko.html + + Observed holidays rules: + - BIH: if first day of New Year's Day and Labor Day fall on Sunday, observed on Tuesday. + - BRC: if holiday fall on Sunday, observed on next working day. + - SRP: if second day of New Year's Day and Labor Day fall on Sunday, observed on Monday. """ country = "BA" default_language = "bs" supported_languages = ("bs", "en_US", "sr", "uk") + # %s (Observed). + observed_label = tr("%s (preneseno)") subdivisions = ( "BIH", # Federacija Bosne i Hercegovine "BRC", # Brčko distrikt @@ -61,21 +74,7 @@ def __init__(self, *args, **kwargs): ChristianHolidays.__init__(self, JULIAN_CALENDAR) InternationalHolidays.__init__(self) IslamicHolidays.__init__(self, calendar=BosniaAndHerzegovinaIslamicCalendar()) - super().__init__(*args, **kwargs) - - def _add_observed( - self, dt: date, include_sat: bool = True, include_sun: bool = True, days: int = +1 - ) -> None: - # BIH: if first day of New Year's Day and Labor Day fall on Sunday, observed on Tuesday. - # BRC: if holiday fall on Sunday, observed on next working day. - # SRP: if second day of New Year's Day and Labor Day fall on Sunday, observed on Monday. - if not self.observed: - return None - if (include_sun and self._is_sunday(dt)) or (include_sat and self._is_saturday(dt)): - self._add_holiday( - self.tr("%s (preneseno)") % self[dt], - dt + td(days=+2 if self._is_saturday(dt) else days), - ) + super().__init__(observed_rule=SUN_TO_NEXT_MON, *args, **kwargs) def _populate(self, year): super()._populate(year) @@ -122,7 +121,7 @@ def _add_subdiv_holidays(self): def _add_subdiv_bih_holidays(self): # New Year's Day. name = tr("Nova godina") - self._add_observed(self._add_new_years_day(name), include_sat=False, days=+2) + self._add_observed(self._add_new_years_day(name), rule=SUN_TO_NEXT_TUE) self._add_new_years_day_two(name) # Orthodox Christmas Eve. @@ -148,7 +147,7 @@ def _add_subdiv_bih_holidays(self): # Labor Day. name = tr("Međunarodni praznik rada") - self._add_observed(self._add_labor_day(name), include_sat=False, days=+2) + self._add_observed(self._add_labor_day(name), rule=SUN_TO_NEXT_TUE) self._add_labor_day_two(name) # Victory Day. @@ -172,36 +171,31 @@ def _add_subdiv_bih_holidays(self): def _add_subdiv_brc_holidays(self): # New Year's Day. name = tr("Nova godina") - self._add_observed(self._add_new_years_day(name), days=+2) + self._add_observed(self._add_new_years_day(name), rule=SAT_SUN_TO_NEXT_MON_TUE) self._add_new_years_day_two(name) # Orthodox Christmas. - self._add_observed( - self._add_christmas_day(tr("Božić (Pravoslavni)")), - include_sat=False, - ) + self._add_observed(self._add_christmas_day(tr("Božić (Pravoslavni)"))) self._add_observed( # Day of establishment of Brčko District. - self._add_holiday_mar_8(tr("Dan uspostavljanja Brčko distrikta")), - include_sat=False, + self._add_holiday_mar_8(tr("Dan uspostavljanja Brčko distrikta")) ) # Labor Day. name = tr("Međunarodni praznik rada") - self._add_observed(self._add_labor_day(name), days=+2) + self._add_observed(self._add_labor_day(name), rule=SAT_SUN_TO_NEXT_MON_TUE) self._add_labor_day_two(name) self._add_observed( # Catholic Christmas. - self._add_christmas_day(tr("Božić (Katolički)"), GREGORIAN_CALENDAR), - include_sat=False, + self._add_christmas_day(tr("Božić (Katolički)"), GREGORIAN_CALENDAR) ) def _add_subdiv_srp_holidays(self): # New Year's Day. name = tr("Nova godina") - self._add_observed(self._add_new_years_day(name), include_sun=False) + self._add_observed(self._add_new_years_day(name), rule=SAT_TO_NEXT_MON) self._add_new_years_day_two(name) # Orthodox Christmas Eve. @@ -227,7 +221,7 @@ def _add_subdiv_srp_holidays(self): # Labor Day. name = tr("Međunarodni praznik rada") - self._add_observed(self._add_labor_day(name), include_sun=False) + self._add_observed(self._add_labor_day(name), rule=SAT_TO_NEXT_MON) self._add_labor_day_two(name) # Victory Day. diff --git a/holidays/countries/botswana.py b/holidays/countries/botswana.py index 88e068822..4868b5319 100644 --- a/holidays/countries/botswana.py +++ b/holidays/countries/botswana.py @@ -9,15 +9,14 @@ # Website: https://github.com/dr-prodigy/python-holidays # License: MIT (see LICENSE file) -from datetime import date from datetime import timedelta as td from holidays.calendars.gregorian import JUL from holidays.groups import ChristianHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ObservedHolidayBase, SUN_TO_NEXT_MON, SUN_TO_NEXT_TUE -class Botswana(HolidayBase, ChristianHolidays, InternationalHolidays): +class Botswana(ObservedHolidayBase, ChristianHolidays, InternationalHolidays): """ https://www.gov.bw/public-holidays https://publicholidays.africa/botswana/2021-dates/ @@ -26,16 +25,13 @@ class Botswana(HolidayBase, ChristianHolidays, InternationalHolidays): """ country = "BW" + observed_label = "%s (Observed)" special_holidays = {2019: (JUL, 2, "Public Holiday")} - def __init__(self, *args, **kwargs) -> None: + def __init__(self, *args, **kwargs): ChristianHolidays.__init__(self) InternationalHolidays.__init__(self) - super().__init__(*args, **kwargs) - - def _add_observed(self, dt: date, days: int = +1) -> None: - if self.observed and self._is_sunday(dt) and self._year >= 1995: - self._add_holiday("%s (Observed)" % self[dt], dt + td(days=days)) + super().__init__(observed_rule=SUN_TO_NEXT_MON, observed_since=1995, *args, **kwargs) def _populate(self, year): if year <= 1965: @@ -43,7 +39,7 @@ def _populate(self, year): super()._populate(year) - self._add_observed(self._add_new_years_day("New Year's Day"), days=+2) + self._add_observed(self._add_new_years_day("New Year's Day"), rule=SUN_TO_NEXT_TUE) self._add_observed(self._add_new_years_day_two("New Year's Day Holiday")) # Easter and easter related calculations @@ -63,12 +59,11 @@ def _populate(self, year): self._add_holiday("President's Day Holiday", third_mon_of_jul + td(days=+1)) sep_30 = self._add_holiday_sep_30("Botswana Day") - self._add_observed(sep_30, days=+2) + self._add_observed(sep_30, rule=SUN_TO_NEXT_TUE) self._add_observed(self._add_holiday("Botswana Day Holiday", sep_30 + td(days=+1))) - self._add_observed(self._add_christmas_day("Christmas Day"), days=+2) - dec_26 = self._add_christmas_day_two("Boxing Day") - self._add_observed(dec_26) + self._add_observed(self._add_christmas_day("Christmas Day"), rule=SUN_TO_NEXT_TUE) + self._add_observed(dec_26 := self._add_christmas_day_two("Boxing Day")) if self.observed and year >= 2016 and self._is_saturday(dec_26): self._add_holiday("Boxing Day Holiday", dec_26 + td(days=+2)) diff --git a/holidays/countries/brunei.py b/holidays/countries/brunei.py index 8c6ca9f79..e93d98260 100644 --- a/holidays/countries/brunei.py +++ b/holidays/countries/brunei.py @@ -9,8 +9,6 @@ # Website: https://github.com/dr-prodigy/python-holidays # License: MIT (see LICENSE file) -from datetime import date -from datetime import timedelta as td from gettext import gettext as tr from holidays.calendars import _CustomIslamicCalendar @@ -21,11 +19,22 @@ InternationalHolidays, IslamicHolidays, ) -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ( + ObservedHolidayBase, + FRI_TO_NEXT_MON, + FRI_TO_NEXT_SAT, + SUN_TO_NEXT_TUE, + SUN_TO_NEXT_WED, + FRI_SUN_TO_NEXT_SAT_MON, +) class Brunei( - HolidayBase, ChineseCalendarHolidays, ChristianHolidays, InternationalHolidays, IslamicHolidays + ObservedHolidayBase, + ChineseCalendarHolidays, + ChristianHolidays, + InternationalHolidays, + IslamicHolidays, ): """ A subclass of :py:class:`HolidayBase` representing public holidays in Brunei Darussalam. @@ -40,6 +49,9 @@ class Brunei( - [Jubli Emas Sultan Hassanal Bolkiah] https://www.brudirect.com/news.php?id=28316 + If Public Holiday falls on either Friday or Sunday, in-lieu observance is given out + on the following Saturday or Monday. + Limitations: - Brunei Darussalam holidays only works from 1984 onwards @@ -54,6 +66,7 @@ class Brunei( country = "BN" default_language = "ms" estimated_label = tr("%s* (*anggaran)") + observed_label = tr("%s - Diperhatikan") supported_languages = ("en_US", "ms", "th") special_holidays = { @@ -66,50 +79,40 @@ def __init__(self, *args, **kwargs): ChristianHolidays.__init__(self) InternationalHolidays.__init__(self) IslamicHolidays.__init__(self, calendar=BruneiIslamicCalendar()) - - super().__init__(*args, **kwargs) + super().__init__(observed_rule=FRI_SUN_TO_NEXT_SAT_MON, *args, **kwargs) def _populate(self, year): # Available post-Independence from 1984 afterwards if year <= 1983: return None - def _add_observed(dt: date) -> None: - """ - If Public Holiday falls on either Friday or Sunday, in-lieu - observance is given out on the following Saturday or Monday. - """ - if self.observed and (self._is_friday(dt) or self._is_sunday(dt)): - for name in self.get_list(dt): - self._add_holiday(self.tr("%s - Diperhatikan") % name, dt + td(days=+1)) - super()._populate(year) # Awal Tahun Masihi # Status: In-Use. # New Year's Day - _add_observed(self._add_new_years_day(tr("Awal Tahun Masihi"))) + self._add_observed(self._add_new_years_day(tr("Awal Tahun Masihi"))) # Tahun Baru Cina # Status: In-Use. # Lunar New Year - _add_observed(self._add_chinese_new_years_day(tr("Tahun Baru Cina"))) + self._add_observed(self._add_chinese_new_years_day(tr("Tahun Baru Cina"))) # Hari Kebangsaan # Status: In-Use. # Starts in 1984. # National Day - _add_observed(self._add_holiday_feb_23(tr("Hari Kebangsaan"))) + self._add_observed(self._add_holiday_feb_23(tr("Hari Kebangsaan"))) # Hari Angkatan Bersenjata Diraja Brunei # Status: In-Use. # Starts in 1984. # Commemorates the formation of Royal Brunei Malay Regiment in 1961. - _add_observed( + self._add_observed( # Armed Forces Day self._add_holiday_may_31(tr("Hari Angkatan Bersenjata Diraja Brunei")) ) @@ -118,7 +121,7 @@ def _add_observed(dt: date) -> None: # Status: In-Use. # Started in 1968. - _add_observed( + self._add_observed( # Sultan Hassanal Bolkiah's Birthday self._add_holiday_jul_15(tr("Hari Keputeraan KDYMM Sultan Brunei")) ) @@ -127,7 +130,7 @@ def _add_observed(dt: date) -> None: # Status: In-Use. # Christmas Day - _add_observed(self._add_christmas_day(tr("Hari Natal"))) + self._add_observed(self._add_christmas_day(tr("Hari Natal"))) # Islamic Holidays are placed after Gregorian holidays to prevent # the duplication of observed tags. - see #1168 @@ -137,76 +140,61 @@ def _add_observed(dt: date) -> None: # Isra Mi'raj for dt in self._add_isra_and_miraj_day(tr("Israk dan Mikraj")): - _add_observed(dt) + self._add_observed(dt) # Hari Pertama Berpuasa # Status: In-Use. # First Day of Ramadan for dt in self._add_ramadan_beginning_day(tr("Hari Pertama Berpuasa")): - _add_observed(dt) + self._add_observed(dt) # Hari Nuzul Al-Quran # Status: In-Use. # Anniversary of the revelation of the Quran for dt in self._add_nuzul_al_quran_day(tr("Hari Nuzul Al-Quran")): - _add_observed(dt) + self._add_observed(dt) # Hari Raya Aidil Fitri # Status: In-Use. # This is celebrate for three days in Brunei. # Observed as 'Hari Raya Puasa' and only for 2 days at certain point. - # We utilizes a separate in-lieu trigger for this one. - # 1: If Wed-Thu-Fri -> Sat (+3) - # 2: If Thu-Fri-Sat -> Mon (+4) - # 3: If Fri-Sat-Sun -> Mon (+3) - # 4: If Sat-Sun-Mon -> Tue (+3) - # 5: If Sun-Mon-Tue -> Wed (+3) + # 1: If Wed-Thu-Fri -> Sat (3rd Day +1) + # 2: If Thu-Fri-Sat -> Mon (2nd Day +3) + # 3: If Fri-Sat-Sun -> Mon (1st Day +3) + # 4: If Sat-Sun-Mon -> Tue (2nd Day +2) + # 5: If Sun-Mon-Tue -> Wed (1st Day +3) # Eid al-Fitr name = tr("Hari Raya Aidil Fitri") - - al_fitr_dates = self._add_eid_al_fitr_day(name) - self._add_eid_al_fitr_day_two(name) - self._add_eid_al_fitr_day_three(name) - - if self.observed: - for dt in al_fitr_dates: - dt_observed = None - for delta in range(3): - dt_delta = dt + td(days=delta) - if self._is_friday(dt_delta) or self._is_sunday(dt_delta): - dt_observed = dt + td(days=+3) - if self._is_sunday(dt_observed): - dt_observed += td(days=+1) - break - if dt_observed: - self._add_islamic_calendar_holiday( - self.tr("%s - Diperhatikan") % self[dt_delta], - ((dt_observed, year not in BruneiIslamicCalendar.EID_AL_FITR_DATES),), - ) + for dt in self._add_eid_al_fitr_day(name): + self._add_observed(dt, rule=FRI_TO_NEXT_MON + SUN_TO_NEXT_WED) + for dt in self._add_eid_al_fitr_day_two(name): + self._add_observed(dt, rule=FRI_TO_NEXT_MON + SUN_TO_NEXT_TUE) + for dt in self._add_eid_al_fitr_day_three(name): + self._add_observed(dt, rule=FRI_TO_NEXT_SAT) # Hari Raya Aidil Adha # Status: In-Use. # Eid al-Adha for dt in self._add_eid_al_adha_day(tr("Hari Raya Aidil Adha")): - _add_observed(dt) + self._add_observed(dt) # Awal Tahun Hijrah # Status: In-Use. # Islamic New Year for dt in self._add_islamic_new_year_day(tr("Awal Tahun Hijrah")): - _add_observed(dt) + self._add_observed(dt) # Maulidur Rasul # Status: In-Use. # Birth of the Prophet for dt in self._add_mawlid_day(tr("Maulidur Rasul")): - _add_observed(dt) + self._add_observed(dt) class BN(Brunei): diff --git a/holidays/countries/burkina_faso.py b/holidays/countries/burkina_faso.py index a76ac669d..bfcf3ee8e 100644 --- a/holidays/countries/burkina_faso.py +++ b/holidays/countries/burkina_faso.py @@ -9,32 +9,26 @@ # Website: https://github.com/dr-prodigy/python-holidays # License: MIT (see LICENSE file) -from datetime import date -from datetime import timedelta as td - from holidays.calendars import _CustomIslamicCalendar from holidays.calendars.gregorian import JAN, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC from holidays.groups import ChristianHolidays, InternationalHolidays, IslamicHolidays -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ObservedHolidayBase, SUN_TO_NEXT_MON -class BurkinaFaso(HolidayBase, ChristianHolidays, InternationalHolidays, IslamicHolidays): +class BurkinaFaso(ObservedHolidayBase, ChristianHolidays, InternationalHolidays, IslamicHolidays): """ References: - https://en.wikipedia.org/wiki/Public_holidays_in_Burkina_Faso """ country = "BF" + observed_label = "%s (Observed)" - def __init__(self, *args, **kwargs) -> None: + def __init__(self, *args, **kwargs): ChristianHolidays.__init__(self) InternationalHolidays.__init__(self) IslamicHolidays.__init__(self, calendar=BurkinaFasoIslamicCalendar()) - super().__init__(*args, **kwargs) - - def _add_observed(self, dt: date) -> None: - if self.observed and self._is_sunday(dt): - self._add_holiday("%s (Observed)" % self[dt], dt + td(days=+1)) + super().__init__(observed_rule=SUN_TO_NEXT_MON, *args, **kwargs) def _populate(self, year): # On 5 August 1960, Burkina Faso (Republic of Upper Volta at that time) diff --git a/holidays/countries/burundi.py b/holidays/countries/burundi.py index 6429c0c7c..2f14b08b6 100644 --- a/holidays/countries/burundi.py +++ b/holidays/countries/burundi.py @@ -10,14 +10,13 @@ # License: MIT (see LICENSE file) from datetime import date -from datetime import timedelta as td from typing import Optional from holidays.groups import ChristianHolidays, IslamicHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ObservedHolidayBase, SUN_TO_NEXT_MON -class Burundi(HolidayBase, ChristianHolidays, InternationalHolidays, IslamicHolidays): +class Burundi(ObservedHolidayBase, ChristianHolidays, InternationalHolidays, IslamicHolidays): """ Burundian holidays Note that holidays falling on a sunday maybe observed @@ -30,21 +29,18 @@ class Burundi(HolidayBase, ChristianHolidays, InternationalHolidays, IslamicHoli """ country = "BI" + observed_label = "%s (Observed)" def __init__(self, *args, **kwargs): ChristianHolidays.__init__(self) InternationalHolidays.__init__(self) IslamicHolidays.__init__(self) - super().__init__(*args, **kwargs) + super().__init__(observed_rule=SUN_TO_NEXT_MON, *args, **kwargs) def _add_holiday(self, name: str, *args) -> Optional[date]: - dt = args if len(args) > 1 else args[0] - dt = dt if isinstance(dt, date) else date(self._year, *dt) - - dt_added = super()._add_holiday(name, dt) - if self.observed and dt_added and self._is_sunday(dt_added): - dt_added = super()._add_holiday("%s (Observed)" % self[dt], dt + td(days=+1)) - + dt_added = super()._add_holiday(name, *args) + if dt_added: + self._add_observed(dt_added) return dt_added def _populate(self, year): diff --git a/holidays/countries/cameroon.py b/holidays/countries/cameroon.py index cfdbef817..d83bcd7e7 100644 --- a/holidays/countries/cameroon.py +++ b/holidays/countries/cameroon.py @@ -9,15 +9,13 @@ # Website: https://github.com/dr-prodigy/python-holidays # License: MIT (see LICENSE file) -from datetime import timedelta as td - from holidays.calendars import _CustomIslamicCalendar from holidays.calendars.gregorian import JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC from holidays.groups import ChristianHolidays, InternationalHolidays, IslamicHolidays -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ObservedHolidayBase, SUN_TO_NEXT_WORKDAY -class Cameroon(HolidayBase, ChristianHolidays, InternationalHolidays, IslamicHolidays): +class Cameroon(ObservedHolidayBase, ChristianHolidays, InternationalHolidays, IslamicHolidays): """ References: - https://en.wikipedia.org/wiki/Public_holidays_in_Cameroon @@ -26,6 +24,7 @@ class Cameroon(HolidayBase, ChristianHolidays, InternationalHolidays, IslamicHol """ country = "CM" + observed_label = "%s (Observed)" special_holidays = { 2021: ( (MAY, 14, "Public Holiday"), @@ -33,11 +32,11 @@ class Cameroon(HolidayBase, ChristianHolidays, InternationalHolidays, IslamicHol ), } - def __init__(self, *args, **kwargs) -> None: + def __init__(self, *args, **kwargs): ChristianHolidays.__init__(self) InternationalHolidays.__init__(self) IslamicHolidays.__init__(self, calendar=CameroonIslamicCalendar()) - super().__init__(*args, **kwargs) + super().__init__(observed_rule=SUN_TO_NEXT_WORKDAY, *args, **kwargs) def _populate(self, year): # On 1 January 1960, French Cameroun gained independence from France. @@ -83,18 +82,11 @@ def _populate(self, year): dts_observed.update(self._add_mawlid_day("Mawlid")) if self.observed: - for dt in sorted(dts_observed): - if not self._is_sunday(dt): - continue - dt_observed = dt + td(days=+1) - while dt_observed in dts_observed: - dt_observed += td(days=+1) - for name in self.get_list(dt): - dts_observed.add(self._add_holiday("%s (Observed)" % name, dt_observed)) + self._populate_observed(dts_observed) # Observed holidays special cases. if year == 2007: - self._add_holiday_jan_2("Eid al-Adha (Observed)") + self._add_holiday_jan_2(self.observed_label % "Eid al-Adha") class CM(Cameroon): diff --git a/holidays/countries/canada.py b/holidays/countries/canada.py index 37539ed03..12c771126 100644 --- a/holidays/countries/canada.py +++ b/holidays/countries/canada.py @@ -10,17 +10,24 @@ # License: MIT (see LICENSE file) from datetime import date -from datetime import timedelta as td from gettext import gettext as tr -from holidays.calendars.gregorian import MAR, APR, JUN, JUL, MON, _get_nth_weekday_from +from holidays.calendars.gregorian import MAR, APR, JUN, JUL from holidays.groups import ChristianHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ( + ObservedHolidayBase, + ALL_TO_NEAREST_MON, + SAT_SUN_TO_NEXT_MON, + SAT_SUN_TO_NEXT_MON_TUE, + SUN_TO_NEXT_MON, +) -class Canada(HolidayBase, ChristianHolidays, InternationalHolidays): +class Canada(ObservedHolidayBase, ChristianHolidays, InternationalHolidays): country = "CA" default_language = "en" + # %s (Observed). + observed_label = tr("%s (Observed)") subdivisions = ( "AB", "BC", @@ -44,24 +51,10 @@ def __init__(self, *args, **kwargs): kwargs["subdiv"] = "ON" ChristianHolidays.__init__(self) InternationalHolidays.__init__(self) - super().__init__(*args, **kwargs) + super().__init__(observed_rule=SAT_SUN_TO_NEXT_MON, *args, **kwargs) def _get_nearest_monday(self, *args) -> date: - dt = date(self._year, *args) - return _get_nth_weekday_from( - +1 if self._is_friday(dt) or self._is_weekend(dt) else -1, - MON, - dt, - ) - - def _add_observed(self, dt: date, include_sat: bool = True, days: int = +1) -> None: - if not self.observed: - return None - if self._is_sunday(dt) or (include_sat and self._is_saturday(dt)): - self._add_holiday( - self.tr("%s (Observed)") % self[dt], - dt + td(days=+2 if self._is_saturday(dt) else days), - ) + return self._get_observed_date(date(self._year, *args), rule=ALL_TO_NEAREST_MON) def _populate(self, year): if year <= 1866: @@ -85,11 +78,17 @@ def _populate(self, year): # Labour Day. self._add_holiday_1st_mon_of_sep(tr("Labour Day")) - # Christmas Day. - self._add_observed(self._add_christmas_day(tr("Christmas Day")), days=+2) + self._add_observed( + # Christmas Day. + self._add_christmas_day(tr("Christmas Day")), + rule=SAT_SUN_TO_NEXT_MON_TUE, + ) - # Boxing Day. - self._add_observed(self._add_christmas_day_two(tr("Boxing Day")), days=+2) + self._add_observed( + # Boxing Day. + self._add_christmas_day_two(tr("Boxing Day")), + rule=SAT_SUN_TO_NEXT_MON_TUE, + ) def _add_family_day(self): # Family Day. @@ -245,7 +244,9 @@ def _add_subdiv_nl_holidays(self): if self._year >= 1931: # Remembrance Day. - self._add_observed(self._add_remembrance_day(tr("Remembrance Day")), include_sat=False) + self._add_observed( + self._add_remembrance_day(tr("Remembrance Day")), rule=SUN_TO_NEXT_MON + ) def _add_subdiv_ns_holidays(self): # http://novascotia.ca/lae/employmentrights/NovaScotiaHeritageDay.asp @@ -261,7 +262,9 @@ def _add_subdiv_ns_holidays(self): if self._year >= 1931: # Remembrance Day. - self._add_observed(self._add_remembrance_day(tr("Remembrance Day")), include_sat=False) + self._add_observed( + self._add_remembrance_day(tr("Remembrance Day")), rule=SUN_TO_NEXT_MON + ) def _add_subdiv_nt_holidays(self): if self._year >= 1953: @@ -280,7 +283,9 @@ def _add_subdiv_nt_holidays(self): if self._year >= 1931: # Remembrance Day. - self._add_observed(self._add_remembrance_day(tr("Remembrance Day")), include_sat=False) + self._add_observed( + self._add_remembrance_day(tr("Remembrance Day")), rule=SUN_TO_NEXT_MON + ) def _add_subdiv_nu_holidays(self): if self._year >= 1953: @@ -290,7 +295,7 @@ def _add_subdiv_nu_holidays(self): if self._year >= 2000: dt = (APR, 1) if self._year == 2000 else (JUL, 9) # Nunavut Day. - self._add_observed(self._add_holiday(tr("Nunavut Day"), dt), include_sat=False) + self._add_observed(self._add_holiday(tr("Nunavut Day"), dt), rule=SUN_TO_NEXT_MON) self._add_thanksgiving() @@ -325,7 +330,9 @@ def _add_subdiv_pe_holidays(self): if self._year >= 1931: # Remembrance Day. - self._add_observed(self._add_remembrance_day(tr("Remembrance Day")), include_sat=False) + self._add_observed( + self._add_remembrance_day(tr("Remembrance Day")), rule=SUN_TO_NEXT_MON + ) def _add_subdiv_qc_holidays(self): if self._year >= 2003: @@ -336,7 +343,7 @@ def _add_subdiv_qc_holidays(self): self._add_observed( # St. Jean Baptiste Day. self._add_saint_johns_day(tr("St. Jean Baptiste Day")), - include_sat=False, + rule=SUN_TO_NEXT_MON, ) self._add_thanksgiving() @@ -358,7 +365,9 @@ def _add_subdiv_sk_holidays(self): if self._year >= 1931: # Remembrance Day. - self._add_observed(self._add_remembrance_day(tr("Remembrance Day")), include_sat=False) + self._add_observed( + self._add_remembrance_day(tr("Remembrance Day")), rule=SUN_TO_NEXT_MON + ) def _add_subdiv_yt_holidays(self): # start date? diff --git a/holidays/countries/chad.py b/holidays/countries/chad.py index 6596b3538..c9abab176 100644 --- a/holidays/countries/chad.py +++ b/holidays/countries/chad.py @@ -9,16 +9,13 @@ # Website: https://github.com/dr-prodigy/python-holidays # License: MIT (see LICENSE file) -from datetime import date -from datetime import timedelta as td - from holidays.calendars import _CustomIslamicCalendar from holidays.calendars.gregorian import JAN, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC from holidays.groups import ChristianHolidays, InternationalHolidays, IslamicHolidays -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ObservedHolidayBase, SUN_TO_NEXT_MON -class Chad(HolidayBase, ChristianHolidays, InternationalHolidays, IslamicHolidays): +class Chad(ObservedHolidayBase, ChristianHolidays, InternationalHolidays, IslamicHolidays): """ References: - https://en.wikipedia.org/wiki/Public_holidays_in_Chad @@ -26,19 +23,16 @@ class Chad(HolidayBase, ChristianHolidays, InternationalHolidays, IslamicHoliday """ country = "TD" + observed_label = "%s (Observed)" special_holidays = { 2021: (APR, 23, "Funeral of Idriss Déby Itno"), } - def __init__(self, *args, **kwargs) -> None: + def __init__(self, *args, **kwargs): ChristianHolidays.__init__(self) InternationalHolidays.__init__(self) IslamicHolidays.__init__(self, calendar=ChadIslamicCalendar()) - super().__init__(*args, **kwargs) - - def _add_observed(self, dt: date) -> None: - if self.observed and self._is_sunday(dt): - self._add_holiday("%s (Observed)" % self[dt], dt + td(days=+1)) + super().__init__(observed_rule=SUN_TO_NEXT_MON, *args, **kwargs) def _populate(self, year): # On 11 August 1960, Chad gained independence from France. diff --git a/holidays/countries/chile.py b/holidays/countries/chile.py index 7e5210e1e..abb7733bb 100644 --- a/holidays/countries/chile.py +++ b/holidays/countries/chile.py @@ -9,17 +9,21 @@ # Website: https://github.com/dr-prodigy/python-holidays # License: MIT (see LICENSE file) -from datetime import date from datetime import timedelta as td from gettext import gettext as tr from typing import Tuple -from holidays.calendars.gregorian import JUN, SEP, OCT, MON, _get_nth_weekday_from +from holidays.calendars.gregorian import JUN, SEP from holidays.groups import ChristianHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ( + ObservedHolidayBase, + TUE_TO_PREV_FRI, + WED_TO_NEXT_FRI, + WORKDAY_TO_NEAREST_MON, +) -class Chile(HolidayBase, ChristianHolidays, InternationalHolidays): +class Chile(ObservedHolidayBase, ChristianHolidays, InternationalHolidays): """ References: - https://www.feriados.cl @@ -74,15 +78,9 @@ class Chile(HolidayBase, ChristianHolidays, InternationalHolidays): def __init__(self, *args, **kwargs): ChristianHolidays.__init__(self) InternationalHolidays.__init__(self) - super().__init__(*args, **kwargs) - - def _move_holiday(self, dt: date) -> None: - if self._year <= 1999 or self._is_monday(dt) or self._is_weekend(dt): - return None - self._add_holiday( - self[dt], _get_nth_weekday_from(+1 if self._is_friday(dt) else -1, MON, dt) + super().__init__( + observed_rule=WORKDAY_TO_NEAREST_MON, observed_since=2000, *args, **kwargs ) - self.pop(dt) def _populate(self, year): if year <= 1914: @@ -177,15 +175,15 @@ def _populate(self, year): self._move_holiday(self._add_columbus_day(name)) if year >= 2008: - dt = date(year, OCT, 31) # This holiday is moved to the preceding Friday if it falls on a Tuesday, # or to the following Friday if it falls on a Wednesday. - if self._is_wednesday(dt): - dt += td(days=+2) - elif self._is_tuesday(dt): - dt += td(days=-4) - # National Day of the Evangelical and Protestant Churches. - self._add_holiday(tr("Día Nacional de las Iglesias Evangélicas y Protestantes"), dt) + self._move_holiday( + self._add_holiday_oct_31( + # National Day of the Evangelical and Protestant Churches. + tr("Día Nacional de las Iglesias Evangélicas y Protestantes") + ), + rule=TUE_TO_PREV_FRI + WED_TO_NEXT_FRI, + ) # All Saints Day. self._add_all_saints_day(tr("Día de Todos los Santos")) diff --git a/holidays/countries/colombia.py b/holidays/countries/colombia.py index 25ca46f31..c354a3d27 100644 --- a/holidays/countries/colombia.py +++ b/holidays/countries/colombia.py @@ -9,54 +9,45 @@ # Website: https://github.com/dr-prodigy/python-holidays # License: MIT (see LICENSE file) -from datetime import date from datetime import timedelta as td from gettext import gettext as tr -from holidays.calendars.gregorian import MON, _get_nth_weekday_from from holidays.groups import ChristianHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ObservedHolidayBase, ALL_TO_NEXT_MON -class Colombia(HolidayBase, ChristianHolidays, InternationalHolidays): +class Colombia(ObservedHolidayBase, ChristianHolidays, InternationalHolidays): """ Colombia has 18 holidays. The establishing of these are by: Ley 35 de 1939 (DEC 4): https://bit.ly/3PJwk7B Decreto 2663 de 1950 (AUG 5): https://bit.ly/3PJcut8 Decreto 3743 de 1950 (DEC 20): https://bit.ly/3B9Otr3 Ley 51 de 1983 (DEC 6): https://bit.ly/3aSobiB + + On the 6th of December 1983, the government of Colombia declared which + holidays are to take effect, and also clarified that a subset of them + are to take place the next Monday if they do not fall on a Monday. + This law is "Ley 51 de 1983" which translates to law 51 of 1983. + Link: https://bit.ly/3PtPi2e + A few links below to calendars from the 1980s to demonstrate this law + change. In 1984 some calendars still use the old rules, presumably + because they were printed prior to the declaration of law change. + 1981: https://bit.ly/3BbgKOc + 1982: https://bit.ly/3BdbhWW + 1984: https://bit.ly/3PqGxWU + 1984: https://bit.ly/3B7ogt8 """ country = "CO" default_language = "es" + # %s (Observed). + observed_label = tr("%s (Observado)") supported_languages = ("en_US", "es", "uk") def __init__(self, *args, **kwargs): ChristianHolidays.__init__(self) InternationalHolidays.__init__(self) - super().__init__(*args, **kwargs) - - def _move_holiday(self, dt: date) -> None: - """ - On the 6th of December 1983, the government of Colombia declared which - holidays are to take effect, and also clarified that a subset of them - are to take place the next Monday if they do not fall on a Monday. - This law is "Ley 51 de 1983" which translates to law 51 of 1983. - Link: https://bit.ly/3PtPi2e - A few links below to calendars from the 1980s to demonstrate this law - change. In 1984 some calendars still use the old rules, presumably - because they were printed prior to the declaration of law change. - 1981: https://bit.ly/3BbgKOc - 1982: https://bit.ly/3BdbhWW - 1984: https://bit.ly/3PqGxWU - 1984: https://bit.ly/3B7ogt8 - """ - - if self.observed and not self._is_monday(dt) and self._year >= 1984: - self._add_holiday( - self.tr("%s (Observado)") % self[dt], _get_nth_weekday_from(+1, MON, dt) - ) - self.pop(dt) + super().__init__(observed_rule=ALL_TO_NEXT_MON, observed_since=1984, *args, **kwargs) def _populate(self, year): super()._populate(year) diff --git a/holidays/countries/costa_rica.py b/holidays/countries/costa_rica.py index 403b10866..361085159 100644 --- a/holidays/countries/costa_rica.py +++ b/holidays/countries/costa_rica.py @@ -9,15 +9,18 @@ # Website: https://github.com/dr-prodigy/python-holidays # License: MIT (see LICENSE file) -from datetime import date from gettext import gettext as tr -from holidays.calendars.gregorian import MON, _get_nth_weekday_from from holidays.groups import ChristianHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ( + ObservedHolidayBase, + ALL_TO_NEAREST_MON_LATAM, + ALL_TO_NEXT_SUN, + WORKDAY_TO_NEXT_MON, +) -class CostaRica(HolidayBase, ChristianHolidays, InternationalHolidays): +class CostaRica(ObservedHolidayBase, ChristianHolidays, InternationalHolidays): """ References: - https://en.wikipedia.org/wiki/Public_holidays_in_Costa_Rica @@ -31,22 +34,14 @@ class CostaRica(HolidayBase, ChristianHolidays, InternationalHolidays): country = "CR" default_language = "es" + # %s (Observed). + observed_label = tr("%s (Observado)") supported_languages = ("en_US", "es", "uk") def __init__(self, *args, **kwargs): ChristianHolidays.__init__(self) InternationalHolidays.__init__(self) - super().__init__(*args, **kwargs) - - def _move_holiday(self, dt: date, forward: bool = False) -> None: - if not self.observed or self._is_monday(dt) or (forward and self._is_weekend(dt)): - return None - - dt_observed = _get_nth_weekday_from( - +1 if forward or not (self._is_tuesday(dt) or self._is_wednesday(dt)) else -1, MON, dt - ) - self._add_holiday(self.tr("%s (Observado)") % self[dt], dt_observed) - self.pop(dt) + super().__init__(observed_rule=ALL_TO_NEAREST_MON_LATAM, *args, **kwargs) def _populate(self, year): super()._populate(year) @@ -61,11 +56,11 @@ def _populate(self, year): self._add_good_friday(tr("Viernes Santo")) # Juan Santamaría Day. - dt = self._add_holiday_apr_11(tr("Día de Juan Santamaría")) + apr_11 = self._add_holiday_apr_11(tr("Día de Juan Santamaría")) if 2006 <= year <= 2010: - self._move_holiday(dt, forward=True) + self._move_holiday(apr_11, rule=WORKDAY_TO_NEXT_MON) elif year in {2023, 2024}: - self._move_holiday(dt) + self._move_holiday(apr_11) # International Labor Day. dt = self._add_labor_day(tr("Día Internacional del Trabajo")) @@ -73,11 +68,11 @@ def _populate(self, year): self._move_holiday(dt) # Annexation of the Party of Nicoya to Costa Rica. - dt = self._add_holiday_jul_25(tr("Anexión del Partido de Nicoya a Costa Rica")) + jul_25 = self._add_holiday_jul_25(tr("Anexión del Partido de Nicoya a Costa Rica")) if 2005 <= year <= 2008: - self._move_holiday(dt, forward=True) + self._move_holiday(jul_25, rule=WORKDAY_TO_NEXT_MON) elif 2020 <= year <= 2024: - self._move_holiday(dt) + self._move_holiday(jul_25) # Feast of Our Lady of the Angels. self._add_holiday_aug_2(tr("Fiesta de Nuestra Señora de los Ángeles")) @@ -85,17 +80,18 @@ def _populate(self, year): # Mother's Day. dt = self._add_assumption_of_mary_day(tr("Día de la Madre")) if 2005 <= year <= 2007: - self._move_holiday(dt, forward=True) + self._move_holiday(dt, rule=WORKDAY_TO_NEXT_MON) elif year in {2020, 2023, 2024}: self._move_holiday(dt) if year >= 2022: - # Day of the Black Person and Afro-Costa Rican Culture. - name = self.tr("Día de la Persona Negra y la Cultura Afrocostarricense") - if self.observed and year in {2022, 2023}: - self._add_holiday_1st_sun_from_aug_31(self.tr("%s (Observado)") % name) - else: - self._add_holiday_aug_31(name) + aug_31 = self._add_holiday_aug_31( + # Day of the Black Person and Afro-Costa Rican Culture. + self.tr("Día de la Persona Negra y la Cultura Afrocostarricense") + ) + if year in {2022, 2023}: + # Move to next Sunday. + self._move_holiday(aug_31, rule=ALL_TO_NEXT_SUN) # Independence Day. sep_15 = self._add_holiday_sep_15(tr("Día de la Independencia")) @@ -103,8 +99,11 @@ def _populate(self, year): self._move_holiday(sep_15) if year <= 2019: - # Cultures Day. - self._move_holiday(self._add_columbus_day(tr("Día de las Culturas")), forward=True) + self._move_holiday( + # Cultures Day. + self._add_columbus_day(tr("Día de las Culturas")), + rule=WORKDAY_TO_NEXT_MON, + ) if year >= 2020: # Army Abolition Day. diff --git a/holidays/countries/cuba.py b/holidays/countries/cuba.py index 596aca85d..caff44848 100644 --- a/holidays/countries/cuba.py +++ b/holidays/countries/cuba.py @@ -9,15 +9,13 @@ # Website: https://github.com/dr-prodigy/python-holidays # License: MIT (see LICENSE file) -from datetime import date -from datetime import timedelta as td from gettext import gettext as tr from holidays.groups import ChristianHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ObservedHolidayBase, SUN_TO_NEXT_MON -class Cuba(HolidayBase, ChristianHolidays, InternationalHolidays): +class Cuba(ObservedHolidayBase, ChristianHolidays, InternationalHolidays): """ Overview: https://en.wikipedia.org/wiki/Public_holidays_in_Cuba 1984 (DEC 28): https://bit.ly/3okNBbt @@ -34,28 +32,26 @@ class Cuba(HolidayBase, ChristianHolidays, InternationalHolidays): country = "CU" default_language = "es" + # %s Observed. + observed_label = tr("%s (Observado)") supported_languages = ("en_US", "es", "uk") def __init__(self, *args, **kwargs): ChristianHolidays.__init__(self) InternationalHolidays.__init__(self) - super().__init__(*args, **kwargs) + super().__init__(observed_rule=SUN_TO_NEXT_MON, *args, **kwargs) def _populate(self, year): # This calendar only works from 1959 onwards. if year <= 1958: return None - def _add_observed(dt: date) -> None: - if self.observed and self._is_sunday(dt): - self._add_holiday(self.tr("%s (Observado)") % self[dt], dt + td(days=+1)) - super()._populate(year) # Liberation Day. jan_1 = self._add_holiday_jan_1(tr("Triunfo de la Revolución")) if year <= 2013: - _add_observed(jan_1) + self._add_observed(jan_1) # Granted in 2007 decree. if year >= 2008: @@ -71,7 +67,7 @@ def _add_observed(dt: date) -> None: self._add_good_friday(tr("Viernes Santo")) # Labour Day. - _add_observed(self._add_labor_day(tr("Día Internacional de los Trabajadores"))) + self._add_observed(self._add_labor_day(tr("Día Internacional de los Trabajadores"))) # Commemoration of the Assault of the Moncada garrison. self._add_holiday_jul_25(tr("Conmemoración del asalto a Moncada")) @@ -83,7 +79,7 @@ def _add_observed(dt: date) -> None: self._add_holiday_jul_27(tr("Conmemoración del asalto a Moncada")) # Independence Day. - _add_observed(self._add_holiday_oct_10(tr("Inicio de las Guerras de Independencia"))) + self._add_observed(self._add_holiday_oct_10(tr("Inicio de las Guerras de Independencia"))) # In 1969, Christmas was cancelled for the sugar harvest but then was # cancelled for good: diff --git a/holidays/countries/dominican_republic.py b/holidays/countries/dominican_republic.py index e865a7fa6..58a8d4e95 100644 --- a/holidays/countries/dominican_republic.py +++ b/holidays/countries/dominican_republic.py @@ -12,12 +12,17 @@ from datetime import date from gettext import gettext as tr -from holidays.calendars.gregorian import JUN, MON, _get_nth_weekday_from +from holidays.calendars.gregorian import JUN from holidays.groups import ChristianHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ( + ObservedHolidayBase, + TUE_WED_TO_PREV_MON, + THU_FRI_TO_NEXT_MON, + THU_FRI_SUN_TO_NEXT_MON, +) -class DominicanRepublic(HolidayBase, ChristianHolidays, InternationalHolidays): +class DominicanRepublic(ObservedHolidayBase, ChristianHolidays, InternationalHolidays): """ http://ojd.org.do/Normativas/LABORAL/Leyes/Ley%20No.%20%20139-97.pdf https://es.wikipedia.org/wiki/Rep%C3%BAblica_Dominicana#D%C3%ADas_festivos_nacionales @@ -27,25 +32,14 @@ class DominicanRepublic(HolidayBase, ChristianHolidays, InternationalHolidays): default_language = "es" supported_languages = ("en_US", "es", "uk") - def _move_holiday(self, dt: date, include_sun: bool = False) -> None: - # Law No. 139-97 - Holidays Dominican Republic - Jun 27, 1997 - if dt < date(1997, JUN, 27): - return None - - dt_observed = None - if self._is_tuesday(dt) or self._is_wednesday(dt): - dt_observed = _get_nth_weekday_from(-1, MON, dt) - elif self._is_thursday(dt) or self._is_friday(dt) or (include_sun and self._is_sunday(dt)): - dt_observed = _get_nth_weekday_from(+1, MON, dt) - - if dt_observed: - self._add_holiday(self[dt], dt_observed) - self.pop(dt) - - def __init__(self, *args, **kwargs) -> None: + def __init__(self, *args, **kwargs): ChristianHolidays.__init__(self) InternationalHolidays.__init__(self) - super().__init__(*args, **kwargs) + super().__init__(observed_rule=TUE_WED_TO_PREV_MON + THU_FRI_TO_NEXT_MON, *args, **kwargs) + + def _is_observed(self, dt: date) -> bool: + # Law No. 139-97 - Holidays Dominican Republic - Jun 27, 1997 + return dt >= date(1997, JUN, 27) def _populate(self, year): super()._populate(year) @@ -68,8 +62,11 @@ def _populate(self, year): # Good Friday. self._add_good_friday(tr("Viernes Santo")) - # Labor Day. - self._move_holiday(self._add_labor_day(tr("Día del Trabajo")), include_sun=True) + self._move_holiday( + # Labor Day. + self._add_labor_day(tr("Día del Trabajo")), + rule=TUE_WED_TO_PREV_MON + THU_FRI_SUN_TO_NEXT_MON, + ) # Feast of Corpus Christi. self._add_corpus_christi_day(tr("Corpus Christi")) diff --git a/holidays/countries/ecuador.py b/holidays/countries/ecuador.py index 8005da5c1..5c200d1d6 100644 --- a/holidays/countries/ecuador.py +++ b/holidays/countries/ecuador.py @@ -9,16 +9,21 @@ # Website: https://github.com/dr-prodigy/python-holidays # License: MIT (see LICENSE file) -from datetime import date -from datetime import timedelta as td from gettext import gettext as tr from holidays.calendars.gregorian import DEC from holidays.groups import ChristianHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ( + ObservedHolidayBase, + TUE_TO_PREV_MON, + WED_TO_NEXT_FRI, + SAT_TO_PREV_FRI, + SUN_TO_NEXT_MON, + WED_THU_TO_NEXT_FRI, +) -class Ecuador(HolidayBase, ChristianHolidays, InternationalHolidays): +class Ecuador(ObservedHolidayBase, ChristianHolidays, InternationalHolidays): """ References: - https://en.wikipedia.org/wiki/Public_holidays_in_Ecuador @@ -27,51 +32,40 @@ class Ecuador(HolidayBase, ChristianHolidays, InternationalHolidays): country = "EC" default_language = "es" + # %s (Observed). + observed_label = tr("%s (Observado)") supported_languages = ("en_US", "es", "uk") def __init__(self, *args, **kwargs): ChristianHolidays.__init__(self) InternationalHolidays.__init__(self) - super().__init__(*args, **kwargs) - - def _add_observed( - self, dt: date, weekend_only: bool = False, before: bool = True, after: bool = True - ) -> None: - if self.observed and self._year >= 2017: - dt_observed = None - # Art. 1 of Law #0 from 20.12.2016 - # When holidays falls on Tuesday, the rest shall be transferred to - # preceding Monday, and if they falls on Wednesday or Thursday, - # the rest shall be transferred to Friday of the same week. - # Exceptions to this provision are January 1, December 25 and - # Shrove Tuesday. - if not weekend_only: - if self._is_tuesday(dt) and before: - dt_observed = dt + td(days=-1) - elif self._is_wednesday(dt): - dt_observed = dt + td(days=+2) - elif self._is_thursday(dt) and after: - dt_observed = dt + td(days=+1) - # When holidays falls on Saturday or Sunday, the rest shall be - # transferred, respectively, to the preceding Friday or the - # following Monday. - if self._is_saturday(dt) and before: - dt_observed = dt + td(days=-1) - elif self._is_sunday(dt) and after: - dt_observed = dt + td(days=+1) - if dt_observed: - self._add_holiday(self.tr("%s (Observado)") % self[dt], dt_observed) + # Art. 1 of Law #0 from 20.12.2016 + # When holidays falls on Tuesday, the rest shall be transferred to + # preceding Monday, and if they falls on Wednesday or Thursday, + # the rest shall be transferred to Friday of the same week. + # Exceptions to this provision are January 1, December 25 and + # Shrove Tuesday. + # When holidays falls on Saturday or Sunday, the rest shall be + # transferred, respectively, to the preceding Friday or the + # following Monday. + super().__init__( + observed_rule=( + TUE_TO_PREV_MON + WED_THU_TO_NEXT_FRI + SAT_TO_PREV_FRI + SUN_TO_NEXT_MON + ), + observed_since=2017, + *args, + **kwargs, + ) def _populate(self, year): super()._populate(year) # New Year's Day. name = self.tr("Año Nuevo") - self._add_observed(self._add_new_years_day(name), weekend_only=True) + self._add_observed(self._add_new_years_day(name), rule=SAT_TO_PREV_FRI + SUN_TO_NEXT_MON) - if self.observed and year >= 2017: - if self._is_friday(DEC, 31): - self._add_holiday_dec_31(self.tr("%s (Observado)") % name) + if self.observed and self._is_friday(DEC, 31) and year >= 2017: + self._add_holiday_dec_31(self.tr(self.observed_label) % name) # Carnival. name = tr("Carnaval") @@ -93,14 +87,23 @@ def _populate(self, year): # Independence of Guayaquil. self._add_observed(self._add_holiday_oct_9(tr("Independencia de Guayaquil"))) - # All Souls' Day. - self._add_observed(self._add_all_souls_day(tr("Día de los Difuntos")), after=False) - - # Independence of Cuenca. - self._add_observed(self._add_holiday_nov_3(tr("Independencia de Cuenca")), before=False) - - # Christmas Day. - self._add_observed(self._add_christmas_day(tr("Día de Navidad")), weekend_only=True) + self._add_observed( + # All Souls' Day. + self._add_all_souls_day(tr("Día de los Difuntos")), + rule=TUE_TO_PREV_MON + WED_TO_NEXT_FRI + SAT_TO_PREV_FRI, # Not observed the next day. + ) + + self._add_observed( + # Independence of Cuenca. + self._add_holiday_nov_3(tr("Independencia de Cuenca")), + rule=WED_THU_TO_NEXT_FRI + SUN_TO_NEXT_MON, # Not observed the previous day. + ) + + self._add_observed( + # Christmas Day. + self._add_christmas_day(tr("Día de Navidad")), + rule=SAT_TO_PREV_FRI + SUN_TO_NEXT_MON, + ) class EC(Ecuador): diff --git a/holidays/countries/eswatini.py b/holidays/countries/eswatini.py index 77e761a4e..72c9e4c29 100644 --- a/holidays/countries/eswatini.py +++ b/holidays/countries/eswatini.py @@ -10,21 +10,20 @@ # License: MIT (see LICENSE file) import warnings -from datetime import date -from datetime import timedelta as td from holidays.calendars.gregorian import JAN, DEC from holidays.groups import ChristianHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ObservedHolidayBase, SUN_TO_NEXT_MON, SUN_TO_NEXT_TUE -class Eswatini(HolidayBase, ChristianHolidays, InternationalHolidays): +class Eswatini(ObservedHolidayBase, ChristianHolidays, InternationalHolidays): """ https://swazilii.org/sz/legislation/act/1938/71 https://www.officeholidays.com/countries/swaziland """ country = "SZ" + observed_label = "%s (Observed)" special_holidays = { # https://mg.co.za/article/1999-12-09-swaziland-declares-bank-holidays/ 1999: (DEC, 31, "Y2K changeover"), @@ -34,13 +33,7 @@ class Eswatini(HolidayBase, ChristianHolidays, InternationalHolidays): def __init__(self, *args, **kwargs): ChristianHolidays.__init__(self) InternationalHolidays.__init__(self) - super().__init__(*args, **kwargs) - - def _add_observed(self, dt: date, days: int = +1) -> None: - # As of 2021/1/1, whenever a public holiday falls on a Sunday - # it rolls over to the following Monday - if self.observed and self._is_sunday(dt) and self._year >= 2021: - self._add_holiday("%s (Observed)" % self[dt], dt + td(days=days)) + super().__init__(observed_rule=SUN_TO_NEXT_MON, observed_since=2021, *args, **kwargs) def _populate(self, year): # Observed since 1939 @@ -58,12 +51,16 @@ def _populate(self, year): self._add_ascension_thursday("Ascension Day") if year >= 1987: - apr_19 = self._add_holiday_apr_19("King's Birthday") - self._add_observed(apr_19, days=+2 if apr_19 == self._easter_sunday else +1) + self._add_observed( + apr_19 := self._add_holiday_apr_19("King's Birthday"), + rule=SUN_TO_NEXT_TUE if apr_19 == self._easter_sunday else SUN_TO_NEXT_MON, + ) if year >= 1969: - apr_25 = self._add_holiday_apr_25("National Flag Day") - self._add_observed(apr_25, days=+2 if apr_25 == self._easter_sunday else +1) + self._add_observed( + apr_25 := self._add_holiday_apr_25("National Flag Day"), + rule=SUN_TO_NEXT_TUE if apr_25 == self._easter_sunday else SUN_TO_NEXT_MON, + ) self._add_observed(self._add_labor_day("Worker's Day")) @@ -72,7 +69,7 @@ def _populate(self, year): self._add_observed(self._add_holiday_sep_6("Independence Day")) - self._add_observed(self._add_christmas_day("Christmas Day"), days=+2) + self._add_observed(self._add_christmas_day("Christmas Day"), rule=SUN_TO_NEXT_TUE) self._add_observed(self._add_christmas_day_two("Boxing Day")) diff --git a/holidays/countries/greece.py b/holidays/countries/greece.py index 282be0e0c..875a47ff3 100644 --- a/holidays/countries/greece.py +++ b/holidays/countries/greece.py @@ -9,17 +9,15 @@ # Website: https://github.com/dr-prodigy/python-holidays # License: MIT (see LICENSE file) -from datetime import timedelta as td from gettext import gettext as tr -from holidays.calendars.gregorian import MON, _get_nth_weekday_from from holidays.calendars.julian_revised import JULIAN_REVISED_CALENDAR from holidays.constants import HALF_DAY, PUBLIC from holidays.groups import ChristianHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ObservedHolidayBase, SAT_SUN_TO_NEXT_WORKDAY -class Greece(HolidayBase, ChristianHolidays, InternationalHolidays): +class Greece(ObservedHolidayBase, ChristianHolidays, InternationalHolidays): """ Greece holidays. @@ -29,13 +27,15 @@ class Greece(HolidayBase, ChristianHolidays, InternationalHolidays): country = "GR" default_language = "el" + # %s (Observed). + observed_label = tr("%s (παρατηρήθηκε)") supported_categories = {HALF_DAY, PUBLIC} supported_languages = ("el", "en_US", "uk") def __init__(self, *args, **kwargs): ChristianHolidays.__init__(self, JULIAN_REVISED_CALENDAR) InternationalHolidays.__init__(self) - super().__init__(*args, **kwargs) + super().__init__(observed_rule=SAT_SUN_TO_NEXT_WORKDAY, *args, **kwargs) def _populate_public_holidays(self): # New Year's Day. @@ -60,19 +60,7 @@ def _populate_public_holidays(self): self._add_whit_monday(tr("Δευτέρα του Αγίου Πνεύματος")) # Labour Day. - name = self.tr("Εργατική Πρωτομαγιά") - may_1 = self._add_labor_day(name) - - if self.observed and self._is_weekend(may_1): - dt_observed = _get_nth_weekday_from(+1, MON, may_1) - # %s (Observed). - name_observed = self.tr("%s (παρατηρήθηκε)") % name - # In 2016 and 2021, Labour Day coincided with other holidays - # https://www.timeanddate.com/holidays/greece/labor-day - self._add_holiday( - name_observed, - dt_observed + td(days=+1) if dt_observed in self else dt_observed, - ) + self._add_observed(self._add_labor_day(self.tr("Εργατική Πρωτομαγιά"))) # Dormition of the Mother of God. self._add_assumption_of_mary_day(tr("Κοίμηση της Θεοτόκου")) diff --git a/holidays/countries/guatemala.py b/holidays/countries/guatemala.py index d5bdda7b9..3a600bd38 100644 --- a/holidays/countries/guatemala.py +++ b/holidays/countries/guatemala.py @@ -12,16 +12,23 @@ from datetime import date from gettext import gettext as tr -from holidays.calendars.gregorian import OCT, MON, _get_nth_weekday_from +from holidays.calendars.gregorian import OCT from holidays.groups import ChristianHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ObservedHolidayBase, ALL_TO_NEAREST_MON_LATAM -class Guatemala(HolidayBase, ChristianHolidays, InternationalHolidays): +class Guatemala(ObservedHolidayBase, ChristianHolidays, InternationalHolidays): """ References: - http://www.bvnsa.com.gt/bvnsa/calendario_dias_festivos.php - https://www.minfin.gob.gt/images/downloads/leyes_acuerdos/decretocong19_101018.pdf + + Moving holidays: + law 19-2018 start 18 oct 2018 + https://www.minfin.gob.gt/images/downloads/leyes_acuerdos/decretocong19_101018.pdf + + EXPEDIENTE 5536-2018 (CC) start 17 abr 2020 + https://leyes.infile.com/index.php?id=181&id_publicacion=81051 """ country = "GT" @@ -31,26 +38,10 @@ class Guatemala(HolidayBase, ChristianHolidays, InternationalHolidays): def __init__(self, *args, **kwargs): ChristianHolidays.__init__(self) InternationalHolidays.__init__(self) - super().__init__(*args, **kwargs) + super().__init__(observed_rule=ALL_TO_NEAREST_MON_LATAM, *args, **kwargs) - def _move_holiday(self, dt: date) -> None: - """ - law 19-2018 start 18 oct 2018 - https://www.minfin.gob.gt/images/downloads/leyes_acuerdos/decretocong19_101018.pdf - - - EXPEDIENTE 5536-2018 (CC) start 17 abr 2020 - https://leyes.infile.com/index.php?id=181&id_publicacion=81051 - """ - if dt <= date(2018, OCT, 17) or self._is_monday(dt): - return None - self._add_holiday( - self[dt], - _get_nth_weekday_from(-1, MON, dt) - if self._is_tuesday(dt) or self._is_wednesday(dt) - else _get_nth_weekday_from(1, MON, dt), - ) - self.pop(dt) + def _is_observed(self, dt: date) -> bool: + return dt >= date(2018, OCT, 18) def _populate(self, year): super()._populate(year) diff --git a/holidays/countries/hungary.py b/holidays/countries/hungary.py index 5d9931711..0f0a977f6 100644 --- a/holidays/countries/hungary.py +++ b/holidays/countries/hungary.py @@ -9,16 +9,14 @@ # Website: https://github.com/dr-prodigy/python-holidays # License: MIT (see LICENSE file) -from datetime import date -from datetime import timedelta as td from gettext import gettext as tr from holidays.calendars.gregorian import DEC from holidays.groups import ChristianHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ObservedHolidayBase, TUE_TO_PREV_MON, THU_TO_NEXT_FRI -class Hungary(HolidayBase, ChristianHolidays, InternationalHolidays): +class Hungary(ObservedHolidayBase, ChristianHolidays, InternationalHolidays): """ https://en.wikipedia.org/wiki/Public_holidays_in_Hungary Codification dates: @@ -28,36 +26,35 @@ class Hungary(HolidayBase, ChristianHolidays, InternationalHolidays): country = "HU" default_language = "hu" + # Day off before + observed_label_before = tr("%s előtti pihenőnap") + # Day off after + observed_label = tr("%s utáni pihenőnap") supported_languages = ("en_US", "hu", "uk") - def _add_observed( - self, dt: date, since: int = 2010, before: bool = True, after: bool = True - ) -> None: - if not self.observed or dt.year < since: - return None - if self._is_tuesday(dt) and before: - # Day off before - self._add_holiday(self.tr("%s előtti pihenőnap") % self[dt], dt + td(days=-1)) - elif self._is_thursday(dt) and after: - # Day off after - self._add_holiday(self.tr("%s utáni pihenőnap") % self[dt], dt + td(days=+1)) - def __init__(self, *args, **kwargs): ChristianHolidays.__init__(self) InternationalHolidays.__init__(self) - super().__init__(*args, **kwargs) + super().__init__( + observed_rule=TUE_TO_PREV_MON + THU_TO_NEXT_FRI, + observed_since=2010, + *args, + **kwargs, + ) def _populate(self, year): super()._populate(year) # New Year's Day. name = self.tr("Újév") - self._add_observed(self._add_new_years_day(name), before=False, since=2014) + jan_1 = self._add_new_years_day(name) + if year >= 2014: + self._add_observed(jan_1) - # The last day of the year is an observed day off if New Year's Day - # falls on a Tuesday. - if self.observed and self._is_monday(DEC, 31) and year >= 2014: - self._add_holiday_dec_31(self.tr("%s előtti pihenőnap") % name) + # The last day of the year is an observed day off if New Year's Day + # falls on a Tuesday. + if self.observed and self._is_monday(DEC, 31): + self._add_holiday_dec_31(self.tr(self.observed_label_before) % name) if 1945 <= year <= 1950 or year >= 1989: # National Day. @@ -111,12 +108,10 @@ def _populate(self, year): self._add_christmas_day(tr("Karácsony")) if year != 1955: - self._add_observed( - # Second Day of Christmas. - self._add_christmas_day_two(tr("Karácsony másnapja")), - since=2013, - before=False, - ) + # Second Day of Christmas. + dec_26 = self._add_christmas_day_two(tr("Karácsony másnapja")) + if year >= 2013: + self._add_observed(dec_26, rule=THU_TO_NEXT_FRI) # Soviet era. if 1950 <= year <= 1989: diff --git a/holidays/countries/ireland.py b/holidays/countries/ireland.py index f015d34a9..eb18624f1 100644 --- a/holidays/countries/ireland.py +++ b/holidays/countries/ireland.py @@ -9,15 +9,16 @@ # Website: https://github.com/dr-prodigy/python-holidays # License: MIT (see LICENSE file) -from datetime import date -from datetime import timedelta as td - from holidays.calendars.gregorian import FEB, MAR from holidays.groups import ChristianHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ( + ObservedHolidayBase, + SAT_SUN_TO_NEXT_MON, + SAT_SUN_TO_NEXT_MON_TUE, +) -class Ireland(HolidayBase, ChristianHolidays, InternationalHolidays): +class Ireland(ObservedHolidayBase, ChristianHolidays, InternationalHolidays): """ Official holidays in Ireland, as declared in the Citizen's Information bulletin: @@ -25,6 +26,7 @@ class Ireland(HolidayBase, ChristianHolidays, InternationalHolidays): """ country = "IE" + observed_label = "%s (Observed)" special_holidays = { 2022: (MAR, 18, "Day of Remembrance and Recognition"), } @@ -32,13 +34,7 @@ class Ireland(HolidayBase, ChristianHolidays, InternationalHolidays): def __init__(self, *args, **kwargs): ChristianHolidays.__init__(self) InternationalHolidays.__init__(self) - super().__init__(*args, **kwargs) - - def _add_observed(self, dt: date, days: int = +1) -> None: - if self.observed and self._is_weekend(dt): - self._add_holiday( - "%s (Observed)" % self[dt], dt + td(days=+2 if self._is_saturday(dt) else days) - ) + super().__init__(observed_rule=SAT_SUN_TO_NEXT_MON, *args, **kwargs) def _populate(self, year): super()._populate(year) @@ -81,7 +77,9 @@ def _populate(self, year): self._add_observed(self._add_christmas_day("Christmas Day")) # St. Stephen's Day. - self._add_observed(self._add_christmas_day_two("St. Stephen's Day"), days=+2) + self._add_observed( + self._add_christmas_day_two("St. Stephen's Day"), rule=SAT_SUN_TO_NEXT_MON_TUE + ) class IE(Ireland): diff --git a/holidays/countries/isle_of_man.py b/holidays/countries/isle_of_man.py index b9ccb82c8..6988f10a8 100644 --- a/holidays/countries/isle_of_man.py +++ b/holidays/countries/isle_of_man.py @@ -13,10 +13,9 @@ from datetime import timedelta as td from holidays.calendars.gregorian import JUL +from holidays.countries.united_kingdom import UnitedKingdom from holidays.groups import ChristianHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase - -from .united_kingdom import UnitedKingdom +from holidays.observed_holiday_base import ObservedHolidayBase, SAT_SUN_TO_NEXT_MON class IsleOfMan(UnitedKingdom): @@ -28,7 +27,7 @@ class IsleOfMan(UnitedKingdom): def __init__(self, *args, **kwargs): # Override UnitedKingdom __init__(). ChristianHolidays.__init__(self) InternationalHolidays.__init__(self) - HolidayBase.__init__(self, *args, **kwargs) + ObservedHolidayBase.__init__(self, observed_rule=SAT_SUN_TO_NEXT_MON, *args, **kwargs) def _populate(self, year: int) -> None: super()._populate(year) diff --git a/holidays/countries/jamaica.py b/holidays/countries/jamaica.py index fbb57e580..9d40cc75d 100644 --- a/holidays/countries/jamaica.py +++ b/holidays/countries/jamaica.py @@ -9,33 +9,28 @@ # Website: https://github.com/dr-prodigy/python-holidays # License: MIT (see LICENSE file) -from datetime import date -from datetime import timedelta as td - from holidays.groups import ChristianHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ( + ObservedHolidayBase, + SUN_TO_NEXT_MON, + SUN_TO_NEXT_TUE, + SAT_SUN_TO_NEXT_MON, +) -class Jamaica(HolidayBase, ChristianHolidays, InternationalHolidays): +class Jamaica(ObservedHolidayBase, ChristianHolidays, InternationalHolidays): """ https://en.wikipedia.org/wiki/Public_holidays_in_Jamaica https://www.mlss.gov.jm/wp-content/uploads/2017/11/The-Holidays-Public-General-Act.pdf """ country = "JM" + observed_label = "%s (Observed)" - def __init__(self, *args, **kwargs) -> None: + def __init__(self, *args, **kwargs): ChristianHolidays.__init__(self) InternationalHolidays.__init__(self) - super().__init__(*args, **kwargs) - - def _add_observed(self, dt: date, include_sat: bool = False, days: int = +1) -> None: - if not self.observed: - return None - if self._is_sunday(dt) or (include_sat and self._is_saturday(dt)): - self._add_holiday( - "%s (Observed)" % self[dt], dt + td(days=+2 if self._is_saturday(dt) else days) - ) + super().__init__(observed_rule=SUN_TO_NEXT_MON, *args, **kwargs) def _populate(self, year): super()._populate(year) @@ -53,7 +48,9 @@ def _populate(self, year): self._add_easter_monday("Easter Monday") # National Labour Day - self._add_observed(self._add_holiday_may_23("National Labour Day"), include_sat=True) + self._add_observed( + self._add_holiday_may_23("National Labour Day"), rule=SAT_SUN_TO_NEXT_MON + ) # Emancipation Day if year >= 1998: @@ -66,7 +63,7 @@ def _populate(self, year): self._add_holiday_3rd_mon_of_oct("National Heroes Day") # Christmas Day - self._add_observed(self._add_christmas_day("Christmas Day"), days=+2) + self._add_observed(self._add_christmas_day("Christmas Day"), rule=SUN_TO_NEXT_TUE) # Boxing Day self._add_observed(self._add_christmas_day_two("Boxing Day")) diff --git a/holidays/countries/kazakhstan.py b/holidays/countries/kazakhstan.py index 711dd9489..0a2071ac9 100644 --- a/holidays/countries/kazakhstan.py +++ b/holidays/countries/kazakhstan.py @@ -9,14 +9,12 @@ # Website: https://github.com/dr-prodigy/python-holidays # License: MIT (see LICENSE file) -from datetime import timedelta as td - from holidays.calendars.julian import JULIAN_CALENDAR -from holidays.groups import ChristianHolidays, IslamicHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase +from holidays.groups import ChristianHolidays, InternationalHolidays, IslamicHolidays +from holidays.observed_holiday_base import ObservedHolidayBase, SAT_SUN_TO_NEXT_WORKDAY -class Kazakhstan(HolidayBase, ChristianHolidays, InternationalHolidays, IslamicHolidays): +class Kazakhstan(ObservedHolidayBase, ChristianHolidays, InternationalHolidays, IslamicHolidays): """ 1. https://www.officeholidays.com/countries/kazakhstan/2020 2. https://egov.kz/cms/en/articles/holidays-calend @@ -25,12 +23,15 @@ class Kazakhstan(HolidayBase, ChristianHolidays, InternationalHolidays, IslamicH """ country = "KZ" + observed_label = "%s (Observed)" def __init__(self, *args, **kwargs): ChristianHolidays.__init__(self, JULIAN_CALENDAR) InternationalHolidays.__init__(self) IslamicHolidays.__init__(self) - super().__init__(*args, **kwargs) + super().__init__( + observed_rule=SAT_SUN_TO_NEXT_WORKDAY, observed_since=2002, *args, **kwargs + ) def _populate(self, year): # Kazakhstan declared its sovereignty on 25 October 1990 @@ -94,19 +95,13 @@ def _populate(self, year): if 2002 <= year <= 2021: dts_observed.add(self._add_holiday_dec_17(name)) + if self.observed: + self._populate_observed(dts_observed) + # Kurban Ait (nonworking day, without extending) if year >= 2006: self._add_eid_al_adha_day("Kurban Ait") - if self.observed and year >= 2002: - for dt in sorted(dts_observed): - if not self._is_weekend(dt): - continue - dt_observed = dt + td(days=+1) - while self._is_weekend(dt_observed) or dt_observed in dts_observed: - dt_observed += td(days=+1) - dts_observed.add(self._add_holiday("%s (Observed)" % self[dt], dt_observed)) - class KZ(Kazakhstan): pass diff --git a/holidays/countries/kenya.py b/holidays/countries/kenya.py index d4cf1a71d..02d455416 100644 --- a/holidays/countries/kenya.py +++ b/holidays/countries/kenya.py @@ -9,15 +9,12 @@ # Website: https://github.com/dr-prodigy/python-holidays # License: MIT (see LICENSE file) -from datetime import date -from datetime import timedelta as td - from holidays.calendars.gregorian import FEB, APR, AUG, SEP from holidays.groups import ChristianHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ObservedHolidayBase, SUN_TO_NEXT_MON, SUN_TO_NEXT_TUE -class Kenya(HolidayBase, ChristianHolidays, InternationalHolidays): +class Kenya(ObservedHolidayBase, ChristianHolidays, InternationalHolidays): """ https://en.wikipedia.org/wiki/Public_holidays_in_Kenya http://kenyaembassyberlin.de/Public-Holidays-in-Kenya.48.0.html @@ -25,6 +22,7 @@ class Kenya(HolidayBase, ChristianHolidays, InternationalHolidays): """ country = "KE" + observed_label = "%s (Observed)" special_holidays = { 2020: (FEB, 11, "President Moi Celebration of Life Day"), 2022: ( @@ -40,20 +38,16 @@ class Kenya(HolidayBase, ChristianHolidays, InternationalHolidays): def __init__(self, *args, **kwargs): ChristianHolidays.__init__(self) InternationalHolidays.__init__(self) - super().__init__(*args, **kwargs) + super().__init__(observed_rule=SUN_TO_NEXT_MON, *args, **kwargs) def _populate(self, year): - def _add_observed(dt: date, days: int = +1) -> None: - if self.observed and self._is_sunday(dt): - self._add_holiday("%s (Observed)" % self[dt], dt + td(days=days)) - if year <= 1962: return None super()._populate(year) # New Year's Day - _add_observed(self._add_new_years_day("New Year's Day")) + self._add_observed(self._add_new_years_day("New Year's Day")) # Good Friday self._add_good_friday("Good Friday") @@ -62,31 +56,31 @@ def _add_observed(dt: date, days: int = +1) -> None: self._add_easter_monday("Easter Monday") # Labour Day - _add_observed(self._add_labor_day("Labour Day")) + self._add_observed(self._add_labor_day("Labour Day")) if year >= 2010: # Mandaraka Day - _add_observed(self._add_holiday_jun_1("Madaraka Day")) + self._add_observed(self._add_holiday_jun_1("Madaraka Day")) if 2002 <= year <= 2009 or year >= 2018: - _add_observed( + self._add_observed( # Utamaduni/Moi Day self._add_holiday_oct_10("Utamaduni Day" if year >= 2021 else "Moi Day") ) - _add_observed( + self._add_observed( # Mashuja/Kenyatta Day self._add_holiday_oct_20("Mashujaa Day" if year >= 2010 else "Kenyatta Day") ) # Jamhuri Day - _add_observed(self._add_holiday_dec_12("Jamhuri Day")) + self._add_observed(self._add_holiday_dec_12("Jamhuri Day")) # Christmas Day - _add_observed(self._add_christmas_day("Christmas Day"), days=+2) + self._add_observed(self._add_christmas_day("Christmas Day"), rule=SUN_TO_NEXT_TUE) # Boxing Day - _add_observed(self._add_christmas_day_two("Boxing Day")) + self._add_observed(self._add_christmas_day_two("Boxing Day")) class KE(Kenya): diff --git a/holidays/countries/latvia.py b/holidays/countries/latvia.py index c59b757c2..cd061b924 100644 --- a/holidays/countries/latvia.py +++ b/holidays/countries/latvia.py @@ -9,16 +9,15 @@ # Website: https://github.com/dr-prodigy/python-holidays # License: MIT (see LICENSE file) -from datetime import date from datetime import timedelta as td from gettext import gettext as tr from holidays.calendars.gregorian import MAY, JUL, SEP from holidays.groups import ChristianHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ObservedHolidayBase, SAT_SUN_TO_NEXT_MON -class Latvia(HolidayBase, ChristianHolidays, InternationalHolidays): +class Latvia(ObservedHolidayBase, ChristianHolidays, InternationalHolidays): """ https://en.wikipedia.org/wiki/Public_holidays_in_Latvia https://information.lv/ @@ -27,6 +26,8 @@ class Latvia(HolidayBase, ChristianHolidays, InternationalHolidays): country = "LV" default_language = "lv" + # %s (Observed). + observed_label = tr("%s (brīvdiena)") # General Latvian Song and Dance Festival closing day. song_and_dance_festival_closing_day = tr( @@ -57,14 +58,7 @@ class Latvia(HolidayBase, ChristianHolidays, InternationalHolidays): def __init__(self, *args, **kwargs): ChristianHolidays.__init__(self) InternationalHolidays.__init__(self) - super().__init__(*args, **kwargs) - - def _add_observed(self, dt: date) -> None: - if self.observed and self._is_weekend(dt): - self._add_holiday( - self.tr("%s (brīvdiena)") % self[dt], - dt + td(days=+2 if self._is_saturday(dt) else +1), - ) + super().__init__(observed_rule=SAT_SUN_TO_NEXT_MON, *args, **kwargs) def _populate(self, year): if year <= 1989: diff --git a/holidays/countries/malawi.py b/holidays/countries/malawi.py index b35cf6249..436178548 100644 --- a/holidays/countries/malawi.py +++ b/holidays/countries/malawi.py @@ -9,31 +9,27 @@ # Website: https://github.com/dr-prodigy/python-holidays # License: MIT (see LICENSE file) -from datetime import date -from datetime import timedelta as td - from holidays.groups import ChristianHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ( + ObservedHolidayBase, + SAT_SUN_TO_NEXT_MON, + SAT_SUN_TO_NEXT_MON_TUE, +) -class Malawi(HolidayBase, ChristianHolidays, InternationalHolidays): +class Malawi(ObservedHolidayBase, ChristianHolidays, InternationalHolidays): """ https://www.officeholidays.com/countries/malawi https://www.timeanddate.com/holidays/malawi/ """ country = "MW" + observed_label = "%s (Observed)" def __init__(self, *args, **kwargs): ChristianHolidays.__init__(self) InternationalHolidays.__init__(self) - super().__init__(*args, **kwargs) - - def _add_observed(self, dt: date, days: int = +1) -> None: - if self.observed and self._is_weekend(dt): - self._add_holiday( - "%s (Observed)" % self[dt], dt + td(+2 if self._is_saturday(dt) else days) - ) + super().__init__(observed_rule=SAT_SUN_TO_NEXT_MON, *args, **kwargs) def _populate(self, year): # Observed since 2000 @@ -60,9 +56,9 @@ def _populate(self, year): self._add_observed(self._add_holiday_oct_15("Mother's Day")) - self._add_observed(self._add_christmas_day("Christmas Day"), days=+2) + self._add_observed(self._add_christmas_day("Christmas Day"), rule=SAT_SUN_TO_NEXT_MON_TUE) - self._add_observed(self._add_christmas_day_two("Boxing Day"), days=+2) + self._add_observed(self._add_christmas_day_two("Boxing Day"), rule=SAT_SUN_TO_NEXT_MON_TUE) class MW(Malawi): diff --git a/holidays/countries/malaysia.py b/holidays/countries/malaysia.py index 83591c19a..43347992c 100644 --- a/holidays/countries/malaysia.py +++ b/holidays/countries/malaysia.py @@ -9,8 +9,6 @@ # Website: https://github.com/dr-prodigy/python-holidays # License: MIT (see LICENSE file) -from datetime import timedelta as td - from holidays.calendars import ( _CustomBuddhistCalendar, _CustomChineseCalendar, @@ -32,7 +30,6 @@ DEC, FRI, SAT, - SUN, ) from holidays.groups import ( BuddhistCalendarHolidays, @@ -42,11 +39,16 @@ InternationalHolidays, IslamicHolidays, ) -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ( + ObservedHolidayBase, + FRI_TO_NEXT_WORKDAY, + SAT_TO_NEXT_WORKDAY, + SUN_TO_NEXT_WORKDAY, +) class Malaysia( - HolidayBase, + ObservedHolidayBase, BuddhistCalendarHolidays, ChineseCalendarHolidays, ChristianHolidays, @@ -55,6 +57,7 @@ class Malaysia( IslamicHolidays, ): country = "MY" + observed_label = "%s [In lieu]" special_holidays = { # The years 1955 1959 1995 seems to have the elections # one weekday but I am not sure if they were marked as @@ -82,7 +85,7 @@ class Malaysia( "TRG", ) - def __init__(self, *args, **kwargs) -> None: + def __init__(self, *args, **kwargs): """ An subclass of :py:class:`HolidayBase` representing public holidays in Malaysia. @@ -113,6 +116,13 @@ def __init__(self, *args, **kwargs) -> None: - Prior to 2021: holidays are not accurate. - 2027 and later: Thaipusam dates are are estimated, and so denoted. + Section 3 of Malaysian Holidays Act: + "If any day specified in the Schedule falls on Sunday then the day following shall be + a public holiday and if such day is already a public holiday, then the day following + shall be a public holiday". + In Johor and Kedah it's Friday -> Sunday, + in Kelantan and Terengganu it's Saturday -> Sunday + Reference: `Wikipedia `__ @@ -132,7 +142,7 @@ def __init__(self, *args, **kwargs) -> None: HinduCalendarHolidays.__init__(self, calendar=MalaysiaHinduCalendar()) InternationalHolidays.__init__(self) IslamicHolidays.__init__(self, calendar=MalaysiaIslamicCalendar()) - super().__init__(*args, **kwargs) + super().__init__(observed_rule=SUN_TO_NEXT_WORKDAY, *args, **kwargs) def _populate(self, year): super()._populate(year) @@ -307,36 +317,22 @@ def _populate(self, year): dts_observed.add(self._add_holiday_apr_26("Birthday of the Sultan of Terengganu")) - # Check for holidays that fall on a Sunday and - # implement Section 3 of Malaysian Holidays Act: - # "if any day specified in the Schedule falls on - # Sunday then the day following shall be a public - # holiday and if such day is already a public holiday, - # then the day following shall be a public holiday" - # In Johor and Kedah it's Friday -> Sunday, - # in Kelantan and Terengganu it's Saturday -> Sunday + if self.subdiv in {"JHR", "KDH"}: + self._observed_rule = FRI_TO_NEXT_WORKDAY + self.weekend = {FRI, SAT} + elif self.subdiv in {"KTN", "TRG"}: + self._observed_rule = SAT_TO_NEXT_WORKDAY + self.weekend = {FRI, SAT} + if self.observed: - weekday_observed_days = ( - (FRI, +2) - if self.subdiv in {"JHR", "KDH"} - else ((SAT, +1) if self.subdiv in {"KTN", "TRG"} else (SUN, +1)) - ) - for dt in sorted(dts_observed): - if dt and dt.weekday() != weekday_observed_days[0]: - continue - dt_in_lieu = dt + td(days=weekday_observed_days[1]) - while dt_in_lieu in dts_observed: - dt_in_lieu += td(days=+1) - for name in self.get_list(dt): - self._add_holiday(f"{name} [In lieu]", dt_in_lieu) - dts_observed.add(dt_in_lieu) + self._populate_observed(dts_observed) # Special cases observed from previous year. if year == 2007 and self.subdiv not in {"JHR", "KDH", "KTN", "TRG"}: - self._add_holiday_jan_2("Hari Raya Haji [In lieu]") + self._add_holiday_jan_2(self.observed_label % "Hari Raya Haji") if year == 2007 and self.subdiv == "TRG": - self._add_holiday_jan_2("Arafat Day [In lieu]") + self._add_holiday_jan_2(self.observed_label % "Arafat Day") # The last two days in May (Pesta Kaamatan). # (Sarawak Act) diff --git a/holidays/countries/marshall_islands.py b/holidays/countries/marshall_islands.py index 1bc0015a1..a6b93b4f0 100644 --- a/holidays/countries/marshall_islands.py +++ b/holidays/countries/marshall_islands.py @@ -10,23 +10,20 @@ # License: MIT (see LICENSE file) import warnings -from datetime import date -from datetime import timedelta as td from holidays.calendars.gregorian import NOV from holidays.groups import ChristianHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ObservedHolidayBase, SUN_TO_NEXT_MON -class HolidaysMH(HolidayBase, ChristianHolidays, InternationalHolidays): +class HolidaysMH(ObservedHolidayBase, ChristianHolidays, InternationalHolidays): """ https://rmiparliament.org/cms/component/content/article/14-pressrelease/49-important-public-holidays.html?Itemid=101 https://www.rmiembassyus.org/country-profile#:~:text=national%20holidays """ country = "MH" - - # Special Holidays + observed_label = "%s Holiday" # General Election Day election_day = "General Election Day" @@ -42,17 +39,10 @@ class HolidaysMH(HolidayBase, ChristianHolidays, InternationalHolidays): 2023: (NOV, 20, election_day), } - def _add_observed(self, dt: date) -> None: - """ - If fall on Sunday, an observed holiday marked with suffix " Holiday" will be added. - """ - if self.observed and self._is_sunday(dt): - self._add_holiday("%s Holiday" % self[dt], dt + td(days=+1)) - def __init__(self, *args, **kwargs): ChristianHolidays.__init__(self) InternationalHolidays.__init__(self) - super().__init__(*args, **kwargs) + super().__init__(observed_rule=SUN_TO_NEXT_MON, *args, **kwargs) def _populate(self, year): super()._populate(year) diff --git a/holidays/countries/monaco.py b/holidays/countries/monaco.py index 14f1eb33d..3131a8d3c 100644 --- a/holidays/countries/monaco.py +++ b/holidays/countries/monaco.py @@ -9,16 +9,14 @@ # Website: https://github.com/dr-prodigy/python-holidays # License: MIT (see LICENSE file) -from datetime import date -from datetime import timedelta as td from gettext import gettext as tr from holidays.calendars.gregorian import JAN, DEC from holidays.groups import ChristianHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ObservedHolidayBase, SUN_TO_NEXT_MON -class Monaco(HolidayBase, ChristianHolidays, InternationalHolidays): +class Monaco(ObservedHolidayBase, ChristianHolidays, InternationalHolidays): """ https://en.wikipedia.org/wiki/Public_holidays_in_Monaco https://en.service-public-entreprises.gouv.mc/Employment-and-social-affairs/Employment-regulations/Leave/Public-Holidays # noqa: E501 @@ -26,17 +24,16 @@ class Monaco(HolidayBase, ChristianHolidays, InternationalHolidays): country = "MC" default_language = "fr" + # %s (Observed). + observed_label = tr("%s (Observé)") + # Public holiday. special_holidays = {2015: (JAN, 7, tr("Jour férié"))} supported_languages = ("en_US", "fr", "uk") def __init__(self, *args, **kwargs): ChristianHolidays.__init__(self) InternationalHolidays.__init__(self) - super().__init__(*args, **kwargs) - - def _add_observed(self, dt: date, days: int = +1) -> None: - if self.observed and self._is_sunday(dt): - self._add_holiday(self.tr("%s (Observé)") % self[dt], dt + td(days=days)) + super().__init__(observed_rule=SUN_TO_NEXT_MON, *args, **kwargs) def _populate(self, year): super()._populate(year) diff --git a/holidays/countries/montenegro.py b/holidays/countries/montenegro.py index 9a212f487..37161eeb5 100644 --- a/holidays/countries/montenegro.py +++ b/holidays/countries/montenegro.py @@ -9,15 +9,12 @@ # Website: https://github.com/dr-prodigy/python-holidays # License: MIT (see LICENSE file) -from datetime import date -from datetime import timedelta as td - from holidays.calendars.julian import JULIAN_CALENDAR from holidays.groups import ChristianHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ObservedHolidayBase, SUN_TO_NEXT_MON, SUN_TO_NEXT_TUE -class Montenegro(HolidayBase, ChristianHolidays, InternationalHolidays): +class Montenegro(ObservedHolidayBase, ChristianHolidays, InternationalHolidays): """ References: - https://en.wikipedia.org/wiki/Public_holidays_in_Montenegro @@ -27,22 +24,19 @@ class Montenegro(HolidayBase, ChristianHolidays, InternationalHolidays): """ country = "ME" + observed_label = "%s (Observed)" def __init__(self, *args, **kwargs): ChristianHolidays.__init__(self, calendar=JULIAN_CALENDAR) InternationalHolidays.__init__(self) - super().__init__(*args, **kwargs) - - def _add_observed(self, dt: date, days: int = +1) -> None: - if self.observed and self._is_sunday(dt): - self._add_holiday("%s (Observed)" % self[dt], dt + td(days)) + super().__init__(observed_rule=SUN_TO_NEXT_MON, *args, **kwargs) def _populate(self, year): super()._populate(year) # New Year's Day. name = "New Year's Day" - self._add_observed(self._add_new_years_day(name), days=+2) + self._add_observed(self._add_new_years_day(name), rule=SUN_TO_NEXT_TUE) self._add_observed(self._add_new_years_day_two(name)) # Orthodox Christmas Eve. @@ -53,7 +47,7 @@ def _populate(self, year): # Labour Day. name = "Labour Day" - self._add_observed(self._add_labor_day(name), days=+2) + self._add_observed(self._add_labor_day(name), rule=SUN_TO_NEXT_TUE) self._add_observed(self._add_labor_day_two(name)) # Good Friday. @@ -67,15 +61,13 @@ def _populate(self, year): # Independence Day. name = "Independence Day" - may_21 = self._add_holiday_may_21(name) - self._add_observed(may_21, days=+2) - self._add_observed(self._add_holiday(name, may_21 + td(days=+1))) + self._add_observed(self._add_holiday_may_21(name), rule=SUN_TO_NEXT_TUE) + self._add_observed(self._add_holiday_may_22(name)) # Statehood Day. name = "Statehood Day" - jul_13 = self._add_holiday_jul_13(name) - self._add_observed(jul_13, days=+2) - self._add_observed(self._add_holiday(name, jul_13 + td(days=+1))) + self._add_observed(self._add_holiday_jul_13(name), rule=SUN_TO_NEXT_TUE) + self._add_observed(self._add_holiday_jul_14(name)) class ME(Montenegro): diff --git a/holidays/countries/mozambique.py b/holidays/countries/mozambique.py index b9bfe8a5a..58db11d12 100644 --- a/holidays/countries/mozambique.py +++ b/holidays/countries/mozambique.py @@ -9,32 +9,23 @@ # Website: https://github.com/dr-prodigy/python-holidays # License: MIT (see LICENSE file) -from datetime import date -from datetime import timedelta as td from gettext import gettext as tr from holidays.groups import ChristianHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ObservedHolidayBase, SUN_TO_NEXT_MON -class Mozambique(HolidayBase, ChristianHolidays, InternationalHolidays): +class Mozambique(ObservedHolidayBase, ChristianHolidays, InternationalHolidays): country = "MZ" default_language = "pt_MZ" + # %s (Observed). + observed_label = tr("%s (Ponte)") supported_languages = ("en_US", "pt_MZ", "uk") def __init__(self, *args, **kwargs): ChristianHolidays.__init__(self) InternationalHolidays.__init__(self) - super().__init__(*args, **kwargs) - - def _add_observed(self, dt: date) -> None: - """ - Whenever a public holiday falls on a Sunday, - it rolls over to the following Monday. - """ - if self.observed and self._is_sunday(dt): - # %s (Observed). - self._add_holiday(self.tr("%s (Ponte)") % self[dt], dt + td(days=+1)) + super().__init__(observed_rule=SUN_TO_NEXT_MON, *args, **kwargs) def _populate(self, year): if year <= 1974: diff --git a/holidays/countries/namibia.py b/holidays/countries/namibia.py index 9de2f3f14..ed8bc51f4 100644 --- a/holidays/countries/namibia.py +++ b/holidays/countries/namibia.py @@ -9,22 +9,26 @@ # Website: https://github.com/dr-prodigy/python-holidays # License: MIT (see LICENSE file) -from datetime import date -from datetime import timedelta as td - -from holidays.calendars.gregorian import JAN, FEB, DEC +from holidays.calendars.gregorian import JAN, DEC from holidays.groups import ChristianHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ObservedHolidayBase, SUN_TO_NEXT_MON -class Namibia(HolidayBase, ChristianHolidays, InternationalHolidays): +class Namibia(ObservedHolidayBase, ChristianHolidays, InternationalHolidays): """ https://www.officeholidays.com/countries/namibia https://www.timeanddate.com/holidays/namibia/ + https://tinyurl.com/lacorg5835 + As of 1991/2/1, whenever a public holiday falls on a Sunday, it rolls over to the monday, + unless that monday is already a public holiday. + Since the interval from 1991/1/1 to 1991/2/1 includes only New Year's Day, and it's a Tuesday, + we can assume that the beginning is 1991. """ country = "NA" + # %s (Observed). + observed_label = "%s (Observed)" special_holidays = { # https://gazettes.africa/archive/na/1999/na-government-gazette-dated-1999-11-22-no-2234.pdf 1999: (DEC, 31, "Y2K changeover"), @@ -34,15 +38,7 @@ class Namibia(HolidayBase, ChristianHolidays, InternationalHolidays): def __init__(self, *args, **kwargs): ChristianHolidays.__init__(self) InternationalHolidays.__init__(self) - super().__init__(*args, **kwargs) - - def _add_observed(self, dt: date) -> None: - # https://tinyurl.com/lacorg5835 - # As of 1991/2/1, whenever a public holiday falls on a Sunday, - # it rolls over to the monday, unless that monday is already - # a public holiday. - if self.observed and self._is_sunday(dt) and dt >= date(1991, FEB, 1): - self._add_holiday("%s (Observed)" % self[dt], dt + td(days=+1)) + super().__init__(observed_rule=SUN_TO_NEXT_MON, observed_since=1991, *args, **kwargs) def _populate(self, year): if year <= 1989: diff --git a/holidays/countries/new_zealand.py b/holidays/countries/new_zealand.py index dc87cba27..ff5f7988f 100644 --- a/holidays/countries/new_zealand.py +++ b/holidays/countries/new_zealand.py @@ -12,24 +12,19 @@ from datetime import date from datetime import timedelta as td -from holidays.calendars.gregorian import ( - JAN, - FEB, - MAR, - JUN, - JUL, - SEP, - NOV, - DEC, - MON, - _get_nth_weekday_from, -) +from holidays.calendars.gregorian import JAN, FEB, MAR, JUN, JUL, SEP, NOV, DEC from holidays.groups import ChristianHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ( + ObservedHolidayBase, + ALL_TO_NEAREST_MON, + SAT_SUN_TO_NEXT_MON, + SAT_SUN_TO_NEXT_MON_TUE, +) -class NewZealand(HolidayBase, ChristianHolidays, InternationalHolidays): +class NewZealand(ObservedHolidayBase, ChristianHolidays, InternationalHolidays): country = "NZ" + observed_label = "%s (Observed)" special_holidays = { 2022: (SEP, 26, "Queen Elizabeth II Memorial Day"), } @@ -78,20 +73,12 @@ class NewZealand(HolidayBase, ChristianHolidays, InternationalHolidays): def _get_nearest_monday(self, *args) -> date: dt = args if len(args) > 1 else args[0] dt = dt if isinstance(dt, date) else date(self._year, *dt) - return _get_nth_weekday_from( - +1 if self._is_friday(dt) or self._is_weekend(dt) else -1, MON, dt - ) - - def _add_observed(self, dt: date, days: int = +1) -> None: - if self.observed and self._is_weekend(dt): - self._add_holiday( - "%s (Observed)" % self[dt], dt + td(days=+2 if self._is_saturday(dt) else days) - ) + return self._get_observed_date(dt, rule=ALL_TO_NEAREST_MON) def __init__(self, *args, **kwargs): ChristianHolidays.__init__(self) InternationalHolidays.__init__(self) - super().__init__(*args, **kwargs) + super().__init__(observed_rule=SAT_SUN_TO_NEXT_MON, *args, **kwargs) def _populate(self, year): # Bank Holidays Act 1873 @@ -111,8 +98,10 @@ def _populate(self, year): super()._populate(year) # New Year's Day - self._add_observed(self._add_new_years_day("New Year's Day"), days=+2) - self._add_observed(self._add_new_years_day_two("Day after New Year's Day"), days=+2) + self._add_observed(self._add_new_years_day("New Year's Day"), rule=SAT_SUN_TO_NEXT_MON_TUE) + self._add_observed( + self._add_new_years_day_two("Day after New Year's Day"), rule=SAT_SUN_TO_NEXT_MON_TUE + ) # Waitangi Day if year >= 1974: @@ -194,10 +183,10 @@ def _populate(self, year): self._add_holiday_2nd_wed_of_oct(name) # Christmas Day - self._add_observed(self._add_christmas_day("Christmas Day"), days=+2) + self._add_observed(self._add_christmas_day("Christmas Day"), rule=SAT_SUN_TO_NEXT_MON_TUE) # Boxing Day - self._add_observed(self._add_christmas_day_two("Boxing Day"), days=+2) + self._add_observed(self._add_christmas_day_two("Boxing Day"), rule=SAT_SUN_TO_NEXT_MON_TUE) if self.subdiv == "Auckland": self._add_subdiv_auk_holidays() diff --git a/holidays/countries/nigeria.py b/holidays/countries/nigeria.py index cedfbdeb2..01c4b8bb2 100644 --- a/holidays/countries/nigeria.py +++ b/holidays/countries/nigeria.py @@ -9,19 +9,18 @@ # Website: https://github.com/dr-prodigy/python-holidays # License: MIT (see LICENSE file) -from datetime import timedelta as td - from holidays.calendars.gregorian import FEB, MAY -from holidays.groups import ChristianHolidays, IslamicHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase +from holidays.groups import ChristianHolidays, InternationalHolidays, IslamicHolidays +from holidays.observed_holiday_base import ObservedHolidayBase, SAT_SUN_TO_NEXT_WORKDAY -class Nigeria(HolidayBase, ChristianHolidays, InternationalHolidays, IslamicHolidays): +class Nigeria(ObservedHolidayBase, ChristianHolidays, InternationalHolidays, IslamicHolidays): """ https://en.wikipedia.org/wiki/Public_holidays_in_Nigeria """ country = "NG" + observed_label = "%s (Observed)" special_holidays = { 2019: ( (FEB, 22, "Public Holiday for Elections"), @@ -33,7 +32,9 @@ def __init__(self, *args, **kwargs): ChristianHolidays.__init__(self) InternationalHolidays.__init__(self) IslamicHolidays.__init__(self) - super().__init__(*args, **kwargs) + super().__init__( + observed_rule=SAT_SUN_TO_NEXT_WORKDAY, observed_since=2016, *args, **kwargs + ) def _populate(self, year): if year <= 1978: @@ -79,16 +80,8 @@ def _populate(self, year): # Birthday of Prophet Muhammad. dts_observed.update(self._add_mawlid_day("Eid-el-Mawlid")) - # Observed holidays. - if self.observed and year >= 2016: - for dt in sorted(dts_observed): - if not self._is_weekend(dt): - continue - dt_observed = dt + td(days=+1) - while self._is_weekend(dt_observed) or dt_observed in dts_observed: - dt_observed += td(days=+1) - for name in self.get_list(dt): - dts_observed.add(self._add_holiday("%s (Observed)" % name, dt_observed)) + if self.observed: + self._populate_observed(dts_observed) class NG(Nigeria): diff --git a/holidays/countries/panama.py b/holidays/countries/panama.py index 125a4b328..7a402bc8f 100644 --- a/holidays/countries/panama.py +++ b/holidays/countries/panama.py @@ -9,14 +9,11 @@ # Website: https://github.com/dr-prodigy/python-holidays # License: MIT (see LICENSE file) -from datetime import date -from datetime import timedelta as td - from holidays.groups import ChristianHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ObservedHolidayBase, SUN_TO_NEXT_MON -class Panama(HolidayBase, ChristianHolidays, InternationalHolidays): +class Panama(ObservedHolidayBase, ChristianHolidays, InternationalHolidays): """ References: - https://en.wikipedia.org/wiki/Public_holidays_in_Panama @@ -24,15 +21,12 @@ class Panama(HolidayBase, ChristianHolidays, InternationalHolidays): """ country = "PA" + observed_label = "%s (Observed)" def __init__(self, *args, **kwargs): ChristianHolidays.__init__(self) InternationalHolidays.__init__(self) - super().__init__(*args, **kwargs) - - def _add_observed(self, dt: date) -> None: - if self.observed and self._is_sunday(dt): - self._add_holiday("%s (Observed)" % self[dt], dt + td(days=+1)) + super().__init__(observed_rule=SUN_TO_NEXT_MON, *args, **kwargs) def _populate(self, year): super()._populate(year) diff --git a/holidays/countries/saudi_arabia.py b/holidays/countries/saudi_arabia.py index 5853482ec..31319c588 100644 --- a/holidays/countries/saudi_arabia.py +++ b/holidays/countries/saudi_arabia.py @@ -16,10 +16,18 @@ from holidays.calendars.gregorian import FEB, SEP, NOV, THU, FRI, SAT from holidays.groups import IslamicHolidays -from holidays.holiday_base import HolidayBase - - -class SaudiArabia(HolidayBase, IslamicHolidays): +from holidays.observed_holiday_base import ( + ObservedHolidayBase, + THU_TO_PREV_WED, + FRI_TO_PREV_THU, + FRI_TO_NEXT_SAT, + SAT_TO_NEXT_SUN, + THU_FRI_TO_NEXT_WORKDAY, + FRI_SAT_TO_NEXT_WORKDAY, +) + + +class SaudiArabia(ObservedHolidayBase, IslamicHolidays): """ There are only 4 official national holidays in Saudi: https://laboreducation.hrsd.gov.sa/en/gallery/274 @@ -49,35 +57,16 @@ class SaudiArabia(HolidayBase, IslamicHolidays): def __init__(self, *args, **kwargs): IslamicHolidays.__init__(self) - super().__init__(*args, **kwargs) + super().__init__(observed_rule=FRI_TO_PREV_THU + SAT_TO_NEXT_SUN, *args, **kwargs) def _add_islamic_observed(self, dts: Set[date]) -> None: # Observed days are added to make up for any days falling on a weekend. if not self.observed: return None - + observed_rule = THU_FRI_TO_NEXT_WORKDAY if self._year <= 2012 else FRI_SAT_TO_NEXT_WORKDAY for dt in dts: for i in range(4): - if not self._is_weekend(dt + td(days=-i)): - continue - dt_observed = dt + td(days=+1) - while dt_observed.year == self._year and ( - self._is_weekend(dt_observed) or dt_observed in self - ): - dt_observed += td(days=+1) - self._add_holiday(self.tr(self.observed_label) % self[dt], dt_observed) - - def _add_observed(self, dt: date) -> None: - if not self.observed: - return None - - weekend = sorted(self.weekend) - # 1st weekend day (Thursday before 2013 and Friday otherwise) - if dt.weekday() == weekend[0]: - self._add_holiday(self.tr(self.observed_label) % self.tr(self[dt]), dt + td(days=-1)) - # 2nd weekend day (Friday before 2013 and Saturday otherwise) - elif dt.weekday() == weekend[1]: - self._add_holiday(self.tr(self.observed_label) % self.tr(self[dt]), dt + td(days=+1)) + self._add_observed(dt + td(days=-i), name=self[dt], rule=observed_rule) def _populate(self, year): super()._populate(year) @@ -85,6 +74,11 @@ def _populate(self, year): # Weekend used to be THU, FRI before June 28th, 2013. # On that year both Eids were after that date, and Founding day # holiday started at 2022; so what below works. + self._observed_rule = ( + THU_TO_PREV_WED + FRI_TO_NEXT_SAT + if year <= 2012 + else FRI_TO_PREV_THU + SAT_TO_NEXT_SUN + ) self.weekend = {THU, FRI} if year <= 2012 else {FRI, SAT} # Eid al-Fitr Holiday @@ -103,7 +97,7 @@ def _populate(self, year): self._add_islamic_observed(self._add_eid_al_adha_day_three(name)) # If National Day happens within the Eid al-Fitr Holiday or - # within Eid al-Adha Holiday, there is no extra holidays given for it. + # Eid al-Adha Holiday, there is no extra holidays given for it. if year >= 2005: dt = date(year, SEP, 23) if dt not in self: @@ -111,7 +105,7 @@ def _populate(self, year): self._add_observed(self._add_holiday(tr("اليوم الوطني"), dt)) # If Founding Day happens within the Eid al-Fitr Holiday or - # within Eid al-Adha Holiday, there is no extra holidays given for it. + # Eid al-Adha Holiday, there is no extra holidays given for it. if year >= 2022: dt = date(year, FEB, 22) if dt not in self: diff --git a/holidays/countries/serbia.py b/holidays/countries/serbia.py index b83cabd7b..6881235e8 100644 --- a/holidays/countries/serbia.py +++ b/holidays/countries/serbia.py @@ -9,16 +9,14 @@ # Website: https://github.com/dr-prodigy/python-holidays # License: MIT (see LICENSE file) -from datetime import date -from datetime import timedelta as td from gettext import gettext as tr from holidays.calendars.julian import JULIAN_CALENDAR from holidays.groups import ChristianHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ObservedHolidayBase, SUN_TO_NEXT_MON, SUN_TO_NEXT_TUE -class Serbia(HolidayBase, ChristianHolidays, InternationalHolidays): +class Serbia(ObservedHolidayBase, ChristianHolidays, InternationalHolidays): """ Serbia holidays. @@ -28,23 +26,21 @@ class Serbia(HolidayBase, ChristianHolidays, InternationalHolidays): country = "RS" default_language = "sr" + # %s (Observed). + observed_label = tr("%s (слободан дан)") supported_languages = ("en_US", "sr") def __init__(self, *args, **kwargs): ChristianHolidays.__init__(self, JULIAN_CALENDAR) InternationalHolidays.__init__(self) - super().__init__(*args, **kwargs) - - def _add_observed(self, dt: date, days: int = +1) -> None: - if self.observed and self._is_sunday(dt): - self._add_holiday(self.tr("%s (слободан дан)") % self[dt], dt + td(days)) + super().__init__(observed_rule=SUN_TO_NEXT_MON, *args, **kwargs) def _populate(self, year): super()._populate(year) # New Year's Day. name = tr("Нова година") - self._add_observed(self._add_new_years_day(name), days=+2) + self._add_observed(self._add_new_years_day(name), rule=SUN_TO_NEXT_TUE) self._add_observed(self._add_new_years_day_two(name)) # Orthodox Christmas. @@ -52,14 +48,17 @@ def _populate(self, year): # Statehood Day. name = tr("Дан државности Србије") - self._add_observed(self._add_holiday_feb_15(name), days=+2) + self._add_observed(self._add_holiday_feb_15(name), rule=SUN_TO_NEXT_TUE) self._add_observed(self._add_holiday_feb_16(name)) # International Workers' Day. name = tr("Празник рада") - self._add_observed(self._add_labor_day(name), days=+2) - may_2 = self._add_labor_day_two(name) - self._add_observed(may_2, days=+2 if may_2 == self._easter_sunday else +1) + self._add_observed(self._add_labor_day(name), rule=SUN_TO_NEXT_TUE) + + self._add_observed( + may_2 := self._add_labor_day_two(name), + rule=SUN_TO_NEXT_TUE if may_2 == self._easter_sunday else SUN_TO_NEXT_MON, + ) # Armistice Day. self._add_observed(self._add_remembrance_day(tr("Дан примирја у Првом светском рату"))) diff --git a/holidays/countries/singapore.py b/holidays/countries/singapore.py index a7269004e..6e3479f17 100644 --- a/holidays/countries/singapore.py +++ b/holidays/countries/singapore.py @@ -9,8 +9,6 @@ # Website: https://github.com/dr-prodigy/python-holidays # License: MIT (see LICENSE file) -from datetime import timedelta as td - from holidays.calendars import ( _CustomBuddhistCalendar, _CustomChineseCalendar, @@ -26,11 +24,11 @@ InternationalHolidays, IslamicHolidays, ) -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ObservedHolidayBase, SUN_TO_NEXT_WORKDAY class Singapore( - HolidayBase, + ObservedHolidayBase, BuddhistCalendarHolidays, ChineseCalendarHolidays, ChristianHolidays, @@ -39,6 +37,7 @@ class Singapore( IslamicHolidays, ): country = "SG" + observed_label = "%s (Observed)" special_holidays = { 2001: (NOV, 3, "Polling Day"), 2006: (MAY, 6, "Polling Day"), @@ -56,7 +55,7 @@ class Singapore( 2023: (SEP, 1, "Polling Day"), } - def __init__(self, *args, **kwargs) -> None: + def __init__(self, *args, **kwargs): """ A subclass of :py:class:`HolidayBase` representing public holidays in Singapore. @@ -100,27 +99,31 @@ def __init__(self, *args, **kwargs) -> None: HinduCalendarHolidays.__init__(self, calendar=SingaporeHinduCalendar()) InternationalHolidays.__init__(self) IslamicHolidays.__init__(self, calendar=SingaporeIslamicCalendar()) - super().__init__(*args, **kwargs) + # Implement Section 4(2) of the Holidays Act: + # "if any day specified in the Schedule falls on a Sunday, + # the day next following not being itself a public holiday + # is declared a public holiday in Singapore." + super().__init__(observed_rule=SUN_TO_NEXT_WORKDAY, observed_since=1998, *args, **kwargs) def _populate(self, year) -> None: super()._populate(year) - observed_dates = set() + dts_observed = set() # New Year's Day - observed_dates.add(self._add_new_years_day("New Year's Day")) + dts_observed.add(self._add_new_years_day("New Year's Day")) # Chinese New Year (two days) name = "Chinese New Year" - observed_dates.add(self._add_chinese_new_years_day(name)) # type: ignore[arg-type] - observed_dates.add(self._add_chinese_new_years_day_two(name)) # type: ignore[arg-type] + dts_observed.add(self._add_chinese_new_years_day(name)) # type: ignore[arg-type] + dts_observed.add(self._add_chinese_new_years_day_two(name)) # type: ignore[arg-type] # Hari Raya Puasa (Eid al-Fitr) - observed_dates.update(self._add_eid_al_fitr_day("Hari Raya Puasa")) + dts_observed.update(self._add_eid_al_fitr_day("Hari Raya Puasa")) if year <= 1968: self._add_eid_al_fitr_day_two("Second day of Hari Raya Puasa") # Hari Raya Haji (Eid al-Adha) - observed_dates.update(self._add_eid_al_adha_day("Hari Raya Haji")) + dts_observed.update(self._add_eid_al_adha_day("Hari Raya Haji")) # Good Friday self._add_good_friday("Good Friday") @@ -133,42 +136,30 @@ def _populate(self, year) -> None: self._add_easter_monday("Easter Monday") # Labour Day - observed_dates.add(self._add_labor_day("Labour Day")) + dts_observed.add(self._add_labor_day("Labour Day")) # Vesak Day - observed_dates.add(self._add_vesak("Vesak Day")) # type: ignore[arg-type] + dts_observed.add(self._add_vesak("Vesak Day")) # type: ignore[arg-type] # National Day - observed_dates.add(self._add_holiday_aug_9("National Day")) + dts_observed.add(self._add_holiday_aug_9("National Day")) # Deepavali (Diwali) - observed_dates.add(self._add_diwali("Deepavali")) # type: ignore[arg-type] + dts_observed.add(self._add_diwali("Deepavali")) # type: ignore[arg-type] # Christmas Day - observed_dates.add(self._add_christmas_day("Christmas Day")) + dts_observed.add(self._add_christmas_day("Christmas Day")) # Boxing day (up to and including 1968) if year <= 1968: self._add_christmas_day_two("Boxing Day") - # Implement Section 4(2) of the Holidays Act: - # "if any day specified in the Schedule falls on a Sunday, - # the day next following not being itself a public holiday - # is declared a public holiday in Singapore." - if not self.observed: - return None - if year >= 1998: - for dt in sorted(observed_dates): - if not self._is_sunday(dt): - continue - self._add_holiday( - "%s (Observed)" % self[dt], - dt + td(days=2 if dt + td(days=+1) in observed_dates else 1), - ) - - # special case (observed from previous year) - if year == 2007: - self._add_holiday_jan_2("Hari Raya Haji (Observed)") + if self.observed: + self._populate_observed(dts_observed) + + # Observed holidays special cases (observed from previous year) + if year == 2007: + self._add_holiday_jan_2(self.observed_label % "Hari Raya Haji") class SG(Singapore): diff --git a/holidays/countries/south_africa.py b/holidays/countries/south_africa.py index 749357d0e..a6ca66191 100644 --- a/holidays/countries/south_africa.py +++ b/holidays/countries/south_africa.py @@ -9,21 +9,19 @@ # Website: https://github.com/dr-prodigy/python-holidays # License: MIT (see LICENSE file) -from datetime import date -from datetime import timedelta as td - from holidays.calendars.gregorian import JAN, MAR, APR, MAY, JUN, AUG, NOV, DEC from holidays.groups import ChristianHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ObservedHolidayBase, SUN_TO_NEXT_MON -class SouthAfrica(HolidayBase, ChristianHolidays, InternationalHolidays): +class SouthAfrica(ObservedHolidayBase, ChristianHolidays, InternationalHolidays): """ http://www.gov.za/about-sa/public-holidays https://en.wikipedia.org/wiki/Public_holidays_in_South_Africa """ country = "ZA" + observed_label = "%s (Observed)" special_holidays = { 1999: ( (JUN, 2, "National and provincial government elections"), @@ -51,13 +49,7 @@ class SouthAfrica(HolidayBase, ChristianHolidays, InternationalHolidays): def __init__(self, *args, **kwargs): ChristianHolidays.__init__(self) InternationalHolidays.__init__(self) - super().__init__(*args, **kwargs) - - def _add_observed(self, dt: date) -> None: - # As of 1995/1/1, whenever a public holiday falls on a Sunday, - # it rolls over to the following Monday - if self.observed and self._is_sunday(dt) and self._year >= 1995: - self._add_holiday("%s (Observed)" % self[dt], dt + td(days=+1)) + super().__init__(observed_rule=SUN_TO_NEXT_MON, observed_since=1995, *args, **kwargs) def _populate(self, year): # Observed since 1910, with a few name changes diff --git a/holidays/countries/spain.py b/holidays/countries/spain.py index 2aedc4152..267839652 100644 --- a/holidays/countries/spain.py +++ b/holidays/countries/spain.py @@ -9,21 +9,18 @@ # Website: https://github.com/dr-prodigy/python-holidays # License: MIT (see LICENSE file) -from datetime import date -from datetime import timedelta as td -from typing import Optional - from holidays.groups import ChristianHolidays, IslamicHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ObservedHolidayBase, SUN_TO_NEXT_MON -class Spain(HolidayBase, ChristianHolidays, IslamicHolidays, InternationalHolidays): +class Spain(ObservedHolidayBase, ChristianHolidays, InternationalHolidays, IslamicHolidays): """ References: - https://administracion.gob.es/pag_Home/atencionCiudadana/calendarios.html """ country = "ES" + observed_label = "%s (Trasladado)" subdivisions = ( "AN", "AR", @@ -50,17 +47,8 @@ def __init__(self, *args, **kwargs): ChristianHolidays.__init__(self) InternationalHolidays.__init__(self) IslamicHolidays.__init__(self) - super().__init__(*args, **kwargs) - - def _add_holiday(self, name: str, dt: date) -> Optional[date]: - if dt.year != self._year: - return None - if self.observed and self._is_sunday(dt): - dt += td(days=+1) - name = self.tr("%s (Trasladado)") % self.tr(name) - - return super()._add_holiday(self.tr(name), dt) + super().__init__(observed_rule=SUN_TO_NEXT_MON, *args, **kwargs) def _populate(self, year): super()._populate(year) @@ -93,35 +81,35 @@ def _populate(self, year): def _add_subdiv_an_holidays(self): if self._year == 2023: - self._add_new_years_day("Año nuevo") + self._add_observed(self._add_new_years_day("Año nuevo")) self._add_holiday_feb_28("Día de Andalucia") if self._year <= 2022: self._add_holy_thursday("Jueves Santo") if self._year == 2022: - self._add_labor_day("Día del Trabajador") - self._add_christmas_day("Navidad") + self._add_observed(self._add_labor_day("Día del Trabajador")) + self._add_observed(self._add_christmas_day("Navidad")) def _add_subdiv_ar_holidays(self): if self._year == 2023: - self._add_new_years_day("Año nuevo") + self._add_observed(self._add_new_years_day("Año nuevo")) if self._year <= 2014: self._add_saint_josephs_day("San José") if self._year <= 2022: self._add_holy_thursday("Jueves Santo") self._add_saint_georges_day("Día de San Jorge") if self._year == 2022: - self._add_labor_day("Día del Trabajador") - self._add_christmas_day("Navidad") + self._add_observed(self._add_labor_day("Día del Trabajador")) + self._add_observed(self._add_christmas_day("Navidad")) def _add_subdiv_as_holidays(self): if self._year == 2023: - self._add_new_years_day("Año nuevo") + self._add_observed(self._add_new_years_day("Año nuevo")) if self._year <= 2022: self._add_holy_thursday("Jueves Santo") self._add_holiday_sep_8("Día de Asturias") if self._year == 2022: - self._add_labor_day("Día del Trabajador") - self._add_christmas_day("Navidad") + self._add_observed(self._add_labor_day("Día del Trabajador")) + self._add_observed(self._add_christmas_day("Navidad")) def _add_subdiv_cb_holidays(self): if self._year <= 2022: @@ -129,7 +117,7 @@ def _add_subdiv_cb_holidays(self): self._add_holiday_jul_28("Día de las Instituciones de Cantabria") self._add_holiday_sep_15("Día de la Bien Aparecida") if self._year == 2022: - self._add_christmas_day("Navidad") + self._add_observed(self._add_christmas_day("Navidad")) def _add_subdiv_ce_holidays(self): if self._year <= 2022: @@ -143,7 +131,7 @@ def _add_subdiv_ce_holidays(self): def _add_subdiv_cl_holidays(self): if self._year == 2023: - self._add_new_years_day("Año nuevo") + self._add_observed(self._add_new_years_day("Año nuevo")) self._add_saint_james_day("Día de Santiago Apóstol") if self._year <= 2014: self._add_saint_josephs_day("San José") @@ -151,8 +139,8 @@ def _add_subdiv_cl_holidays(self): self._add_holy_thursday("Jueves Santo") self._add_holiday_apr_23("Día de Castilla y Leon") if self._year == 2022: - self._add_labor_day("Día del Trabajador") - self._add_christmas_day("Navidad") + self._add_observed(self._add_labor_day("Día del Trabajador")) + self._add_observed(self._add_christmas_day("Navidad")) def _add_subdiv_cm_holidays(self): if self._year <= 2015 or 2020 <= self._year <= 2021: @@ -165,14 +153,14 @@ def _add_subdiv_cm_holidays(self): self._add_corpus_christi_day("Corpus Christi") self._add_holiday_may_31("Día de Castilla La Mancha") if self._year == 2022: - self._add_christmas_day("Navidad") + self._add_observed(self._add_christmas_day("Navidad")) def _add_subdiv_cn_holidays(self): if self._year <= 2022: self._add_holy_thursday("Jueves Santo") self._add_holiday_may_30("Día de Canarias") if self._year == 2022: - self._add_christmas_day("Navidad") + self._add_observed(self._add_christmas_day("Navidad")) def _add_subdiv_ct_holidays(self): self._add_easter_monday("Lunes de Pascua") @@ -193,12 +181,12 @@ def _add_subdiv_ex_holidays(self): if self._year == 2023: self._add_carnival_tuesday("Carnaval") if self._year == 2022: - self._add_labor_day("Día del Trabajador") - self._add_christmas_day("Navidad") + self._add_observed(self._add_labor_day("Día del Trabajador")) + self._add_observed(self._add_christmas_day("Navidad")) def _add_subdiv_ga_holidays(self): if self._year <= 2014 or 2018 <= self._year <= 2021: - self._add_saint_josephs_day("San José") + self._add_observed(self._add_saint_josephs_day("San José")) if self._year <= 2022: self._add_holy_thursday("Jueves Santo") if self._year >= 2022: @@ -213,32 +201,32 @@ def _add_subdiv_ib_holidays(self): self._add_holy_thursday("Jueves Santo") self._add_easter_monday("Lunes de Pascua") if self._year == 2022: - self._add_christmas_day("Navidad") + self._add_observed(self._add_christmas_day("Navidad")) if self._year <= 2020: self._add_christmas_day_two("San Esteban") def _add_subdiv_mc_holidays(self): if self._year == 2023: - self._add_new_years_day("Año nuevo") + self._add_observed(self._add_new_years_day("Año nuevo")) if self._year <= 2021 and self._year != 2017: - self._add_saint_josephs_day("San José") + self._add_observed(self._add_saint_josephs_day("San José")) if self._year <= 2022: self._add_holy_thursday("Jueves Santo") if self._year == 2022: - self._add_labor_day("Día del Trabajador") + self._add_observed(self._add_labor_day("Día del Trabajador")) self._add_holiday_jun_9("Día de la Región de Murcia") if self._year == 2022: - self._add_christmas_day("Navidad") + self._add_observed(self._add_christmas_day("Navidad")) def _add_subdiv_md_holidays(self): if self._year <= 2015 or self._year == 2023: - self._add_saint_josephs_day("San José") + self._add_observed(self._add_saint_josephs_day("San José")) if self._year <= 2022: self._add_holy_thursday("Jueves Santo") self._add_holiday_may_2("Día de Comunidad de Madrid") if self._year == 2022: self._add_saint_james_day("Día de Santiago Apóstol") - self._add_christmas_day("Navidad") + self._add_observed(self._add_christmas_day("Navidad")) def _add_subdiv_ml_holidays(self): if self._year <= 2016: @@ -250,7 +238,7 @@ def _add_subdiv_ml_holidays(self): if self._year == 2022: self._add_eid_al_fitr_day_two("Eid al-Fitr") self._add_eid_al_adha_day_three("Eid al-Adha") - self._add_christmas_day("Navidad") + self._add_observed(self._add_christmas_day("Navidad")) elif self._year == 2023: self._add_eid_al_fitr_day("Eid al-Fitr") self._add_eid_al_adha_day_two("Eid al-Adha") @@ -264,7 +252,7 @@ def _add_subdiv_nc_holidays(self): if self._year >= 2022: self._add_saint_james_day("Día de Santiago Apóstol") if self._year == 2022: - self._add_christmas_day("Navidad") + self._add_observed(self._add_christmas_day("Navidad")) def _add_subdiv_pv_holidays(self): if self._year <= 2021: @@ -286,7 +274,7 @@ def _add_subdiv_ri_holidays(self): self._add_easter_monday("Lunes de Pascua") self._add_holiday_jun_9("Día de La Rioja") if self._year == 2022: - self._add_christmas_day("Navidad") + self._add_observed(self._add_christmas_day("Navidad")) def _add_subdiv_vc_holidays(self): if self._year <= 2022 and self._year != 2017: diff --git a/holidays/countries/taiwan.py b/holidays/countries/taiwan.py index 569ca1def..d2c4b8ced 100644 --- a/holidays/countries/taiwan.py +++ b/holidays/countries/taiwan.py @@ -9,14 +9,16 @@ # Website: https://github.com/dr-prodigy/python-holidays # License: MIT (see LICENSE file) -from datetime import timedelta as td - from holidays.calendars.gregorian import DEC from holidays.groups import ChineseCalendarHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ( + ObservedHolidayBase, + SAT_TO_PREV_WORKDAY, + SUN_TO_NEXT_WORKDAY, +) -class Taiwan(HolidayBase, ChineseCalendarHolidays, InternationalHolidays): +class Taiwan(ObservedHolidayBase, ChineseCalendarHolidays, InternationalHolidays): """ References: - https://en.wikipedia.org/wiki/Public_holidays_in_Taiwan @@ -28,11 +30,17 @@ class Taiwan(HolidayBase, ChineseCalendarHolidays, InternationalHolidays): """ country = "TW" + observed_label = "%s (Observed)" def __init__(self, *args, **kwargs): ChineseCalendarHolidays.__init__(self) InternationalHolidays.__init__(self) - super().__init__(*args, **kwargs) + super().__init__( + observed_rule=SAT_TO_PREV_WORKDAY + SUN_TO_NEXT_WORKDAY, + observed_since=2015, + *args, + **kwargs, + ) def _populate(self, year): if year <= 1911: @@ -45,8 +53,8 @@ def _populate(self, year): name = "Republic of China Founding Day / New Year's Day" dts_observed.add(self._add_new_years_day(name)) - if self.observed and year >= 2015 and self._is_friday(DEC, 31): - self._add_holiday_dec_31("%s (Observed)" % name) + if self.observed and self._is_friday(DEC, 31) and year >= 2015: + self._add_holiday_dec_31(self.observed_label % name) # Lunar New Year. self._add_chinese_new_years_eve("Lunar New Year's Eve") @@ -84,16 +92,8 @@ def _populate(self, year): # National Day. dts_observed.add(self._add_holiday_oct_10("National Day")) - if self.observed and year >= 2015: - for dt in sorted(dts_observed): - if not self._is_weekend(dt): - continue - delta = -1 if self._is_saturday(dt) else +1 - for name in self.get_list(dt): - dt_observed = dt + td(days=delta) - while dt_observed in dts_observed: - dt_observed += td(days=delta) - dts_observed.add(self._add_holiday("%s (Observed)" % name, dt_observed)) + if self.observed: + self._populate_observed(dts_observed, multiple=True) class TW(Taiwan): diff --git a/holidays/countries/thailand.py b/holidays/countries/thailand.py index 06b566e67..77d6bca1b 100644 --- a/holidays/countries/thailand.py +++ b/holidays/countries/thailand.py @@ -10,15 +10,22 @@ # License: MIT (see LICENSE file) from datetime import date -from datetime import timedelta as td from gettext import gettext as tr from holidays.calendars.gregorian import JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC from holidays.groups import InternationalHolidays, ThaiCalendarHolidays -from holidays.holiday_base import HolidayBase - - -class Thailand(HolidayBase, InternationalHolidays, ThaiCalendarHolidays): +from holidays.observed_holiday_base import ( + ObservedHolidayBase, + SAT_TO_NEXT_MON, + SAT_TO_NEXT_TUE, + THU_FRI_TO_NEXT_MON, + SAT_SUN_TO_NEXT_MON, + SAT_SUN_TO_NEXT_TUE, + SAT_SUN_TO_NEXT_MON_TUE, +) + + +class Thailand(ObservedHolidayBase, InternationalHolidays, ThaiCalendarHolidays): """ A subclass of :py:class:`HolidayBase` representing public holidays in Thailand. @@ -83,6 +90,21 @@ class Thailand(HolidayBase, InternationalHolidays, ThaiCalendarHolidays): https://www.lib.ru.ac.th/journal/may/may_phauchmongkol.html https://www.myhora.com/ปฏิทิน/ปฏิทิน-พ.ศ.2540.aspx + !!! If Public Holiday falls on weekends, (in lieu) on workday !!! + Despite the wording, this usually only applies to Monday only for + holidays, consecutive holidays all have their own special in lieu + declared separately. + + Data from 1992-1994 and 1998-2000 are declared discretely in + special_holidays declarations above. + + Applied Automatically for Monday if on Weekends: 1961-1973 + **NOTE: No New Year's Eve (in lieu) for this period + No In Lieu days available: 1974-1988 + Case-by-Case application for Workday if on Weekends: 1989-1994 + Applied Automatically for Workday if on Weekends: 1995-1997 + Case-by-Case application for Workday if on Weekends: 1998-2000 + Applied Automatically for Workday if on Weekends: 2001-Present Limitations: @@ -110,6 +132,8 @@ class Thailand(HolidayBase, InternationalHolidays, ThaiCalendarHolidays): country = "TH" default_language = "th" + # %s (in lieu). + observed_label = tr("ชดเชย%s") # วันหยุดพิเศษ (เพิ่มเติม) - see Bank of Thailand's DB for Cross-Check. @@ -255,10 +279,13 @@ class Thailand(HolidayBase, InternationalHolidays, ThaiCalendarHolidays): } supported_languages = ("en_US", "th") - def __init__(self, **kwargs) -> None: + def __init__(self, *args, **kwargs): InternationalHolidays.__init__(self) ThaiCalendarHolidays.__init__(self) - super().__init__(**kwargs) + super().__init__(observed_rule=SAT_SUN_TO_NEXT_MON, *args, **kwargs) + + def _is_observed(self, dt: date) -> bool: + return 1961 <= self._year <= 1973 or 1995 <= self._year <= 1997 or self._year >= 2001 def _populate(self, year): # Due to Thai Calendar Migration, this is capped off at 1941. @@ -266,34 +293,6 @@ def _populate(self, year): if year <= 1940: return None - def _add_observed(dt: date) -> None: - """ - !!! If Public Holiday falls on weekends, (in lieu) on workday !!! - Despite the wording, this usually only applies to Monday only for - holidays, consecutive holidays all have their own special in lieu - declared separately. - - Data from 1992-1994 and 1998-2000 are declared discretely in - special_holidays declarations above. - - Applied Automatically for Monday if on Weekends: 1961-1973 - **NOTE: No New Year's Eve (in lieu) for this period - No In Lieu days available: 1974-1988 - Case-by-Case application for Workday if on Weekends: 1989-1994 - Applied Automatically for Workday if on Weekends: 1995-1997 - Case-by-Case application for Workday if on Weekends: 1998-2000 - Applied Automatically for Workday if on Weekends: 2001-Present - """ - if ( - self.observed - and self._is_weekend(dt) - and (1961 <= year <= 1973 or 1995 <= year <= 1997 or year >= 2001) - ): - in_lieu = dt + td(days=+2 if self._is_saturday(dt) else +1) - for name in self.get_list(dt): - # %s (in lieu) - self._add_holiday(self.tr("ชดเชย%s") % name, in_lieu) - super()._populate(year) # Fixed Date Holidays @@ -304,24 +303,7 @@ def _add_observed(dt: date) -> None: # TODO: Add check for 1941 if we support earlier dates. # New Year's Day. - jan_1 = self._add_new_years_day(tr("วันขึ้นปีใหม่")) - _add_observed(jan_1) - - # วันหยุดชดเชยวันสิ้นปี - # Status: In-Use. - # Added separately from New Year's Eve itself so that it would't - # go over the next year. - # - CASE 1: SAT-SUN -> 1 in-lieu on TUE. - # - CASE 2: SUN-MON -> 1 in-lieu on TUE. - # See in lieu logic in `_add_observed(dt: date)`. - - new_years_eve_in_lieu = self.tr("ชดเชย%s") % self.tr("วันสิ้นปี") - - if self.observed and (1995 <= year <= 1997 or year >= 2001): - if self._is_sunday(jan_1): - self._add_new_years_day_three(new_years_eve_in_lieu) - elif self._is_monday(jan_1): - self._add_new_years_day_two(new_years_eve_in_lieu) + self._add_observed(self._add_new_years_day(tr("วันขึ้นปีใหม่"))) # วันจักรี # Status: In-Use. @@ -329,7 +311,7 @@ def _add_observed(dt: date) -> None: # TODO: Add check for 1918 if we support earlier dates. # Chakri Memorial Day. - _add_observed(self._add_holiday_apr_6(tr("วันจักรี"))) + self._add_observed(self._add_holiday_apr_6(tr("วันจักรี"))) # วันสงกรานต์ # Status: In-Use. @@ -344,20 +326,19 @@ def _add_observed(dt: date) -> None: # (Except for 2020 due to Covid-19 outbreaks) # This has its own in-lieu trigger. - if year >= 1948: + if 1948 <= year <= 1953 or (1957 <= year != 2020): # Songkran Festival. songkran_festival = tr("วันสงกรานต์") - if year <= 1953 or (1957 <= year != 2020): - dt = ( - self._add_holiday_apr_12(songkran_festival) - if 1989 <= year <= 1997 - else self._add_holiday_apr_13(songkran_festival) - ) - if 1957 <= year <= 1988: - _add_observed(dt) - else: - self._add_holiday(songkran_festival, dt + td(days=+1)) - self._add_holiday(songkran_festival, dt + td(days=+2)) + if 1957 <= year <= 1988: + self._add_observed(self._add_holiday_apr_13(songkran_festival)) + elif 1989 <= year <= 1997: + dt = self._add_holiday_apr_12(songkran_festival) + self._add_holiday_apr_13(songkran_festival) + self._add_holiday_apr_14(songkran_festival) + else: + dt = self._add_holiday_apr_13(songkran_festival) + self._add_holiday_apr_14(songkran_festival) + self._add_holiday_apr_15(songkran_festival) # วันหยุดชดเชยวันสงกรานต์ # If Songkran happened to be held on the weekends, only one in-lieu @@ -367,14 +348,8 @@ def _add_observed(dt: date) -> None: # - CASE 3: SAT-SUN-MON -> 1 in-lieu on TUE # See in lieu logic in `_add_observed(dt: date)`. # Status: In Use. - - songkran_festival_in_lieu = self.tr("ชดเชย%s") % songkran_festival - - if self.observed and (1995 <= year <= 1997 or 2001 <= year != 2020): - if self._is_thursday(dt): - self._add_holiday(songkran_festival_in_lieu, dt + td(days=+4)) - elif self._is_friday(dt) or self._is_saturday(dt): - self._add_holiday(songkran_festival_in_lieu, dt + td(days=+3)) + if year >= 1995: + self._add_observed(dt, rule=THU_FRI_TO_NEXT_MON + SAT_TO_NEXT_TUE) # วันแรงงานแห่งชาติ # Status: In-Use. @@ -384,7 +359,7 @@ def _add_observed(dt: date) -> None: if year >= 1974: # National Labour day. - _add_observed(self._add_labor_day(tr("วันแรงงานแห่งชาติ"))) + self._add_observed(self._add_labor_day(tr("วันแรงงานแห่งชาติ"))) # วันชาติ # Status: In-Use. @@ -394,7 +369,7 @@ def _add_observed(dt: date) -> None: # National Day. name = tr("วันชาติ") - _add_observed( + self._add_observed( self._add_holiday_jun_24(name) if year <= 1959 else self._add_holiday_dec_5(name) ) @@ -407,16 +382,16 @@ def _add_observed(dt: date) -> None: coronation_day = tr("วันฉัตรมงคล") if 1958 <= year <= 2016: - _add_observed(self._add_holiday_may_5(coronation_day)) + self._add_observed(self._add_holiday_may_5(coronation_day)) elif year >= 2020: - _add_observed(self._add_holiday_may_4(coronation_day)) + self._add_observed(self._add_holiday_may_4(coronation_day)) # วันเฉลิมพระชนมพรรษา พระราชินี # Status: In-Use. # Starts in 2019 (B.E. 2562). if year >= 2019: - _add_observed( + self._add_observed( # HM Queen Suthida's Birthday. self._add_holiday_jun_3( tr("วันเฉลิมพระชนมพรรษาสมเด็จพระนางเจ้าสุทิดา พัชรสุธาพิมลลักษณ พระบรมราชินี") @@ -428,7 +403,7 @@ def _add_observed(dt: date) -> None: # Started in 2017 (B.E 2560). if year >= 2017: - _add_observed( + self._add_observed( self._add_holiday_jul_28( # HM King Maha Vajiralongkorn's Birthday. tr( @@ -453,7 +428,7 @@ def _add_observed(dt: date) -> None: # HM Queen Sirikit's Birthday. else tr("วันเฉลิมพระชนมพรรษาสมเด็จพระนางเจ้าสิริกิติ์ พระบรมราชินีนาถ") ) - _add_observed(self._add_holiday_aug_12(name)) + self._add_observed(self._add_holiday_aug_12(name)) # วันแม่แห่งชาติ # Status: In-Use. @@ -466,9 +441,9 @@ def _add_observed(dt: date) -> None: thai_mothers_day = tr("วันแม่แห่งชาติ") if 1950 <= year <= 1957: - _add_observed(self._add_holiday_apr_15(thai_mothers_day)) + self._add_observed(self._add_holiday_apr_15(thai_mothers_day)) elif year >= 1976: - _add_observed(self._add_holiday_aug_12(thai_mothers_day)) + self._add_observed(self._add_holiday_aug_12(thai_mothers_day)) # วันคล้ายวันสวรรคตพระบาทสมเด็จพระปรมินทร มหาภูมิพลอดุลยเดช บรมนาถบพิตร # Status: In-Use. @@ -486,7 +461,7 @@ def _add_observed(dt: date) -> None: # Anniversary for the Death of King Bhumibol Adulyadej. else tr("วันคล้ายวันสวรรคตพระบาทสมเด็จพระปรมินทรมหาภูมิพลอดุลยเดช บรมนาถบพิตร") ) - _add_observed(self._add_holiday_oct_13(name)) + self._add_observed(self._add_holiday_oct_13(name)) # วันปิยมหาราช # Status: In-Use. @@ -494,7 +469,7 @@ def _add_observed(dt: date) -> None: # TODO: Add check for 1911 if we support earlier dates. # HM King Chulalongkorn Memorial Day. - _add_observed(self._add_holiday_oct_23(tr("วันปิยมหาราช"))) + self._add_observed(self._add_holiday_oct_23(tr("วันปิยมหาราช"))) # วันเฉลิมพระชนมพรรษา รัชกาลที่ 9 (1960-2016) # วันคล้ายวันเฉลิมพระชนมพรรษา รัชกาลที่ 9 (2017-Present) @@ -524,7 +499,7 @@ def _add_observed(dt: date) -> None: ) ) ) - _add_observed(self._add_holiday_dec_5(name)) + self._add_observed(self._add_holiday_dec_5(name)) # วันพ่อแห่งชาติ # Status: In-Use. @@ -534,7 +509,7 @@ def _add_observed(dt: date) -> None: if year >= 1980: # National Father's Day. - _add_observed(self._add_holiday_dec_5(tr("วันพ่อแห่งชาติ"))) + self._add_observed(self._add_holiday_dec_5(tr("วันพ่อแห่งชาติ"))) # วันรัฐธรรมนูญ # Status: In-Use. @@ -543,7 +518,7 @@ def _add_observed(dt: date) -> None: # TODO: Add check for 1932 if we support earlier dates. # Constitution Day. - _add_observed(self._add_holiday_dec_10(tr("วันรัฐธรรมนูญ"))) + self._add_observed(self._add_holiday_dec_10(tr("วันรัฐธรรมนูญ"))) # วันสิ้นปี # Status: In-Use. @@ -553,7 +528,19 @@ def _add_observed(dt: date) -> None: # This has its own in-lieu trigger. # New Year's Eve. - self._add_new_years_eve(tr("วันสิ้นปี")) + name = tr("วันสิ้นปี") + self._add_new_years_eve(name) + + # วันหยุดชดเชยวันสิ้นปี + # Status: In-Use. + # Added separately from New Year's Eve itself so that it would't + # go over the next year. + # - CASE 1: SAT-SUN -> 1 in-lieu on TUE. + # - CASE 2: SUN-MON -> 1 in-lieu on TUE. + # See in lieu logic in `_add_observed(dt: date)`. + + if year >= 1995: + self._add_observed(date(year - 1, DEC, 31), name=name, rule=SAT_SUN_TO_NEXT_TUE) # Thai Lunar Calendar Holidays # See `_ThaiLunisolar` in holidays/utils.py for more details. @@ -565,7 +552,7 @@ def _add_observed(dt: date) -> None: # Makha Bucha. makha_bucha_date = self._add_makha_bucha(tr("วันมาฆบูชา")) if makha_bucha_date: - _add_observed(makha_bucha_date) + self._add_observed(makha_bucha_date) # วันวิสาขบูชา # Status: In-Use. @@ -573,43 +560,28 @@ def _add_observed(dt: date) -> None: # Visakha Bucha. visakha_bucha_date = self._add_visakha_bucha(tr("วันวิสาขบูชา")) if visakha_bucha_date: - _add_observed(visakha_bucha_date) + self._add_observed(visakha_bucha_date) # วันอาสาฬหบูชา # Status: In-Use. - # This has its own in-lieu trigger. + # วันหยุดชดเชยวันอาสาฬหบูชา + # วันหยุดชดเชยวันเข้าพรรษา + # - CASE 1: FRI-SAT -> 1 in-lieu on MON + # - CASE 2: SAT-SUN -> 1 in-lieu on MON + # - CASE 3: SUN-MON -> 1 in-lieu on TUE # Asarnha Bucha. asarnha_bucha_date = self._add_asarnha_bucha(tr("วันอาสาฬหบูชา")) + if asarnha_bucha_date: + self._add_observed(asarnha_bucha_date, rule=SAT_SUN_TO_NEXT_MON_TUE) # วันเข้าพรรษา # Status: In-Use. - # This has its own in-lieu trigger. # Buddhist Lent Day. - self._add_khao_phansa(tr("วันเข้าพรรษา")) - - # วันหยุดชดเชยวันอาสาฬหบูชา - # วันหยุดชดเชยวันเข้าพรรษา - # Status: In Use. - # - CASE 1: FRI-SAT -> 1 in-lieu on MON - # - CASE 2: SAT-SUN -> 1 in-lieu on MON - # - CASE 3: SUN-MON -> 1 in-lieu on TUE - # See in lieu logic in `_add_observed(dt: date)`. - - if ( - asarnha_bucha_date - and self.observed - and (1961 <= year <= 1973 or 1995 <= year <= 1997 or year >= 2001) - ): - if self._is_friday(asarnha_bucha_date): - self._add_holiday( - self.tr("ชดเชย%s") % self.tr("วันเข้าพรรษา"), asarnha_bucha_date + td(days=+3) - ) - elif self._is_weekend(asarnha_bucha_date): - self._add_holiday( - self.tr("ชดเชย%s") % self.tr("วันอาสาฬหบูชา"), asarnha_bucha_date + td(days=+2) - ) + khao_phansa_date = self._add_khao_phansa(tr("วันเข้าพรรษา")) + if khao_phansa_date: + self._add_observed(khao_phansa_date, rule=SAT_TO_NEXT_MON) # No Future Fixed Date Holidays @@ -656,10 +628,10 @@ def _add_observed(dt: date) -> None: } # For years with exact date data. if year in raeknakhwan_dates: - _add_observed(self._add_holiday(raeknakhwan, raeknakhwan_dates[year])) + self._add_observed(self._add_holiday(raeknakhwan, raeknakhwan_dates[year])) # Approx. otherwise for 1957-2013. elif 1957 <= year <= 1996: - _add_observed(self._add_holiday_may_13(raeknakhwan)) + self._add_observed(self._add_holiday_may_13(raeknakhwan)) class TH(Thailand): diff --git a/holidays/countries/ukraine.py b/holidays/countries/ukraine.py index 6bebc1d6a..df52b64d6 100644 --- a/holidays/countries/ukraine.py +++ b/holidays/countries/ukraine.py @@ -10,7 +10,6 @@ # License: MIT (see LICENSE file) from datetime import date -from datetime import timedelta as td from gettext import gettext as tr from holidays.calendars.gregorian import ( @@ -29,10 +28,10 @@ ) from holidays.calendars.julian import JULIAN_CALENDAR from holidays.groups import ChristianHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ObservedHolidayBase, SAT_SUN_TO_NEXT_WORKDAY -class Ukraine(HolidayBase, ChristianHolidays, InternationalHolidays): +class Ukraine(ObservedHolidayBase, ChristianHolidays, InternationalHolidays): """ Current holidays list: https://zakon1.rada.gov.ua/laws/show/322-08/paran454#n454 @@ -68,6 +67,8 @@ class Ukraine(HolidayBase, ChristianHolidays, InternationalHolidays): country = "UA" default_language = "uk" + # %s (Observed). + observed_label = tr("%s (вихідний)") supported_languages = ("ar", "en_US", "uk") # Date format (see strftime() Format Codes) substituted_date_format = tr("%d.%m.%Y") @@ -192,7 +193,16 @@ class Ukraine(HolidayBase, ChristianHolidays, InternationalHolidays): def __init__(self, *args, **kwargs): ChristianHolidays.__init__(self, JULIAN_CALENDAR) InternationalHolidays.__init__(self) - super().__init__(*args, **kwargs) + super().__init__(observed_rule=SAT_SUN_TO_NEXT_WORKDAY, *args, **kwargs) + + def _is_observed(self, dt: date) -> bool: + # 27.01.1995: holiday on weekend move to next workday + # https://zakon.rada.gov.ua/laws/show/35/95-вр + # 10.01.1998: cancelled + # https://zakon.rada.gov.ua/laws/show/785/97-вр + # 23.04.1999: holiday on weekend move to next workday + # https://zakon.rada.gov.ua/laws/show/576-14 + return date(1995, JAN, 27) <= dt <= date(1998, JAN, 9) or dt >= date(1999, APR, 23) def _populate(self, year): # The current set of holidays came into force in 1991 @@ -284,23 +294,8 @@ def _populate(self, year): ) ) - # 27.01.1995: holiday on weekend move to next workday - # https://zakon.rada.gov.ua/laws/show/35/95-вр - # 10.01.1998: cancelled - # https://zakon.rada.gov.ua/laws/show/785/97-вр - # 23.04.1999: holiday on weekend move to next workday - # https://zakon.rada.gov.ua/laws/show/576-14 - if not self.observed: - return None - for dt in sorted(dts_observed): - if self._is_weekend(dt) and ( - date(1995, JAN, 27) <= dt <= date(1998, JAN, 9) or dt >= date(1999, APR, 23) - ): - dt_observed = dt + td(days=+2 if self._is_saturday(dt) else +1) - while dt_observed in self: - dt_observed += td(days=+1) - for name in self.get_list(dt): - self._add_holiday(self.tr("%s (вихідний)") % name, dt_observed) + if self.observed: + self._populate_observed(dts_observed) class UA(Ukraine): diff --git a/holidays/countries/united_kingdom.py b/holidays/countries/united_kingdom.py index bac8649a7..5d7e66a50 100644 --- a/holidays/countries/united_kingdom.py +++ b/holidays/countries/united_kingdom.py @@ -9,21 +9,27 @@ # Website: https://github.com/dr-prodigy/python-holidays # License: MIT (see LICENSE file) -from datetime import date -from datetime import timedelta as td from typing import Tuple, Union from holidays.calendars.gregorian import APR, MAY, JUN, JUL, SEP, DEC from holidays.groups import ChristianHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ( + ObservedHolidayBase, + MON_TO_NEXT_TUE, + SAT_TO_NEXT_TUE, + SUN_TO_NEXT_MON, + SAT_SUN_TO_NEXT_MON, + SAT_SUN_TO_NEXT_MON_TUE, +) -class UnitedKingdom(HolidayBase, ChristianHolidays, InternationalHolidays): +class UnitedKingdom(ObservedHolidayBase, ChristianHolidays, InternationalHolidays): """ https://en.wikipedia.org/wiki/Public_holidays_in_the_United_Kingdom """ country = "GB" + observed_label = "%s (Observed)" special_holidays = { 1977: (JUN, 7, "Silver Jubilee of Elizabeth II"), 1981: (JUL, 29, "Wedding of Charles and Diana"), @@ -54,13 +60,7 @@ class UnitedKingdom(HolidayBase, ChristianHolidays, InternationalHolidays): def __init__(self, *args, **kwargs): ChristianHolidays.__init__(self) InternationalHolidays.__init__(self) - super().__init__(*args, **kwargs) - - def _add_observed(self, dt: date, days: int = +1) -> None: - if self.observed and self._is_weekend(dt): - self._add_holiday( - "%s (Observed)" % self[dt], dt + td(days=+2 if self._is_saturday(dt) else days) - ) + super().__init__(observed_rule=SAT_SUN_TO_NEXT_MON, *args, **kwargs) def _populate(self, year: int) -> None: super()._populate(year) @@ -105,10 +105,14 @@ def _add_subdiv_holidays(self): self._add_observed(self._add_new_years_day("New Year's Day")) # Christmas Day - self._add_observed(self._add_christmas_day("Christmas Day"), days=+2) + self._add_observed( + self._add_christmas_day("Christmas Day"), rule=SAT_SUN_TO_NEXT_MON_TUE + ) # Boxing Day - self._add_observed(self._add_christmas_day_two("Boxing Day"), days=+2) + self._add_observed( + self._add_christmas_day_two("Boxing Day"), rule=SAT_SUN_TO_NEXT_MON_TUE + ) super()._add_subdiv_holidays() @@ -137,19 +141,14 @@ def _add_subdiv_nir_holidays(self): def _add_subdiv_sct_holidays(self): # New Year's Day - name = "New Year's Day" - jan_1 = self._add_new_years_day(name) - if self.observed and self._is_weekend(jan_1): - self._add_holiday( - "%s (Observed)" % name, jan_1 + td(days=+3 if self._is_saturday(jan_1) else +1) - ) + jan_1 = self._add_new_years_day("New Year's Day") # New Year Holiday - name = "New Year Holiday" - jan_2 = self._add_new_years_day_two(name) - self._add_observed(jan_2) - if self.observed and self._is_monday(jan_2): - self._add_new_years_day_three("%s (Observed)" % name) + self._add_observed( + self._add_new_years_day_two("New Year Holiday"), + rule=MON_TO_NEXT_TUE + SAT_SUN_TO_NEXT_MON, + ) + self._add_observed(jan_1, rule=SAT_TO_NEXT_TUE + SUN_TO_NEXT_MON) # Summer bank holiday (first Monday in August) self._add_holiday_1st_mon_of_aug("Summer Bank Holiday") @@ -160,12 +159,15 @@ def _add_subdiv_sct_holidays(self): # Christmas Day self._add_observed( - self._add_christmas_day("Christmas Day"), days=+2 if self._year >= 1974 else +1 + self._add_christmas_day("Christmas Day"), + rule=SAT_SUN_TO_NEXT_MON_TUE if self._year >= 1974 else SAT_SUN_TO_NEXT_MON, ) if self._year >= 1974: # Boxing Day - self._add_observed(self._add_christmas_day_two("Boxing Day"), days=+2) + self._add_observed( + self._add_christmas_day_two("Boxing Day"), rule=SAT_SUN_TO_NEXT_MON_TUE + ) def _add_subdiv_wls_holidays(self): # Easter Monday diff --git a/holidays/countries/united_states.py b/holidays/countries/united_states.py index f1f0f13c6..6cfa821de 100644 --- a/holidays/countries/united_states.py +++ b/holidays/countries/united_states.py @@ -9,16 +9,22 @@ # Website: https://github.com/dr-prodigy/python-holidays # License: MIT (see LICENSE file) -from datetime import date -from datetime import timedelta as td from typing import Tuple, Union -from holidays.calendars.gregorian import DEC, FRI, _get_nth_weekday_from +from holidays.calendars.gregorian import DEC from holidays.groups import ChristianHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase - - -class UnitedStates(HolidayBase, ChristianHolidays, InternationalHolidays): +from holidays.observed_holiday_base import ( + ObservedHolidayBase, + MON_TO_NEXT_TUE, + FRI_TO_PREV_THU, + SAT_TO_PREV_FRI, + SUN_TO_NEXT_MON, + SAT_SUN_TO_PREV_FRI, + SAT_SUN_TO_NEXT_MON, +) + + +class UnitedStates(ObservedHolidayBase, ChristianHolidays, InternationalHolidays): """ https://en.wikipedia.org/wiki/Public_holidays_in_the_United_States @@ -28,6 +34,7 @@ class UnitedStates(HolidayBase, ChristianHolidays, InternationalHolidays): """ country = "US" + observed_label = "%s (Observed)" subdivisions: Union[Tuple[()], Tuple[str, ...]] = ( "AK", "AL", @@ -94,15 +101,7 @@ class UnitedStates(HolidayBase, ChristianHolidays, InternationalHolidays): def __init__(self, *args, **kwargs): ChristianHolidays.__init__(self) InternationalHolidays.__init__(self) - super().__init__(*args, **kwargs) - - def _add_observed(self, dt: date, before: bool = True, after: bool = True) -> None: - if not self.observed: - return None - if self._is_saturday(dt) and before: - self._add_holiday("%s (Observed)" % self[dt], dt + td(days=-1)) - elif self._is_sunday(dt) and after: - self._add_holiday("%s (Observed)" % self[dt], dt + td(days=+1)) + super().__init__(observed_rule=SAT_TO_PREV_FRI + SUN_TO_NEXT_MON, *args, **kwargs) def _populate(self, year): super()._populate(year) @@ -110,11 +109,11 @@ def _populate(self, year): # New Year's Day if year >= 1871: name = "New Year's Day" - self._add_observed(self._add_new_years_day(name), before=False) + self._add_observed(self._add_new_years_day(name)) # The following year's observed New Year's Day can be in this year # when it falls on a Friday (Jan 1st is a Saturday). if self.observed and self._is_friday(DEC, 31): - self._add_holiday_dec_31("%s (Observed)" % name) + self._add_holiday_dec_31(self.observed_label % name) # Memorial Day if year >= 1888: @@ -154,15 +153,12 @@ def _populate(self, year): def _add_christmas_eve_holiday(self): # Christmas Eve - name = "Christmas Eve" - dec_24 = self._add_christmas_eve(name) - if self.observed: - # If on Friday, observed on Thursday - if self._is_friday(dec_24): - self._add_holiday("%s (Observed)" % name, dec_24 + td(days=-1)) - # If on Saturday or Sunday, observed on Friday - elif self._is_weekend(dec_24): - self._add_holiday("%s (Observed)" % name, _get_nth_weekday_from(-1, FRI, dec_24)) + # If on Friday, observed on Thursday + # If on Saturday or Sunday, observed on Friday + self._add_observed( + self._add_christmas_eve("Christmas Eve"), + rule=FRI_TO_PREV_THU + SAT_SUN_TO_PREV_FRI, + ) def _add_subdiv_holidays(self): # Martin Luther King Jr. Day @@ -275,7 +271,7 @@ def _add_subdiv_ca_holidays(self): # Cesar Chavez Day if self._year >= 1995: - self._add_observed(self._add_holiday_mar_31("Cesar Chavez Day"), before=False) + self._add_observed(self._add_holiday_mar_31("Cesar Chavez Day"), rule=SUN_TO_NEXT_MON) # Day After Thanksgiving if self._year >= 1975: @@ -300,7 +296,7 @@ def _add_subdiv_dc_holidays(self): self._add_holiday_jan_20(name) if self._year >= 1937 else self._add_holiday_mar_4(name), - before=False, + rule=SUN_TO_NEXT_MON, ) # Emancipation Day @@ -450,7 +446,7 @@ def _add_subdiv_ky_holidays(self): # New Year's Eve if self._year >= 2013: - self._add_observed(self._add_new_years_eve("New Year's Eve"), after=False) + self._add_observed(self._add_new_years_eve("New Year's Eve")) def _add_subdiv_la_holidays(self): # Inauguration Day @@ -460,7 +456,7 @@ def _add_subdiv_la_holidays(self): self._add_holiday_jan_20(name) if self._year >= 1937 else self._add_holiday_mar_4(name), - before=False, + rule=SUN_TO_NEXT_MON, ) # Mardi Gras @@ -477,10 +473,9 @@ def _add_subdiv_la_holidays(self): def _add_subdiv_ma_holidays(self): # Evacuation Day if self._year >= 1901: - name = "Evacuation Day" - mar_17 = self._add_holiday_mar_17(name) - if self.observed and self._is_weekend(mar_17): - self._add_holiday_1st_mon_from_mar_17("%s (Observed)" % name) + self._add_observed( + self._add_holiday_mar_17("Evacuation Day"), rule=SAT_SUN_TO_NEXT_MON + ) # Patriots' Day if self._year >= 1894: @@ -498,7 +493,7 @@ def _add_subdiv_md_holidays(self): self._add_holiday_jan_20(name) if self._year >= 1937 else self._add_holiday_mar_4(name), - before=False, + rule=SUN_TO_NEXT_MON, ) # American Indian Heritage Day @@ -523,7 +518,7 @@ def _add_subdiv_mi_holidays(self): self._add_christmas_eve_holiday() # New Year's Eve - self._add_observed(self._add_new_years_eve("New Year's Eve"), after=False) + self._add_observed(self._add_new_years_eve("New Year's Eve")) def _add_subdiv_mn_holidays(self): pass @@ -583,15 +578,12 @@ def _add_subdiv_nc_holidays(self): # Day After Christmas if self._year >= 2013: - name = "Day After Christmas" - dec_26 = self._add_christmas_day_two(name) - if self.observed: - # If on Saturday or Sunday, observed on Monday - if self._is_weekend(dec_26): - self._add_holiday_1st_mon_from_dec_26("%s (Observed)" % name) - # If on Monday, observed on Tuesday - elif self._is_monday(dec_26): - self._add_holiday("%s (Observed)" % name, dec_26 + td(days=+1)) + # If on Saturday or Sunday, observed on Monday + # If on Monday, observed on Tuesday + self._add_observed( + self._add_christmas_day_two("Day After Christmas"), + rule=MON_TO_NEXT_TUE + SAT_SUN_TO_NEXT_MON, + ) def _add_subdiv_nd_holidays(self): pass @@ -683,16 +675,16 @@ def _add_subdiv_pr_holidays(self): self._add_holiday_3rd_mon_of_feb("Presidents' Day") # Emancipation Day - self._add_observed(self._add_holiday_mar_22("Emancipation Day"), before=False) + self._add_observed(self._add_holiday_mar_22("Emancipation Day"), rule=SUN_TO_NEXT_MON) # Good Friday self._add_good_friday("Good Friday") # Constitution Day - self._add_observed(self._add_holiday_jul_25("Constitution Day"), before=False) + self._add_observed(self._add_holiday_jul_25("Constitution Day"), rule=SUN_TO_NEXT_MON) # Discovery Day - self._add_observed(self._add_holiday_nov_19("Discovery Day"), before=False) + self._add_observed(self._add_holiday_nov_19("Discovery Day"), rule=SUN_TO_NEXT_MON) def _add_subdiv_pw_holidays(self): pass @@ -786,7 +778,7 @@ def _add_subdiv_va_holidays(self): self._add_holiday_jan_20(name) if self._year >= 1937 else self._add_holiday_mar_4(name), - before=False, + rule=SUN_TO_NEXT_MON, ) def _add_subdiv_vi_holidays(self): @@ -851,7 +843,7 @@ def _add_subdiv_wi_holidays(self): self._add_christmas_eve_holiday() # New Year's Eve - self._add_observed(self._add_new_years_eve("New Year's Eve"), after=False) + self._add_observed(self._add_new_years_eve("New Year's Eve")) def _add_subdiv_wv_holidays(self): # West Virginia Day diff --git a/holidays/countries/uruguay.py b/holidays/countries/uruguay.py index 18b2f6ed0..22e84e5a1 100644 --- a/holidays/countries/uruguay.py +++ b/holidays/countries/uruguay.py @@ -13,13 +13,17 @@ from datetime import timedelta as td from gettext import gettext as tr -from holidays.calendars.gregorian import MAR, JUN, DEC, MON, _get_nth_weekday_from +from holidays.calendars.gregorian import MAR from holidays.constants import BANK, PUBLIC from holidays.groups import ChristianHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ( + ObservedHolidayBase, + TUE_WED_TO_PREV_MON, + THU_FRI_TO_NEXT_MON, +) -class Uruguay(HolidayBase, ChristianHolidays, InternationalHolidays): +class Uruguay(ObservedHolidayBase, ChristianHolidays, InternationalHolidays): """ References: - https://en.wikipedia.org/wiki/Public_holidays_in_Uruguay @@ -49,22 +53,14 @@ class Uruguay(HolidayBase, ChristianHolidays, InternationalHolidays): 2020: (MAR, 1, presidential_inauguration_day), } - def __init__(self, *args, **kwargs) -> None: + def __init__(self, *args, **kwargs): ChristianHolidays.__init__(self) InternationalHolidays.__init__(self) - super().__init__(*args, **kwargs) - - def _move_holiday(self, dt: date) -> None: # Decree Law #14977, # 15535, #16805. - if self.observed and (1980 <= self._year <= 1983 or self._year >= 1997): - dt_observed = None - if self._is_tuesday(dt) or self._is_wednesday(dt): - dt_observed = _get_nth_weekday_from(-1, MON, dt) - elif self._is_thursday(dt) or self._is_friday(dt): - dt_observed = _get_nth_weekday_from(1, MON, dt) - if dt_observed: - self._add_holiday(self[dt], dt_observed) - self.pop(dt) + super().__init__(observed_rule=TUE_WED_TO_PREV_MON + THU_FRI_TO_NEXT_MON, *args, **kwargs) + + def _is_observed(self, dt: date) -> bool: + return 1980 <= self._year <= 1983 or self._year >= 1997 def _populate_public_holidays(self): # Law # 6997. @@ -111,7 +107,7 @@ def _populate_public_holidays(self): if self._year <= 1932 or 1936 <= self._year <= 1979: # Beaches Day. - self._add_holiday(tr("Día de las Playas"), DEC, 8) + self._add_holiday_dec_8(tr("Día de las Playas")) # Day of the Family. self._add_christmas_day(tr("Día de la Familia")) @@ -149,7 +145,7 @@ def _populate_bank_holidays(self): if self._year <= 1932 or self._year >= 1940: # Birthday of Artigas. - dt = self._add_holiday(tr("Natalicio de Artigas"), JUN, 19) + dt = self._add_holiday_jun_19(tr("Natalicio de Artigas")) if self._year <= 2001: self._move_holiday(dt) diff --git a/holidays/countries/vietnam.py b/holidays/countries/vietnam.py index 6f4b41cf6..a6777a76d 100644 --- a/holidays/countries/vietnam.py +++ b/holidays/countries/vietnam.py @@ -9,13 +9,11 @@ # Website: https://github.com/dr-prodigy/python-holidays # License: MIT (see LICENSE file) -from datetime import timedelta as td - from holidays.groups import ChineseCalendarHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ObservedHolidayBase, SAT_SUN_TO_NEXT_WORKDAY -class Vietnam(HolidayBase, ChineseCalendarHolidays, InternationalHolidays): +class Vietnam(ObservedHolidayBase, ChineseCalendarHolidays, InternationalHolidays): """ https://publicholidays.vn/ http://vbpl.vn/TW/Pages/vbpqen-toanvan.aspx?ItemID=11013 Article.115 @@ -23,18 +21,19 @@ class Vietnam(HolidayBase, ChineseCalendarHolidays, InternationalHolidays): """ country = "VN" + observed_label = "%s (Observed)" def __init__(self, *args, **kwargs): ChineseCalendarHolidays.__init__(self) InternationalHolidays.__init__(self) - super().__init__(*args, **kwargs) + super().__init__(observed_rule=SAT_SUN_TO_NEXT_WORKDAY, *args, **kwargs) def _populate(self, year): super()._populate(year) - observed_dates = set() + dts_observed = set() # New Year's Day - observed_dates.add(self._add_new_years_day("International New Year's Day")) + dts_observed.add(self._add_new_years_day("International New Year's Day")) # Lunar New Year self._add_chinese_new_years_eve("Vietnamese New Year's Eve") @@ -47,25 +46,19 @@ def _populate(self, year): # Vietnamese Kings' Commemoration Day # https://en.wikipedia.org/wiki/H%C3%B9ng_Kings%27_Festival if year >= 2007: - observed_dates.add(self._add_hung_kings_day("Hung Kings Commemoration Day")) + dts_observed.add(self._add_hung_kings_day("Hung Kings Commemoration Day")) # Liberation Day/Reunification Day - observed_dates.add(self._add_holiday_apr_30("Liberation Day/Reunification Day")) + dts_observed.add(self._add_holiday_apr_30("Liberation Day/Reunification Day")) # International Labor Day - observed_dates.add(self._add_labor_day("International Labor Day")) + dts_observed.add(self._add_labor_day("International Labor Day")) # Independence Day - observed_dates.add(self._add_holiday_sep_2("Independence Day")) + dts_observed.add(self._add_holiday_sep_2("Independence Day")) if self.observed: - for dt in sorted(observed_dates): - if not self._is_weekend(dt): - continue - next_workday = dt + td(days=+1) - while self._is_weekend(next_workday) or next_workday in observed_dates: - next_workday += td(days=+1) - observed_dates.add(self._add_holiday(f"{self[dt]} (Observed)", next_workday)) + self._populate_observed(dts_observed) class VN(Vietnam): diff --git a/holidays/countries/zambia.py b/holidays/countries/zambia.py index aa8381d27..6dd4d8398 100644 --- a/holidays/countries/zambia.py +++ b/holidays/countries/zambia.py @@ -9,15 +9,12 @@ # Website: https://github.com/dr-prodigy/python-holidays # License: MIT (see LICENSE file) -from datetime import date -from datetime import timedelta as td - from holidays.calendars.gregorian import MAR, JUL, AUG, SEP from holidays.groups import ChristianHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ObservedHolidayBase, SUN_TO_NEXT_MON -class Zambia(HolidayBase, ChristianHolidays, InternationalHolidays): +class Zambia(ObservedHolidayBase, ChristianHolidays, InternationalHolidays): """ https://www.officeholidays.com/countries/zambia/ https://www.timeanddate.com/holidays/zambia/ @@ -26,6 +23,7 @@ class Zambia(HolidayBase, ChristianHolidays, InternationalHolidays): """ country = "ZM" + observed_label = "%s (Observed)" special_holidays = { 2016: ( (AUG, 11, "General elections and referendum"), @@ -48,13 +46,7 @@ class Zambia(HolidayBase, ChristianHolidays, InternationalHolidays): def __init__(self, *args, **kwargs): ChristianHolidays.__init__(self) InternationalHolidays.__init__(self) - super().__init__(*args, **kwargs) - - def _add_observed(self, dt: date) -> None: - # whenever a public holiday falls on a Sunday, - # it rolls over to the following Monday - if self.observed and self._is_sunday(dt): - self._add_holiday("%s (Observed)" % self[dt], dt + td(days=+1)) + super().__init__(observed_rule=SUN_TO_NEXT_MON, *args, **kwargs) def _populate(self, year): # Observed since 1965 @@ -95,10 +87,10 @@ def _populate(self, year): self._add_observed(self._add_africa_day("Africa Freedom Day")) # Heroes' Day. - first_mon_of_july = self._add_holiday_1st_mon_of_jul("Heroes' Day") + self._add_holiday_1st_mon_of_jul("Heroes' Day") # Unity Day. - self._add_holiday("Unity Day", first_mon_of_july + td(days=+1)) + self._add_holiday_1_day_past_1st_mon_of_jul("Unity Day") # Farmers' Day. self._add_holiday_1st_mon_of_aug("Farmers' Day") diff --git a/holidays/countries/zimbabwe.py b/holidays/countries/zimbabwe.py index 968c34911..310299281 100644 --- a/holidays/countries/zimbabwe.py +++ b/holidays/countries/zimbabwe.py @@ -9,29 +9,23 @@ # Website: https://github.com/dr-prodigy/python-holidays # License: MIT (see LICENSE file) -from datetime import date -from datetime import timedelta as td - from holidays.groups import ChristianHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ObservedHolidayBase, SUN_TO_NEXT_MON, SUN_TO_NEXT_TUE -class Zimbabwe(HolidayBase, ChristianHolidays, InternationalHolidays): +class Zimbabwe(ObservedHolidayBase, ChristianHolidays, InternationalHolidays): """ https://en.wikipedia.org/wiki/Public_holidays_in_Zimbabwe https://en.wikipedia.org/wiki/Robert_Gabriel_Mugabe_National_Youth_Day """ country = "ZW" + observed_label = "%s (Observed)" def __init__(self, *args, **kwargs): ChristianHolidays.__init__(self) InternationalHolidays.__init__(self) - super().__init__(*args, **kwargs) - - def _add_observed(self, dt: date, days: int = +1) -> None: - if self.observed and self._is_sunday(dt): - self._add_holiday("%s (Observed)" % self[dt], dt + td(days=days)) + super().__init__(observed_rule=SUN_TO_NEXT_MON, *args, **kwargs) def _populate(self, year): if year <= 1987: @@ -57,9 +51,11 @@ def _populate(self, year): # Easter Monday. self._add_easter_monday("Easter Monday") - # Independence Day. - apr_18 = self._add_holiday_apr_18("Independence Day") - self._add_observed(apr_18, days=+2 if apr_18 == self._easter_sunday else +1) + self._add_observed( + # Independence Day. + apr_18 := self._add_holiday_apr_18("Independence Day"), + rule=SUN_TO_NEXT_TUE if apr_18 == self._easter_sunday else SUN_TO_NEXT_MON, + ) # Workers' Day. self._add_observed(self._add_labor_day("Workers' Day")) @@ -68,16 +64,16 @@ def _populate(self, year): self._add_observed(self._add_africa_day("Africa Day")) # Zimbabwe Heroes' Day. - second_mon_of_august = self._add_holiday_2nd_mon_of_aug("Zimbabwe Heroes' Day") + self._add_holiday_2nd_mon_of_aug("Zimbabwe Heroes' Day") # Defense Forces Day. - self._add_holiday("Defense Forces Day", second_mon_of_august + td(days=+1)) + self._add_holiday_1_day_past_2nd_mon_of_aug("Defense Forces Day") # Unity Day. self._add_observed(self._add_holiday_dec_22("Unity Day")) # Christmas Day. - self._add_observed(self._add_christmas_day("Christmas Day"), days=+2) + self._add_observed(self._add_christmas_day("Christmas Day"), rule=SUN_TO_NEXT_TUE) # Boxing Day. self._add_observed(self._add_christmas_day_two("Boxing Day")) diff --git a/holidays/locale/ar/LC_MESSAGES/CA.po b/holidays/locale/ar/LC_MESSAGES/CA.po index d285452e3..ff41f0769 100644 --- a/holidays/locale/ar/LC_MESSAGES/CA.po +++ b/holidays/locale/ar/LC_MESSAGES/CA.po @@ -16,6 +16,7 @@ msgstr "" "Generated-By: Lingua 4.15.0\n" "X-Generator: Poedit 3.3.1\n" +#. %s (Observed). #, c-format msgid "%s (Observed)" msgstr "(تمت ملاحظته) %s" diff --git a/holidays/locale/ar/LC_MESSAGES/UA.po b/holidays/locale/ar/LC_MESSAGES/UA.po index b626650f2..f7c6b42fe 100644 --- a/holidays/locale/ar/LC_MESSAGES/UA.po +++ b/holidays/locale/ar/LC_MESSAGES/UA.po @@ -25,6 +25,7 @@ msgstr "%d/%m/%Y" msgid "Вихідний день (перенесено з %s)" msgstr "يوم عطلة (استبدل من %s)" +#. %s (Observed). #, c-format msgid "%s (вихідний)" msgstr "(يوم عطلة) %s" diff --git a/holidays/locale/bs/LC_MESSAGES/BA.po b/holidays/locale/bs/LC_MESSAGES/BA.po index 2c0b94ebe..66c6d3e29 100644 --- a/holidays/locale/bs/LC_MESSAGES/BA.po +++ b/holidays/locale/bs/LC_MESSAGES/BA.po @@ -16,6 +16,7 @@ msgstr "" "Generated-By: pygettext.py 1.5\n" "X-Generator: Poedit 3.2.2\n" +#. %s (Observed). #, c-format msgid "%s (preneseno)" msgstr "" diff --git a/holidays/locale/en/LC_MESSAGES/CA.po b/holidays/locale/en/LC_MESSAGES/CA.po index 38d491493..701a1243c 100644 --- a/holidays/locale/en/LC_MESSAGES/CA.po +++ b/holidays/locale/en/LC_MESSAGES/CA.po @@ -16,6 +16,7 @@ msgstr "" "Generated-By: Lingua 4.15.0\n" "X-Generator: Poedit 3.2.2\n" +#. %s (Observed). #, c-format msgid "%s (Observed)" msgstr "" diff --git a/holidays/locale/en_US/LC_MESSAGES/AO.po b/holidays/locale/en_US/LC_MESSAGES/AO.po index 31befd5f2..382f9f183 100644 --- a/holidays/locale/en_US/LC_MESSAGES/AO.po +++ b/holidays/locale/en_US/LC_MESSAGES/AO.po @@ -20,7 +20,7 @@ msgstr "" msgid "Dia de eleições gerais" msgstr "General Election Day" -#. Day off for %s. +#. %s (Observed). #, c-format msgid "%s (Ponte)" msgstr "Day off for %s" diff --git a/holidays/locale/en_US/LC_MESSAGES/AR.po b/holidays/locale/en_US/LC_MESSAGES/AR.po index f84130885..ecf07494c 100644 --- a/holidays/locale/en_US/LC_MESSAGES/AR.po +++ b/holidays/locale/en_US/LC_MESSAGES/AR.po @@ -122,6 +122,7 @@ msgstr "Columbus day" msgid "Día de la Soberanía Nacional" msgstr "National Sovereignty Day" +#. %s (Observed). #, c-format msgid "%s (Observado)" msgstr "%s (Observed)" diff --git a/holidays/locale/en_US/LC_MESSAGES/BA.po b/holidays/locale/en_US/LC_MESSAGES/BA.po index 0ffa33669..6d8ebe842 100644 --- a/holidays/locale/en_US/LC_MESSAGES/BA.po +++ b/holidays/locale/en_US/LC_MESSAGES/BA.po @@ -16,6 +16,7 @@ msgstr "" "Generated-By: pygettext.py 1.5\n" "X-Generator: Poedit 3.2.2\n" +#. %s (Observed). #, c-format msgid "%s (preneseno)" msgstr "%s (Observed)" diff --git a/holidays/locale/en_US/LC_MESSAGES/CO.po b/holidays/locale/en_US/LC_MESSAGES/CO.po index cb4a18658..dbf11fd47 100644 --- a/holidays/locale/en_US/LC_MESSAGES/CO.po +++ b/holidays/locale/en_US/LC_MESSAGES/CO.po @@ -88,6 +88,7 @@ msgstr "Corpus Christi" msgid "Sagrado Corazón" msgstr "Sacred Heart" +#. %s (Observed). #, c-format msgid "%s (Observado)" msgstr "%s (Observed)" diff --git a/holidays/locale/en_US/LC_MESSAGES/CR.po b/holidays/locale/en_US/LC_MESSAGES/CR.po index f55a3698a..b10592fca 100644 --- a/holidays/locale/en_US/LC_MESSAGES/CR.po +++ b/holidays/locale/en_US/LC_MESSAGES/CR.po @@ -16,6 +16,7 @@ msgstr "" "Generated-By: pygettext.py 1.5\n" "X-Generator: Poedit 3.3\n" +#. %s (Observed). #, c-format msgid "%s (Observado)" msgstr "%s (Observed)" diff --git a/holidays/locale/en_US/LC_MESSAGES/CU.po b/holidays/locale/en_US/LC_MESSAGES/CU.po index 686355972..32c384eb6 100644 --- a/holidays/locale/en_US/LC_MESSAGES/CU.po +++ b/holidays/locale/en_US/LC_MESSAGES/CU.po @@ -16,6 +16,7 @@ msgstr "" "Generated-By: pygettext.py 1.5\n" "X-Generator: Poedit 3.2.2\n" +#. %s Observed. #, c-format msgid "%s (Observado)" msgstr "%s (Observed)" diff --git a/holidays/locale/en_US/LC_MESSAGES/EC.po b/holidays/locale/en_US/LC_MESSAGES/EC.po index 7effaed68..1e6ed6d6c 100644 --- a/holidays/locale/en_US/LC_MESSAGES/EC.po +++ b/holidays/locale/en_US/LC_MESSAGES/EC.po @@ -56,6 +56,7 @@ msgstr "Independence of Cuenca" msgid "Día de Navidad" msgstr "Christmas Day" +#. %s (Observed). #, c-format msgid "%s (Observado)" msgstr "%s (Observed)" diff --git a/holidays/locale/en_US/LC_MESSAGES/LV.po b/holidays/locale/en_US/LC_MESSAGES/LV.po index dbd1db111..ea05f6e7a 100644 --- a/holidays/locale/en_US/LC_MESSAGES/LV.po +++ b/holidays/locale/en_US/LC_MESSAGES/LV.po @@ -33,6 +33,7 @@ msgstr "" "Day the Latvian hockey team won the bronze medal at the 2023 World Ice " "Hockey Championship" +#. %s (Observed). #, c-format msgid "%s (brīvdiena)" msgstr "%s (Observed)" diff --git a/holidays/locale/en_US/LC_MESSAGES/MC.po b/holidays/locale/en_US/LC_MESSAGES/MC.po index eea048d13..2b0f39d1d 100644 --- a/holidays/locale/en_US/LC_MESSAGES/MC.po +++ b/holidays/locale/en_US/LC_MESSAGES/MC.po @@ -16,9 +16,11 @@ msgstr "" "Generated-By: Lingua 4.15.0\n" "X-Generator: Poedit 3.2.2\n" +#. Public holiday. msgid "Jour férié" msgstr "Public holiday" +#. %s (Observed). #, c-format msgid "%s (Observé)" msgstr "%s (Observed)" diff --git a/holidays/locale/en_US/LC_MESSAGES/RS.po b/holidays/locale/en_US/LC_MESSAGES/RS.po index 0f901eb23..b8fc64cf3 100644 --- a/holidays/locale/en_US/LC_MESSAGES/RS.po +++ b/holidays/locale/en_US/LC_MESSAGES/RS.po @@ -51,6 +51,7 @@ msgstr "Easter Sunday" msgid "Други дан Васкрса" msgstr "Easter Monday" +#. %s (Observed). #, c-format msgid "%s (слободан дан)" msgstr "%s (Observed)" diff --git a/holidays/locale/en_US/LC_MESSAGES/TH.po b/holidays/locale/en_US/LC_MESSAGES/TH.po index 98f3b025d..f6aeda739 100644 --- a/holidays/locale/en_US/LC_MESSAGES/TH.po +++ b/holidays/locale/en_US/LC_MESSAGES/TH.po @@ -67,7 +67,7 @@ msgstr "HM King Maha Vajiralongkorn's Coronation Celebrations" msgid "ชดเชยวันสงกรานต์" msgstr "Songkran Festival (in lieu)" -#. %s (in lieu) +#. %s (in lieu). #, c-format msgid "ชดเชย%s" msgstr "%s (in lieu)" diff --git a/holidays/locale/en_US/LC_MESSAGES/UA.po b/holidays/locale/en_US/LC_MESSAGES/UA.po index 6f0367809..cbcc910be 100644 --- a/holidays/locale/en_US/LC_MESSAGES/UA.po +++ b/holidays/locale/en_US/LC_MESSAGES/UA.po @@ -26,6 +26,7 @@ msgstr "%m/%d/%Y" msgid "Вихідний день (перенесено з %s)" msgstr "Day off (substituted from %s)" +#. %s (Observed). #, c-format msgid "%s (вихідний)" msgstr "%s (Observed)" diff --git a/holidays/locale/es/LC_MESSAGES/AR.po b/holidays/locale/es/LC_MESSAGES/AR.po index 56417df97..fa8953b34 100644 --- a/holidays/locale/es/LC_MESSAGES/AR.po +++ b/holidays/locale/es/LC_MESSAGES/AR.po @@ -118,6 +118,7 @@ msgstr "" msgid "Día de la Soberanía Nacional" msgstr "" +#. %s (Observed). #, c-format msgid "%s (Observado)" msgstr "" diff --git a/holidays/locale/es/LC_MESSAGES/CO.po b/holidays/locale/es/LC_MESSAGES/CO.po index 696c6f97e..2e9c002fe 100644 --- a/holidays/locale/es/LC_MESSAGES/CO.po +++ b/holidays/locale/es/LC_MESSAGES/CO.po @@ -88,6 +88,7 @@ msgstr "" msgid "Sagrado Corazón" msgstr "" +#. %s (Observed). #, c-format msgid "%s (Observado)" msgstr "" diff --git a/holidays/locale/es/LC_MESSAGES/CR.po b/holidays/locale/es/LC_MESSAGES/CR.po index 6440d4202..00b691441 100644 --- a/holidays/locale/es/LC_MESSAGES/CR.po +++ b/holidays/locale/es/LC_MESSAGES/CR.po @@ -16,6 +16,7 @@ msgstr "" "Generated-By: pygettext.py 1.5\n" "X-Generator: Poedit 3.3\n" +#. %s (Observed). #, c-format msgid "%s (Observado)" msgstr "" diff --git a/holidays/locale/es/LC_MESSAGES/CU.po b/holidays/locale/es/LC_MESSAGES/CU.po index b5ffe707c..03eb51b3d 100644 --- a/holidays/locale/es/LC_MESSAGES/CU.po +++ b/holidays/locale/es/LC_MESSAGES/CU.po @@ -16,6 +16,7 @@ msgstr "" "Generated-By: pygettext.py 1.5\n" "X-Generator: Poedit 3.2.2\n" +#. %s Observed. #, c-format msgid "%s (Observado)" msgstr "" diff --git a/holidays/locale/es/LC_MESSAGES/EC.po b/holidays/locale/es/LC_MESSAGES/EC.po index 0cd4c5da8..fb6b0e850 100644 --- a/holidays/locale/es/LC_MESSAGES/EC.po +++ b/holidays/locale/es/LC_MESSAGES/EC.po @@ -56,6 +56,7 @@ msgstr "" msgid "Día de Navidad" msgstr "" +#. %s (Observed). #, c-format msgid "%s (Observado)" msgstr "" diff --git a/holidays/locale/fr/LC_MESSAGES/CA.po b/holidays/locale/fr/LC_MESSAGES/CA.po index 747681cc6..c68bdd5b7 100644 --- a/holidays/locale/fr/LC_MESSAGES/CA.po +++ b/holidays/locale/fr/LC_MESSAGES/CA.po @@ -16,6 +16,7 @@ msgstr "" "Generated-By: pygettext.py 1.5\n" "X-Generator: Poedit 3.2.2\n" +#. %s (Observed). #, c-format msgid "%s (Observed)" msgstr "%s (Observé)" diff --git a/holidays/locale/fr/LC_MESSAGES/MC.po b/holidays/locale/fr/LC_MESSAGES/MC.po index 050c731a1..511e016c3 100644 --- a/holidays/locale/fr/LC_MESSAGES/MC.po +++ b/holidays/locale/fr/LC_MESSAGES/MC.po @@ -16,9 +16,11 @@ msgstr "" "Generated-By: Lingua 4.15.0\n" "X-Generator: Poedit 3.2.2\n" +#. Public holiday. msgid "Jour férié" msgstr "" +#. %s (Observed). #, c-format msgid "%s (Observé)" msgstr "" diff --git a/holidays/locale/lv/LC_MESSAGES/LV.po b/holidays/locale/lv/LC_MESSAGES/LV.po index 6cceae5d1..ae20fd01d 100644 --- a/holidays/locale/lv/LC_MESSAGES/LV.po +++ b/holidays/locale/lv/LC_MESSAGES/LV.po @@ -31,6 +31,7 @@ msgid "" " hokeja čempionātā" msgstr "" +#. %s (Observed). #, c-format msgid "%s (brīvdiena)" msgstr "" diff --git a/holidays/locale/pt_AO/LC_MESSAGES/AO.po b/holidays/locale/pt_AO/LC_MESSAGES/AO.po index 32f281b04..1c98775fb 100644 --- a/holidays/locale/pt_AO/LC_MESSAGES/AO.po +++ b/holidays/locale/pt_AO/LC_MESSAGES/AO.po @@ -20,7 +20,7 @@ msgstr "" msgid "Dia de eleições gerais" msgstr "" -#. Day off for %s. +#. %s (Observed). #, c-format msgid "%s (Ponte)" msgstr "" diff --git a/holidays/locale/sr/LC_MESSAGES/BA.po b/holidays/locale/sr/LC_MESSAGES/BA.po index 9a35b07cd..025715739 100644 --- a/holidays/locale/sr/LC_MESSAGES/BA.po +++ b/holidays/locale/sr/LC_MESSAGES/BA.po @@ -16,6 +16,7 @@ msgstr "" "Generated-By: pygettext.py 1.5\n" "X-Generator: Poedit 3.2.2\n" +#. %s (Observed). #, c-format msgid "%s (preneseno)" msgstr "%s (пренешено)" diff --git a/holidays/locale/sr/LC_MESSAGES/RS.po b/holidays/locale/sr/LC_MESSAGES/RS.po index 6a560a87e..803c5558c 100644 --- a/holidays/locale/sr/LC_MESSAGES/RS.po +++ b/holidays/locale/sr/LC_MESSAGES/RS.po @@ -51,6 +51,7 @@ msgstr "" msgid "Други дан Васкрса" msgstr "" +#. %s (Observed). #, c-format msgid "%s (слободан дан)" msgstr "" diff --git a/holidays/locale/th/LC_MESSAGES/CA.po b/holidays/locale/th/LC_MESSAGES/CA.po index 86165f0c3..9b8d78700 100644 --- a/holidays/locale/th/LC_MESSAGES/CA.po +++ b/holidays/locale/th/LC_MESSAGES/CA.po @@ -18,6 +18,7 @@ msgstr "" msgid "New Year's Day" msgstr "วันขึ้นปีใหม่" +#. %s (Observed). #, c-format msgid "%s (Observed)" msgstr "ชดเชย%s" diff --git a/holidays/locale/th/LC_MESSAGES/TH.po b/holidays/locale/th/LC_MESSAGES/TH.po index 51912cf62..047329385 100644 --- a/holidays/locale/th/LC_MESSAGES/TH.po +++ b/holidays/locale/th/LC_MESSAGES/TH.po @@ -67,7 +67,7 @@ msgstr "" msgid "ชดเชยวันสงกรานต์" msgstr "" -#. %s (in lieu) +#. %s (in lieu). #, c-format msgid "ชดเชย%s" msgstr "" diff --git a/holidays/locale/uk/LC_MESSAGES/AO.po b/holidays/locale/uk/LC_MESSAGES/AO.po index 363a4f015..7f12f21dc 100644 --- a/holidays/locale/uk/LC_MESSAGES/AO.po +++ b/holidays/locale/uk/LC_MESSAGES/AO.po @@ -20,7 +20,7 @@ msgstr "" msgid "Dia de eleições gerais" msgstr "День загальних виборів" -#. Day off for %s. +#. %s (Observed). #, c-format msgid "%s (Ponte)" msgstr "Вихідний за %s" diff --git a/holidays/locale/uk/LC_MESSAGES/AR.po b/holidays/locale/uk/LC_MESSAGES/AR.po index 58d80c785..5aedd70ba 100644 --- a/holidays/locale/uk/LC_MESSAGES/AR.po +++ b/holidays/locale/uk/LC_MESSAGES/AR.po @@ -121,6 +121,7 @@ msgstr "День Колумба" msgid "Día de la Soberanía Nacional" msgstr "День національного суверенітету" +#. %s (Observed). #, c-format msgid "%s (Observado)" msgstr "%s (вихідний)" diff --git a/holidays/locale/uk/LC_MESSAGES/BA.po b/holidays/locale/uk/LC_MESSAGES/BA.po index b13d8138d..233f763eb 100644 --- a/holidays/locale/uk/LC_MESSAGES/BA.po +++ b/holidays/locale/uk/LC_MESSAGES/BA.po @@ -16,6 +16,7 @@ msgstr "" "Generated-By: pygettext.py 1.5\n" "X-Generator: Poedit 3.2.2\n" +#. %s (Observed). #, c-format msgid "%s (preneseno)" msgstr "%s (вихідний)" diff --git a/holidays/locale/uk/LC_MESSAGES/CO.po b/holidays/locale/uk/LC_MESSAGES/CO.po index ea2ec8337..49125527c 100644 --- a/holidays/locale/uk/LC_MESSAGES/CO.po +++ b/holidays/locale/uk/LC_MESSAGES/CO.po @@ -88,6 +88,7 @@ msgstr "Свято Тіла і Крові Христових" msgid "Sagrado Corazón" msgstr "Свято Найсвятішого Серця Ісуса" +#. %s (Observed). #, c-format msgid "%s (Observado)" msgstr "%s (вихідний)" diff --git a/holidays/locale/uk/LC_MESSAGES/CR.po b/holidays/locale/uk/LC_MESSAGES/CR.po index bbed989db..2582ff44e 100644 --- a/holidays/locale/uk/LC_MESSAGES/CR.po +++ b/holidays/locale/uk/LC_MESSAGES/CR.po @@ -16,6 +16,7 @@ msgstr "" "Generated-By: pygettext.py 1.5\n" "X-Generator: Poedit 3.3\n" +#. %s (Observed). #, c-format msgid "%s (Observado)" msgstr "%s (вихідний)" diff --git a/holidays/locale/uk/LC_MESSAGES/CU.po b/holidays/locale/uk/LC_MESSAGES/CU.po index 4a6c0cd41..285692878 100644 --- a/holidays/locale/uk/LC_MESSAGES/CU.po +++ b/holidays/locale/uk/LC_MESSAGES/CU.po @@ -16,6 +16,7 @@ msgstr "" "Generated-By: pygettext.py 1.5\n" "X-Generator: Poedit 3.2.2\n" +#. %s Observed. #, c-format msgid "%s (Observado)" msgstr "%s (вихідний)" diff --git a/holidays/locale/uk/LC_MESSAGES/EC.po b/holidays/locale/uk/LC_MESSAGES/EC.po index 9edbe5045..08845ae58 100644 --- a/holidays/locale/uk/LC_MESSAGES/EC.po +++ b/holidays/locale/uk/LC_MESSAGES/EC.po @@ -56,6 +56,7 @@ msgstr "День незалежності Куенки" msgid "Día de Navidad" msgstr "Різдво Христове" +#. %s (Observed). #, c-format msgid "%s (Observado)" msgstr "%s (вихідний)" diff --git a/holidays/locale/uk/LC_MESSAGES/LV.po b/holidays/locale/uk/LC_MESSAGES/LV.po index 8f981b231..624e6df21 100644 --- a/holidays/locale/uk/LC_MESSAGES/LV.po +++ b/holidays/locale/uk/LC_MESSAGES/LV.po @@ -32,6 +32,7 @@ msgid "" msgstr "" "День здобуття збірною Латвії з хокею бронзової медалі Чемпіонату світу" +#. %s (Observed). #, c-format msgid "%s (brīvdiena)" msgstr "%s (вихідний)" diff --git a/holidays/locale/uk/LC_MESSAGES/MC.po b/holidays/locale/uk/LC_MESSAGES/MC.po index c5a9b807f..95f876064 100644 --- a/holidays/locale/uk/LC_MESSAGES/MC.po +++ b/holidays/locale/uk/LC_MESSAGES/MC.po @@ -16,9 +16,11 @@ msgstr "" "Generated-By: Lingua 4.15.0\n" "X-Generator: Poedit 3.2.2\n" +#. Public holiday. msgid "Jour férié" msgstr "Державне свято" +#. %s (Observed). #, c-format msgid "%s (Observé)" msgstr "%s (вихідний)" diff --git a/holidays/locale/uk/LC_MESSAGES/UA.po b/holidays/locale/uk/LC_MESSAGES/UA.po index b917d21db..1e2bc4ae0 100644 --- a/holidays/locale/uk/LC_MESSAGES/UA.po +++ b/holidays/locale/uk/LC_MESSAGES/UA.po @@ -25,6 +25,7 @@ msgstr "" msgid "Вихідний день (перенесено з %s)" msgstr "" +#. %s (Observed). #, c-format msgid "%s (вихідний)" msgstr "" diff --git a/holidays/observed_holiday_base.py b/holidays/observed_holiday_base.py new file mode 100644 index 000000000..01b75ee74 --- /dev/null +++ b/holidays/observed_holiday_base.py @@ -0,0 +1,155 @@ +# python-holidays +# --------------- +# A fast, efficient Python library for generating country, province and state +# specific sets of holidays on the fly. It aims to make determining whether a +# specific date is a holiday as fast and flexible as possible. +# +# Authors: dr-prodigy (c) 2017-2022 +# ryanss (c) 2014-2017 +# Website: https://github.com/dr-prodigy/python-holidays +# License: MIT (see LICENSE file) + +from datetime import date +from datetime import timedelta as td +from typing import Dict, Optional, Tuple, Set + +from holidays.calendars.gregorian import MON, TUE, WED, THU, FRI, SAT, SUN +from holidays.holiday_base import DateArg, HolidayBase + + +class ObservedRule(Dict[int, int]): + __slots__ = () + + def __add__(self, other): + return ObservedRule({**self, **other}) + + +# Observance calculation rules: +7 - next workday, -7 - previous workday. +# Single days. +MON_TO_NEXT_TUE = ObservedRule({MON: +1}) + +TUE_TO_PREV_MON = ObservedRule({TUE: -1}) +TUE_TO_PREV_FRI = ObservedRule({TUE: -4}) + +WED_TO_PREV_MON = ObservedRule({WED: -2}) +WED_TO_NEXT_FRI = ObservedRule({WED: +2}) + +THU_TO_PREV_MON = ObservedRule({THU: -3}) +THU_TO_PREV_WED = ObservedRule({THU: -1}) +THU_TO_NEXT_MON = ObservedRule({THU: +4}) +THU_TO_NEXT_FRI = ObservedRule({THU: +1}) + +FRI_TO_PREV_THU = ObservedRule({FRI: -1}) +FRI_TO_NEXT_MON = ObservedRule({FRI: +3}) +FRI_TO_NEXT_SAT = ObservedRule({FRI: +1}) +FRI_TO_NEXT_WORKDAY = ObservedRule({FRI: +7}) + +SAT_TO_PREV_FRI = ObservedRule({SAT: -1}) +SAT_TO_PREV_WORKDAY = ObservedRule({SAT: -7}) +SAT_TO_NEXT_MON = ObservedRule({SAT: +2}) +SAT_TO_NEXT_TUE = ObservedRule({SAT: +3}) +SAT_TO_NEXT_SUN = ObservedRule({SAT: +1}) +SAT_TO_NEXT_WORKDAY = ObservedRule({SAT: +7}) + +SUN_TO_NEXT_MON = ObservedRule({SUN: +1}) +SUN_TO_NEXT_TUE = ObservedRule({SUN: +2}) +SUN_TO_NEXT_WED = ObservedRule({SUN: +3}) +SUN_TO_NEXT_WORKDAY = ObservedRule({SUN: +7}) + +# Multiple days. +ALL_TO_NEAREST_MON = ObservedRule({TUE: -1, WED: -2, THU: -3, FRI: +3, SAT: +2, SUN: +1}) +ALL_TO_NEAREST_MON_LATAM = ObservedRule({TUE: -1, WED: -2, THU: 4, FRI: +3, SAT: +2, SUN: +1}) +ALL_TO_NEXT_MON = ObservedRule({TUE: +6, WED: +5, THU: +4, FRI: +3, SAT: +2, SUN: +1}) +ALL_TO_NEXT_SUN = ObservedRule({MON: +6, TUE: +5, WED: +4, THU: +3, FRI: +2, SAT: +1}) + +WORKDAY_TO_NEAREST_MON = ObservedRule({TUE: -1, WED: -2, THU: -3, FRI: +3}) +WORKDAY_TO_NEXT_MON = ObservedRule({TUE: +6, WED: +5, THU: +4, FRI: +3}) +WORKDAY_TO_NEXT_WORKDAY = ObservedRule({MON: +7, TUE: +7, WED: +7, THU: +7, FRI: +7}) + +TUE_WED_TO_PREV_MON = ObservedRule({TUE: -1, WED: -2}) +TUE_WED_THU_TO_PREV_MON = ObservedRule({TUE: -1, WED: -2, THU: -3}) + +WED_THU_TO_NEXT_FRI = ObservedRule({WED: +2, THU: +1}) + +THU_FRI_TO_NEXT_MON = ObservedRule({THU: +4, FRI: +3}) +THU_FRI_TO_NEXT_WORKDAY = ObservedRule({THU: +7, FRI: +7}) +THU_FRI_SUN_TO_NEXT_MON = ObservedRule({THU: +4, FRI: +3, SUN: +1}) + +FRI_SAT_TO_NEXT_WORKDAY = ObservedRule({FRI: +7, SAT: +7}) +FRI_SUN_TO_NEXT_MON = ObservedRule({FRI: +3, SUN: +1}) +FRI_SUN_TO_NEXT_SAT_MON = ObservedRule({FRI: +1, SUN: +1}) + +SAT_SUN_TO_PREV_FRI = ObservedRule({SAT: -1, SUN: -2}) +SAT_SUN_TO_NEXT_MON = ObservedRule({SAT: +2, SUN: +1}) +SAT_SUN_TO_NEXT_TUE = ObservedRule({SAT: +3, SUN: +2}) +SAT_SUN_TO_NEXT_MON_TUE = ObservedRule({SAT: +2, SUN: +2}) +SAT_SUN_TO_NEXT_WORKDAY = ObservedRule({SAT: +7, SUN: +7}) + + +class ObservedHolidayBase(HolidayBase): + """Observed holidays implementation.""" + + observed_label = "%s" + + def __init__(self, observed_rule: ObservedRule, observed_since: int = None, *args, **kwargs): + self._observed_rule = observed_rule + self._observed_since = observed_since + + super().__init__(*args, **kwargs) + + def _is_observed(self, *args, **kwargs) -> bool: + return self._observed_since is None or self._year >= self._observed_since + + def _get_observed_date(self, dt: date, rule: ObservedRule) -> date: + delta = rule.get(dt.weekday(), 0) + if delta != 0: + if abs(delta) == 7: + delta //= 7 + dt += td(days=delta) + while dt.year == self._year and ( + dt in self or self._is_weekend(dt) # type: ignore[operator] + ): + dt += td(days=delta) + else: + dt += td(days=delta) + return dt + + def _add_observed( + self, dt: DateArg, name: Optional[str] = None, rule: Optional[ObservedRule] = None + ) -> Tuple[bool, date]: + dt = dt if isinstance(dt, date) else date(self._year, *dt) + + if not self.observed or not self._is_observed(dt): + return False, dt + + dt_observed = self._get_observed_date(dt, rule or self._observed_rule) + if dt_observed == dt: + return False, dt + + observed_label = getattr( + self, + "observed_label_before" if dt_observed < dt else "observed_label", + self.observed_label, + ) + for name in (name,) if name else self.get_list(dt): + super()._add_holiday(self.tr(observed_label) % self.tr(name), dt_observed) + return True, dt_observed + + def _move_holiday(self, dt: date, rule: Optional[ObservedRule] = None) -> Tuple[bool, date]: + is_observed, dt_observed = self._add_observed(dt, rule=rule) + if is_observed: + self.pop(dt) + return is_observed, dt_observed if is_observed else dt + + def _populate_observed(self, dts: Set[date], multiple: bool = False) -> None: + """ + When multiple is True, each holiday from a given date has its own observed date. + """ + for dt in sorted(dts): + if not self._is_observed(dt): + continue + if multiple: + for name in self.get_list(dt): + self._add_observed(dt, name) + else: + self._add_observed(dt) diff --git a/tests/countries/test_saudi_arabia.py b/tests/countries/test_saudi_arabia.py index 361ebdbc3..6d98f965d 100644 --- a/tests/countries/test_saudi_arabia.py +++ b/tests/countries/test_saudi_arabia.py @@ -80,7 +80,6 @@ def test_national_day_overlaps_hijri_holiday(self): "2009-09-23", "2015-09-23", "2048-09-23", - "2074-09-23", ) def test_founding_day(self): From 927f298c5a80a6a2a890a8186af2c6727f497c1c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 Sep 2023 18:49:23 -0700 Subject: [PATCH 03/21] Bump actions/checkout from 3 to 4 (#1458) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci-cd.yml | 6 +++--- .github/workflows/pre-commit-autoupdate.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 1a1104aaa..1b5d8d052 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check Out Repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set Up Python uses: actions/setup-python@v4.7.0 with: @@ -28,7 +28,7 @@ jobs: python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', 'pypy-3.8'] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set Up Python ${{ matrix.python-version }} uses: actions/setup-python@v4.7.0 with: @@ -58,7 +58,7 @@ jobs: needs: [test] runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set Up Python uses: actions/setup-python@v4.7.0 with: diff --git a/.github/workflows/pre-commit-autoupdate.yml b/.github/workflows/pre-commit-autoupdate.yml index 0613b9b03..49e2eadd9 100644 --- a/.github/workflows/pre-commit-autoupdate.yml +++ b/.github/workflows/pre-commit-autoupdate.yml @@ -9,7 +9,7 @@ jobs: auto-update: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-python@v4.7.0 - uses: browniebroke/pre-commit-autoupdate-action@v1.0.0 - uses: peter-evans/create-pull-request@v5.0.2 From 8fc727112f8eabfe1296be3764df5b25445e334a Mon Sep 17 00:00:00 2001 From: Arkadii Yakovets Date: Thu, 7 Sep 2023 07:34:22 -0700 Subject: [PATCH 04/21] Update skipIf rules for heavy tests (#1460) --- tests/common.py | 4 +++- tests/test_registry.py | 7 +++---- tests/test_utils.py | 7 +++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/common.py b/tests/common.py index b543e12bc..a61f2d403 100644 --- a/tests/common.py +++ b/tests/common.py @@ -10,6 +10,7 @@ # License: MIT (see LICENSE file) import os +import sys import unittest import warnings from datetime import date @@ -20,7 +21,8 @@ from holidays import HolidayBase from holidays.calendars.gregorian import SUN -PYTHON_VERSION = (3, 11) +PYTHON_LATEST_SUPPORTED_VERSION = (3, 11) +PYTHON_VERSION = (sys.version_info.major, sys.version_info.minor) class TestCase(unittest.TestCase): diff --git a/tests/test_registry.py b/tests/test_registry.py index 8f0b5d8f5..13f735ca7 100644 --- a/tests/test_registry.py +++ b/tests/test_registry.py @@ -11,7 +11,6 @@ import importlib import inspect -import sys import warnings from unittest import TestCase @@ -19,12 +18,12 @@ import holidays from holidays import countries, financial, registry -from tests.common import PYTHON_VERSION +from tests.common import PYTHON_LATEST_SUPPORTED_VERSION, PYTHON_VERSION class TestEntityLoader(TestCase): @pytest.mark.skipif( - sys.version_info < PYTHON_VERSION, + PYTHON_VERSION != PYTHON_LATEST_SUPPORTED_VERSION, reason="Run once on the latest Python version only", ) def test_countries_imports(self): @@ -67,7 +66,7 @@ def test_country_str(self): ) @pytest.mark.skipif( - sys.version_info < PYTHON_VERSION, + PYTHON_VERSION != PYTHON_LATEST_SUPPORTED_VERSION, reason="Run once on the latest Python version only", ) def test_financial_imports(self): diff --git a/tests/test_utils.py b/tests/test_utils.py index d8e2c655d..3d7ba7a15 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -9,7 +9,6 @@ # Website: https://github.com/dr-prodigy/python-holidays # License: MIT (see LICENSE file) -import sys import unittest import warnings from datetime import date @@ -28,7 +27,7 @@ list_supported_countries, list_supported_financial, ) -from tests.common import PYTHON_VERSION +from tests.common import PYTHON_LATEST_SUPPORTED_VERSION, PYTHON_VERSION class TestCountryHolidays(unittest.TestCase): @@ -94,7 +93,7 @@ class TestAllInSameYear(unittest.TestCase): years = set(range(1950, 2051)) @pytest.mark.skipif( - sys.version_info < PYTHON_VERSION, + PYTHON_VERSION != PYTHON_LATEST_SUPPORTED_VERSION, reason="Run once on the latest Python version only", ) @mock.patch("pathlib.Path.rglob", return_value=()) @@ -117,7 +116,7 @@ def test_all_countries(self, unused_rglob_mock): self.assertEqual(self.years, country_holidays(country, years=self.years).years) @pytest.mark.skipif( - sys.version_info < PYTHON_VERSION, + PYTHON_VERSION != PYTHON_LATEST_SUPPORTED_VERSION, reason="Run once on the latest Python version only", ) @mock.patch("pathlib.Path.rglob", return_value=()) From d2fe963ab4d7f9bf53c1fba4bf1eb227770e8707 Mon Sep 17 00:00:00 2001 From: Arkadii Yakovets Date: Thu, 7 Sep 2023 07:34:52 -0700 Subject: [PATCH 05/21] Update PR template (#1461) --- .github/PULL_REQUEST_TEMPLATE.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 7fa5420e8..dfefb94da 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -34,9 +34,10 @@ Your PR description goes here. --> - [ ] I've followed the [contributing guidelines][contributing-guidelines] +- [ ] I've added references to all holidays information sources used in this PR - [ ] This PR is filed against `beta` branch of the repository - [ ] This PR doesn't contain any merge conflicts and has clean commit history -- [ ] The code style looks good: `make pre-commit` +- [ ] The code style looks good: `make pre-commit` command generates no changes - [ ] All tests pass locally: `make test`, `make tox` (we strongly encourage adding tests to your code) - [ ] The related [documentation][docs] has been added/updated (check off the box for free if no updates is required) From 424505a0387e064c2e45b43d8bf3bab989f15d1a Mon Sep 17 00:00:00 2001 From: Arkadii Yakovets Date: Fri, 8 Sep 2023 11:46:23 -0700 Subject: [PATCH 06/21] Add merge queue support (#1464) --- .github/workflows/ci-cd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 1b5d8d052..e380ea560 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -1,5 +1,5 @@ name: Tests -on: [push, pull_request, workflow_dispatch] +on: [merge_group, push, pull_request, workflow_dispatch] env: FORCE_COLOR: 1 From e841412c196633d3373ce927d5b2610bbd77692c Mon Sep 17 00:00:00 2001 From: ~Jhellico Date: Mon, 11 Sep 2023 18:41:21 +0300 Subject: [PATCH 07/21] Update Canada holidays (#1448) Co-authored-by: Arkadii Yakovets --- README.rst | 2 +- holidays/constants.py | 4 +- holidays/countries/canada.py | 380 ++++++---- holidays/locale/ar/LC_MESSAGES/CA.po | 20 +- holidays/locale/en/LC_MESSAGES/CA.po | 16 +- holidays/locale/fr/LC_MESSAGES/CA.po | 16 +- holidays/locale/th/LC_MESSAGES/CA.po | 20 +- tests/countries/test_canada.py | 1025 ++++++++++++++++++-------- 8 files changed, 1040 insertions(+), 443 deletions(-) diff --git a/README.rst b/README.rst index e2c55c314..1de0b2817 100644 --- a/README.rst +++ b/README.rst @@ -243,7 +243,7 @@ The list of supported countries, their subdivisions and supported languages - * - Canada - CA - - Provinces and territories: AB, BC, MB, NB, NL, NS, NT, NU, **ON**, PE, QC, SK, YT + - Provinces and territories: AB, BC, MB, NB, NL, NS, NT, NU, ON, PE, QC, SK, YT - ar, **en**, fr, th * - Chad - TD diff --git a/holidays/constants.py b/holidays/constants.py index e18e65873..130648eca 100644 --- a/holidays/constants.py +++ b/holidays/constants.py @@ -38,9 +38,9 @@ # Supported holiday categories. BANK = "bank" -EXTENDED = "extended" GOVERNMENT = "government" HALF_DAY = "half_day" +OPTIONAL = "optional" PUBLIC = "public" SCHOOL = "school" WORKDAY = "workday" @@ -55,12 +55,12 @@ BANK, CHINESE, CHRISTIAN, - EXTENDED, GOVERNMENT, HALF_DAY, HEBREW, HINDU, ISLAMIC, + OPTIONAL, PUBLIC, SCHOOL, WORKDAY, diff --git a/holidays/countries/canada.py b/holidays/countries/canada.py index 12c771126..92dd56e47 100644 --- a/holidays/countries/canada.py +++ b/holidays/countries/canada.py @@ -13,6 +13,7 @@ from gettext import gettext as tr from holidays.calendars.gregorian import MAR, APR, JUN, JUL +from holidays.constants import GOVERNMENT, OPTIONAL, PUBLIC from holidays.groups import ChristianHolidays, InternationalHolidays from holidays.observed_holiday_base import ( ObservedHolidayBase, @@ -20,14 +21,29 @@ SAT_SUN_TO_NEXT_MON, SAT_SUN_TO_NEXT_MON_TUE, SUN_TO_NEXT_MON, + SUN_TO_NEXT_TUE, ) class Canada(ObservedHolidayBase, ChristianHolidays, InternationalHolidays): + """ + References: + - https://en.wikipedia.org/wiki/Public_holidays_in_Canada + - https://web.archive.org/web/20130703014214/http://www.hrsdc.gc.ca/eng/labour/overviews/employment_standards/holidays.shtml # noqa: E501 + - https://www.alberta.ca/alberta-general-holidays + - https://www2.gov.bc.ca/gov/content/employment-business/employment-standards-advice/employment-standards/statutory-holidays # noqa: E501 + - http://web2.gov.mb.ca/laws/statutes/ccsm/r120e.php + - https://www2.gnb.ca/content/gnb/en/departments/elg/local_government/content/governance/content/days_of_rest_act.html # noqa: E501 + - https://www.ontario.ca/document/your-guide-employment-standards-act-0/public-holidays + - https://www.officeholidays.com/countries/canada/ + - https://www.timeanddate.com/holidays/canada/ + """ + country = "CA" default_language = "en" # %s (Observed). observed_label = tr("%s (Observed)") + supported_categories = {GOVERNMENT, OPTIONAL, PUBLIC} subdivisions = ( "AB", "BC", @@ -46,9 +62,6 @@ class Canada(ObservedHolidayBase, ChristianHolidays, InternationalHolidays): supported_languages = ("ar", "en", "fr", "th") def __init__(self, *args, **kwargs): - # Default subdivision to ON; prov for backwards compatibility - if not kwargs.get("subdiv", kwargs.get("prov")): - kwargs["subdiv"] = "ON" ChristianHolidays.__init__(self) InternationalHolidays.__init__(self) super().__init__(observed_rule=SAT_SUN_TO_NEXT_MON, *args, **kwargs) @@ -56,48 +69,91 @@ def __init__(self, *args, **kwargs): def _get_nearest_monday(self, *args) -> date: return self._get_observed_date(date(self._year, *args), rule=ALL_TO_NEAREST_MON) - def _populate(self, year): - if year <= 1866: - return None - - super()._populate(year) + def _add_common_holidays(self): + """Nationwide statutory holidays.""" # New Year's Day. self._add_observed(self._add_new_years_day(tr("New Year's Day"))) # Good Friday. self._add_good_friday(tr("Good Friday")) - # Easter Monday. - self._add_easter_monday(tr("Easter Monday")) - if year <= 1982: - # Dominion Day. - self._add_observed(self._add_holiday_jul_1(tr("Dominion Day"))) + if self._year >= 1879: + self._canada_day = self._add_holiday_jul_1( + # Canada Day. + tr("Canada Day") + if self._year >= 1983 + # Dominion Day. + else tr("Dominion Day") + ) if self._year >= 1894: # Labour Day. self._add_holiday_1st_mon_of_sep(tr("Labour Day")) + # Christmas Day. + self._add_christmas_day(tr("Christmas Day")) + + def _populate_public_holidays(self): + if self._year <= 1866: + return None + + self._add_common_holidays() + + self._add_observed(self._christmas_day) + + def _populate_government_holidays(self): + """Holidays for federally regulated workplaces.""" + + if self._year <= 1866: + return None + + self._add_common_holidays() + + if self._year >= 1953: + # Victoria Day. + self._add_holiday_1st_mon_before_may_24(tr("Victoria Day")) + + if self._year >= 1879: + self._add_observed(self._canada_day) + + if self._year >= 2021: + self._add_observed( + # National Day for Truth and Reconciliation. + self._add_holiday_sep_30(tr("National Day for Truth and Reconciliation")) + ) + + self._add_thanksgiving_day() + + if self._year >= 1931: + # Remembrance Day. + self._add_observed(self._add_remembrance_day(tr("Remembrance Day"))) + + self._add_observed(self._christmas_day, rule=SAT_SUN_TO_NEXT_MON_TUE) + + # Boxing Day. self._add_observed( - # Christmas Day. - self._add_christmas_day(tr("Christmas Day")), - rule=SAT_SUN_TO_NEXT_MON_TUE, + self._add_christmas_day_two(tr("Boxing Day")), rule=SAT_SUN_TO_NEXT_MON_TUE ) + def _populate_optional_holidays(self): + if self._year <= 1866: + return None + + # Christmas Day. self._add_observed( - # Boxing Day. - self._add_christmas_day_two(tr("Boxing Day")), - rule=SAT_SUN_TO_NEXT_MON_TUE, + self._add_christmas_day(tr("Christmas Day")), rule=SAT_SUN_TO_NEXT_MON_TUE ) - def _add_family_day(self): - # Family Day. - self._add_holiday_3rd_mon_of_feb(tr("Family Day")) + # Boxing Day. + self._add_observed( + self._add_christmas_day_two(tr("Boxing Day")), rule=SAT_SUN_TO_NEXT_MON_TUE + ) - def _add_thanksgiving(self): + def _add_thanksgiving_day(self): if self._year >= 1931: - # Thanksgiving. - name = tr("Thanksgiving") + # Thanksgiving Day. + name = tr("Thanksgiving Day") # in 1935, Canadian Thanksgiving was moved due to the General # Election falling on the second Monday of October # http://tiny.cc/can_thkgvg @@ -106,47 +162,41 @@ def _add_thanksgiving(self): else: self._add_holiday_2nd_mon_of_oct(name) - def _add_queens_funeral(self): - if self._year == 2022: - # Funeral of Queen Elizabeth II. - self._add_holiday_sep_19(tr("Funeral of Her Majesty the Queen Elizabeth II")) - - def _add_subdiv_holidays(self): - if self._year >= 1983: - self._add_observed( - self._add_holiday_jul_1( - ( - # Memorial Day. - tr("Memorial Day") - if self.subdiv == "NL" - # Canada Day. - else tr("Canada Day") - ) - ) - ) - - super()._add_subdiv_holidays() - - def _add_subdiv_ab_holidays(self): + def _add_subdiv_ab_public_holidays(self): if self._year >= 1990: - self._add_family_day() + # Family Day. + self._add_holiday_3rd_mon_of_feb(tr("Family Day")) if self._year >= 1953: # Victoria Day. self._add_holiday_1st_mon_before_may_24(tr("Victoria Day")) + if self._year >= 1879: + self._add_observed(self._canada_day, rule=SUN_TO_NEXT_MON) + + self._add_thanksgiving_day() + + if self._year >= 1931: + # Remembrance Day. + self._add_observed(self._add_remembrance_day(tr("Remembrance Day"))) + + def _add_subdiv_ab_optional_holidays(self): + # Easter Monday. + self._add_easter_monday(tr("Easter Monday")) + # https://en.wikipedia.org/wiki/Civic_Holiday#Alberta if self._year >= 1974: # Heritage Day. self._add_holiday_1st_mon_of_aug(tr("Heritage Day")) - self._add_thanksgiving() + if self._year >= 2021: + # National Day for Truth and Reconciliation. + self._add_holiday_sep_30(tr("National Day for Truth and Reconciliation")) - if self._year >= 1931: - # Remembrance Day. - self._add_remembrance_day(tr("Remembrance Day")) + # Boxing Day. + self._add_christmas_day_two(tr("Boxing Day")) - def _add_subdiv_bc_holidays(self): + def _add_subdiv_bc_public_holidays(self): if self._year >= 2013: name = tr("Family Day") if self._year >= 2019: @@ -158,24 +208,29 @@ def _add_subdiv_bc_holidays(self): # Victoria Day. self._add_holiday_1st_mon_before_may_24(tr("Victoria Day")) + if self._year >= 1879: + self._add_observed(self._canada_day, rule=SUN_TO_NEXT_MON) + # https://en.wikipedia.org/wiki/Civic_Holiday#British_Columbia if self._year >= 1974: # British Columbia Day. self._add_holiday_1st_mon_of_aug(tr("British Columbia Day")) - self._add_queens_funeral() + 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")) - self._add_thanksgiving() + self._add_thanksgiving_day() if self._year >= 1931: # Remembrance Day. self._add_remembrance_day(tr("Remembrance Day")) - def _add_subdiv_mb_holidays(self): + def _add_subdiv_mb_public_holidays(self): if self._year >= 2008: # Louis Riel Day. self._add_holiday_3rd_mon_of_feb(tr("Louis Riel Day")) @@ -184,6 +239,9 @@ def _add_subdiv_mb_holidays(self): # Victoria Day. self._add_holiday_1st_mon_before_may_24(tr("Victoria Day")) + self._add_thanksgiving_day() + + def _add_subdiv_mb_optional_holidays(self): if self._year >= 1900: name = ( # Terry Fox Day. @@ -194,36 +252,55 @@ def _add_subdiv_mb_holidays(self): ) self._add_holiday_1st_mon_of_aug(name) - if self._year >= 2021: - # National Day for Truth and Reconciliation. - self._add_holiday_sep_30(tr("National Day for Truth and Reconciliation")) - - self._add_thanksgiving() - if self._year >= 1931: # Remembrance Day. self._add_remembrance_day(tr("Remembrance Day")) - def _add_subdiv_nb_holidays(self): + def _add_subdiv_nb_public_holidays(self): if self._year >= 2018: - self._add_family_day() - - if self._year >= 1953: - # Victoria Day. - self._add_holiday_1st_mon_before_may_24(tr("Victoria Day")) + # Family Day. + self._add_holiday_3rd_mon_of_feb(tr("Family Day")) # https://en.wikipedia.org/wiki/Civic_Holiday#New_Brunswick - if self._year >= 1900: + if self._year >= 1975: # New Brunswick Day. self._add_holiday_1st_mon_of_aug(tr("New Brunswick Day")) - self._add_queens_funeral() + 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")) - def _add_subdiv_nl_holidays(self): + def _add_subdiv_nb_optional_holidays(self): + if self._year >= 1953: + # Victoria Day. + self._add_holiday_1st_mon_before_may_24(tr("Victoria Day")) + + self._add_thanksgiving_day() + + # Boxing Day. + self._add_christmas_day_two(tr("Boxing Day")) + + def _add_subdiv_nl_public_holidays(self): + if self._year >= 1917: + # Memorial Day. + self._add_holiday_jul_1(tr("Memorial Day")) + + 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"))) + + def _add_subdiv_nl_optional_holidays(self): if self._year >= 1900: # St. Patrick's Day. self._add_holiday(tr("St. Patrick's Day"), self._get_nearest_monday(MAR, 17)) @@ -240,33 +317,35 @@ def _add_subdiv_nl_holidays(self): # Discovery Day. self._add_holiday(tr("Discovery Day"), self._get_nearest_monday(JUN, 24)) - self._add_queens_funeral() + if self._year >= 1900: + # Orangemen's Day. + self._add_holiday(tr("Orangemen's Day"), self._get_nearest_monday(JUL, 12)) - if self._year >= 1931: - # Remembrance Day. - self._add_observed( - self._add_remembrance_day(tr("Remembrance Day")), rule=SUN_TO_NEXT_MON - ) + self._add_thanksgiving_day() + + # Boxing Day. + self._add_christmas_day_two(tr("Boxing Day")) - def _add_subdiv_ns_holidays(self): + def _add_subdiv_ns_public_holidays(self): # http://novascotia.ca/lae/employmentrights/NovaScotiaHeritageDay.asp if self._year >= 2015: # Heritage Day. self._add_holiday_3rd_mon_of_feb(tr("Heritage Day")) - self._add_queens_funeral() - - if self._year >= 2021: - # National Day for Truth and Reconciliation. - self._add_holiday_sep_30(tr("National Day for Truth and Reconciliation")) + 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: + if self._year >= 1981: # Remembrance Day. - self._add_observed( - self._add_remembrance_day(tr("Remembrance Day")), rule=SUN_TO_NEXT_MON - ) + self._add_observed(self._add_remembrance_day(tr("Remembrance Day"))) - def _add_subdiv_nt_holidays(self): + def _add_subdiv_ns_optional_holidays(self): + if self._year >= 1996: + # Natal Day. + self._add_holiday_1st_mon_of_aug(tr("Natal Day")) + + def _add_subdiv_nt_public_holidays(self): if self._year >= 1953: # Victoria Day. self._add_holiday_1st_mon_before_may_24(tr("Victoria Day")) @@ -279,45 +358,63 @@ def _add_subdiv_nt_holidays(self): # Civic Holiday. self._add_holiday_1st_mon_of_aug(tr("Civic Holiday")) - self._add_thanksgiving() + if self._year >= 2022: + # National Day for Truth and Reconciliation. + self._add_holiday_sep_30(tr("National Day for Truth and Reconciliation")) + + self._add_thanksgiving_day() if self._year >= 1931: # Remembrance Day. - self._add_observed( - self._add_remembrance_day(tr("Remembrance Day")), rule=SUN_TO_NEXT_MON - ) + self._add_remembrance_day(tr("Remembrance Day")) - def _add_subdiv_nu_holidays(self): + def _add_subdiv_nu_public_holidays(self): if self._year >= 1953: # Victoria Day. self._add_holiday_1st_mon_before_may_24(tr("Victoria Day")) - if self._year >= 2000: - dt = (APR, 1) if self._year == 2000 else (JUL, 9) - # Nunavut Day. - self._add_observed(self._add_holiday(tr("Nunavut Day"), dt), rule=SUN_TO_NEXT_MON) + if self._year >= 1900: + # Civic Holiday. + self._add_holiday_1st_mon_of_aug(tr("Civic Holiday")) - self._add_thanksgiving() + if self._year >= 2022: + # National Day for Truth and Reconciliation. + self._add_holiday_sep_30(tr("National Day for Truth and Reconciliation")) + + self._add_thanksgiving_day() if self._year >= 1931: # Remembrance Day. self._add_remembrance_day(tr("Remembrance Day")) - def _add_subdiv_on_holidays(self): + def _add_subdiv_nu_optional_holidays(self): + if self._year >= 2000: + # Nunavut Day. + name = tr("Nunavut Day") + if self._year == 2000: + self._add_holiday_apr_1(name) + else: + self._add_holiday_jul_9(name) + + def _add_subdiv_on_public_holidays(self): if self._year >= 2008: - self._add_family_day() + # Family Day. + self._add_holiday_3rd_mon_of_feb(tr("Family Day")) if self._year >= 1953: # Victoria Day. self._add_holiday_1st_mon_before_may_24(tr("Victoria Day")) + self._add_thanksgiving_day() + + self._add_observed(self._add_christmas_day_two(tr("Boxing Day")), rule=SUN_TO_NEXT_TUE) + + def _add_subdiv_on_optional_holidays(self): if self._year >= 1900: # Civic Holiday. self._add_holiday_1st_mon_of_aug(tr("Civic Holiday")) - self._add_thanksgiving() - - def _add_subdiv_pe_holidays(self): + def _add_subdiv_pe_public_holidays(self): if self._year >= 2009: # Islander Day. name = tr("Islander Day") @@ -326,15 +423,22 @@ def _add_subdiv_pe_holidays(self): else: self._add_holiday_2nd_mon_of_feb(name) - self._add_queens_funeral() + 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")) if self._year >= 1931: # Remembrance Day. - self._add_observed( - self._add_remembrance_day(tr("Remembrance Day")), rule=SUN_TO_NEXT_MON - ) + self._add_observed(self._add_remembrance_day(tr("Remembrance Day"))) - def _add_subdiv_qc_holidays(self): + def _add_subdiv_qc_public_holidays(self): if self._year >= 2003: # National Patriots' Day. self._add_holiday_1st_mon_before_may_24(tr("National Patriots' Day")) @@ -346,56 +450,72 @@ def _add_subdiv_qc_holidays(self): rule=SUN_TO_NEXT_MON, ) - self._add_thanksgiving() + if self._year >= 1879: + self._add_observed(self._canada_day, rule=SUN_TO_NEXT_MON) + + self._add_thanksgiving_day() - def _add_subdiv_sk_holidays(self): + def _add_subdiv_qc_optional_holidays(self): + # Easter Monday. + self._add_easter_monday(tr("Easter Monday")) + + def _add_subdiv_sk_public_holidays(self): if self._year >= 2007: - self._add_family_day() + # Family Day. + self._add_holiday_3rd_mon_of_feb(tr("Family Day")) if self._year >= 1953: # Victoria Day. self._add_holiday_1st_mon_before_may_24(tr("Victoria Day")) + if self._year >= 1879: + self._add_observed(self._canada_day) + # https://en.wikipedia.org/wiki/Civic_Holiday#Saskatchewan if self._year >= 1900: # Saskatchewan Day. self._add_holiday_1st_mon_of_aug(tr("Saskatchewan Day")) - self._add_thanksgiving() + self._add_thanksgiving_day() if self._year >= 1931: # Remembrance Day. - self._add_observed( - self._add_remembrance_day(tr("Remembrance Day")), rule=SUN_TO_NEXT_MON - ) - - def _add_subdiv_yt_holidays(self): - # start date? - # https://www.britannica.com/topic/Heritage-Day-Canadian-holiday - # Heritage Day was created in 1973 - # by the Heritage Canada Foundation - # therefore, start date is not earlier than 1974 - # http://heritageyukon.ca/programs/heritage-day - # https://en.wikipedia.org/wiki/Family_Day_(Canada)#Yukon_Heritage_Day - # Friday before the last Sunday in February - if self._year >= 1974: - self._add_holiday_2_days_prior_last_sun_of_feb(tr("Heritage Day")) + self._add_observed(self._add_remembrance_day(tr("Remembrance Day"))) + def _add_subdiv_yt_public_holidays(self): if self._year >= 1953: # Victoria Day. self._add_holiday_1st_mon_before_may_24(tr("Victoria Day")) + if self._year >= 2017: + # National Aboriginal Day. + self._add_holiday_jun_21(tr("National Aboriginal Day")) + + if self._year >= 1879: + self._add_observed(self._canada_day) + if self._year >= 1912: # Discovery Day. self._add_holiday_3rd_mon_of_aug(tr("Discovery Day")) - self._add_queens_funeral() + 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")) - self._add_thanksgiving() + self._add_thanksgiving_day() if self._year >= 1931: # Remembrance Day. - self._add_remembrance_day(tr("Remembrance Day")) + self._add_observed(self._add_remembrance_day(tr("Remembrance Day"))) + + def _add_subdiv_yt_optional_holidays(self): + # Friday before the last Sunday in February + if self._year >= 1976: + self._add_holiday_2_days_prior_last_sun_of_feb(tr("Heritage Day")) class CA(Canada): diff --git a/holidays/locale/ar/LC_MESSAGES/CA.po b/holidays/locale/ar/LC_MESSAGES/CA.po index ff41f0769..e8067485d 100644 --- a/holidays/locale/ar/LC_MESSAGES/CA.po +++ b/holidays/locale/ar/LC_MESSAGES/CA.po @@ -3,10 +3,10 @@ # msgid "" msgstr "" -"Project-Id-Version: Python Holidays 0.26\n" +"Project-Id-Version: Python Holidays 0.32\n" "POT-Creation-Date: 2023-05-20 18:16-0700\n" -"PO-Revision-Date: 2023-05-20 18:30-0700\n" -"Last-Translator: Arkadii Yakovets \n" +"PO-Revision-Date: 2023-08-26 18:25+0300\n" +"Last-Translator: ~Jhellico \n" "Language-Team: Python Holidays localization team\n" "Language: ar\n" "MIME-Version: 1.0\n" @@ -14,7 +14,7 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=6; plural=(n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5);\n" "Generated-By: Lingua 4.15.0\n" -"X-Generator: Poedit 3.3.1\n" +"X-Generator: Poedit 3.2.2\n" #. %s (Observed). #, c-format @@ -53,8 +53,8 @@ msgstr "يوم الملاكمة" msgid "Family Day" msgstr "يوم العائلة" -#. Thanksgiving. -msgid "Thanksgiving" +#. Thanksgiving Day. +msgid "Thanksgiving Day" msgstr "عيد الشكر" #. Funeral of Queen Elizabeth II. @@ -140,3 +140,11 @@ msgstr "عيد القديس جان بابتيست" #. Saskatchewan Day. msgid "Saskatchewan Day" msgstr "يوم ساسكاتشوان" + +#. Orangemen's Day. +msgid "Orangemen's Day" +msgstr "يوم رجال البرتقال" + +#. Natal Day. +msgid "Natal Day" +msgstr "يوم التأسيس" diff --git a/holidays/locale/en/LC_MESSAGES/CA.po b/holidays/locale/en/LC_MESSAGES/CA.po index 701a1243c..486136783 100644 --- a/holidays/locale/en/LC_MESSAGES/CA.po +++ b/holidays/locale/en/LC_MESSAGES/CA.po @@ -3,9 +3,9 @@ # msgid "" msgstr "" -"Project-Id-Version: Python Holidays 0.20\n" +"Project-Id-Version: Python Holidays 0.32\n" "POT-Creation-Date: 2023-04-10 14:10+0300\n" -"PO-Revision-Date: 2023-04-10 14:11+0300\n" +"PO-Revision-Date: 2023-08-26 18:22+0300\n" "Last-Translator: ~Jhellico \n" "Language-Team: Python Holidays localization team\n" "Language: en\n" @@ -53,8 +53,8 @@ msgstr "" msgid "Family Day" msgstr "" -#. Thanksgiving. -msgid "Thanksgiving" +#. Thanksgiving Day. +msgid "Thanksgiving Day" msgstr "" #. Funeral of Queen Elizabeth II. @@ -140,3 +140,11 @@ msgstr "" #. Saskatchewan Day. msgid "Saskatchewan Day" msgstr "" + +#. Orangemen's Day. +msgid "Orangemen's Day" +msgstr "" + +#. Natal Day. +msgid "Natal Day" +msgstr "" diff --git a/holidays/locale/fr/LC_MESSAGES/CA.po b/holidays/locale/fr/LC_MESSAGES/CA.po index c68bdd5b7..dcf3eab2c 100644 --- a/holidays/locale/fr/LC_MESSAGES/CA.po +++ b/holidays/locale/fr/LC_MESSAGES/CA.po @@ -3,9 +3,9 @@ # msgid "" msgstr "" -"Project-Id-Version: Python Holidays 0.21\n" +"Project-Id-Version: Python Holidays 0.32\n" "POT-Creation-Date: 2023-04-10 14:10+0300\n" -"PO-Revision-Date: 2023-04-10 14:11+0300\n" +"PO-Revision-Date: 2023-08-26 18:23+0300\n" "Last-Translator: ~Jhellico \n" "Language-Team: Python Holidays localization team\n" "Language: fr\n" @@ -53,8 +53,8 @@ msgstr "Boxing Day" msgid "Family Day" msgstr "Fête de la famille" -#. Thanksgiving. -msgid "Thanksgiving" +#. Thanksgiving Day. +msgid "Thanksgiving Day" msgstr "Action de grâce" #. Funeral of Queen Elizabeth II. @@ -140,3 +140,11 @@ msgstr "Fête nationale du Québec" #. Saskatchewan Day. msgid "Saskatchewan Day" msgstr "Jour du Saskatchewan" + +#. Orangemen's Day. +msgid "Orangemen's Day" +msgstr "Journée des Orangistes" + +#. Natal Day. +msgid "Natal Day" +msgstr "Jour de la Fondation" diff --git a/holidays/locale/th/LC_MESSAGES/CA.po b/holidays/locale/th/LC_MESSAGES/CA.po index 9b8d78700..d0bdfe823 100644 --- a/holidays/locale/th/LC_MESSAGES/CA.po +++ b/holidays/locale/th/LC_MESSAGES/CA.po @@ -3,16 +3,18 @@ # msgid "" msgstr "" -"Project-Id-Version: Python Holidays 0.25\n" +"Project-Id-Version: Python Holidays 0.32\n" "POT-Creation-Date: 2023-02-24 17:37+0700\n" -"PO-Revision-Date: 2023-02-24 17:37+0700\n" -"Last-Translator: PPsyrius \n" +"PO-Revision-Date: 2023-08-26 18:27+0300\n" +"Last-Translator: ~Jhellico \n" "Language-Team: Python Holidays localization team\n" "Language: th\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" "Generated-By: pygettext.py 1.5\n" +"X-Generator: Poedit 3.2.2\n" #. New Year's Day. msgid "New Year's Day" @@ -119,8 +121,8 @@ msgstr "พระราชพิธีพระบรมศพของสมเ msgid "National Day for Truth and Reconciliation" msgstr "วันชาติแห่งความจริงและการปรองดอง" -#. Thanksgiving. -msgid "Thanksgiving" +#. Thanksgiving Day. +msgid "Thanksgiving Day" msgstr "วันขอบคุณพระเจ้า" #. Remembrance Day. @@ -138,3 +140,11 @@ msgstr "วันเปิดกล่องของขวัญ" #. Terry Fox Day. msgid "Terry Fox Day" msgstr "วันเทร์รี ฟอกซ์" + +#. Orangemen's Day. +msgid "Orangemen's Day" +msgstr "วันออเรนจ์เมนส์" + +#. Natal Day. +msgid "Natal Day" +msgstr "วันสถาปนา" diff --git a/tests/countries/test_canada.py b/tests/countries/test_canada.py index 226f929f1..079abc9e5 100644 --- a/tests/countries/test_canada.py +++ b/tests/countries/test_canada.py @@ -9,6 +9,7 @@ # Website: https://github.com/dr-prodigy/python-holidays # License: MIT (see LICENSE file) +from holidays.constants import GOVERNMENT, OPTIONAL from holidays.countries.canada import Canada, CA, CAN from tests.common import TestCase @@ -16,118 +17,49 @@ class TestCanada(TestCase): @classmethod def setUpClass(cls): - years = range(1900, 2050) - super().setUpClass(Canada, years=years) + years = range(1867, 2050) + super().setUpClass(Canada, years=years, years_non_observed=(range(2000, 2024))) cls.prov_hols = {prov: CA(subdiv=prov, years=years) for prov in CA.subdivisions} + cls.gov_hols = CA(years=years, categories=(GOVERNMENT,)) + cls.prov_opt_hols = { + prov: CA(subdiv=prov, years=years, categories=(OPTIONAL,)) for prov in CA.subdivisions + } def test_country_aliases(self): self.assertCountryAliases(Canada, CA, CAN) def test_no_holidays(self): self.assertNoHolidays(Canada(years=1866)) + self.assertNoHolidays(Canada(years=1866, categories=(GOVERNMENT,))) + self.assertNoHolidays(Canada(years=1866, categories=(OPTIONAL,))) - def test_new_years(self): - self.assertHoliday(f"{year}-01-01" for year in range(1900, 2050)) - self.assertHoliday("2011-01-03", "2017-01-02") - self.assertNoNonObservedHoliday("2011-01-03", "2017-01-02") - - def test_islander_day(self): - dt = ( - "2010-02-15", - "2011-02-21", - "2012-02-20", - "2013-02-18", - "2014-02-17", - "2015-02-16", - "2016-02-15", - "2020-02-17", - ) - self.assertHoliday(self.prov_hols["PE"], dt, "2009-02-09") - for d in dt: - self.assertNotEqual(self.holidays[d], "Islander Day") - - def test_yukon_heritage_day(self): - # https://www.timeanddate.com/holidays/canada/heritage-day-yukon - dt = ( - "2017-02-24", - "2018-02-23", - "2019-02-22", - "2020-02-21", - "2021-02-26", - "2022-02-25", - ) - self.assertHoliday(self.prov_hols["YT"], dt) - - def test_family_day(self): - ab_holidays = self.prov_hols["AB"] - bc_holidays = self.prov_hols["BC"] - mb_holidays = self.prov_hols["MB"] - sk_holidays = self.prov_hols["SK"] - nb_holidays = self.prov_hols["NB"] - ns_holidays = self.prov_hols["NS"] - dt = ( - "1990-02-19", - "1999-02-15", - "2000-02-21", - "2006-02-20", + def test_new_years_day(self): + name = "New Year's Day" + name_observed = f"{name} (Observed)" + self.assertHolidayName(name, (f"{year}-01-01" for year in range(1867, 2050))) + self.assertHolidayName( + name, self.gov_hols, (f"{year}-01-01" for year in range(1867, 2050)) ) - self.assertNoHoliday(dt) - self.assertHoliday(ab_holidays, dt) - self.assertNoHoliday(bc_holidays, dt) - self.assertNoHoliday(mb_holidays, dt) - self.assertNoHoliday(sk_holidays, dt) - d = "2007-02-19" - self.assertNoHoliday(d) - self.assertHoliday(ab_holidays, d) - self.assertNoHoliday(bc_holidays, d) - self.assertNoHoliday(mb_holidays, d) - self.assertHoliday(sk_holidays, d) - dt = ( - "2008-02-18", - "2012-02-20", - "2014-02-17", - "2018-02-19", + for _, holidays in self.prov_hols.items(): + self.assertHolidayName(name, holidays, (f"{year}-01-01" for year in range(1867, 2050))) + + dts = ( + "2011-01-03", + "2012-01-02", + "2017-01-02", + "2022-01-03", + "2023-01-02", ) - self.assertHoliday(dt) - self.assertHoliday(ab_holidays, dt) - self.assertNoHoliday(bc_holidays, dt) - self.assertHoliday(mb_holidays, dt) - self.assertHoliday(sk_holidays, dt) - self.assertHoliday(nb_holidays, "2018-02-19") - dt = ("2019-02-18", "2020-02-17") - self.assertHoliday(dt) - self.assertHoliday(ab_holidays, dt) - self.assertHoliday(bc_holidays, dt) - self.assertHoliday(mb_holidays, dt) - self.assertHoliday(sk_holidays, dt) - dt = ("2013-02-11", "2016-02-08") - self.assertNoHoliday(dt) - self.assertNoHoliday(ab_holidays, dt) - self.assertHoliday(bc_holidays, dt) - self.assertNoHoliday(mb_holidays, dt) - self.assertNoHoliday(sk_holidays, dt) - self.assertHolidayName("Louis Riel Day", mb_holidays, "2014-02-17") - self.assertHolidayName("Heritage Day", ns_holidays, "2015-02-16") - - def test_st_patricks_day(self): - nl_holidays = self.prov_hols["NL"] - dt = ( - "1900-03-19", - "1999-03-15", - "2000-03-20", - "2012-03-19", - "2013-03-18", - "2014-03-17", - "2015-03-16", - "2016-03-14", - "2020-03-16", - ) - self.assertNoHoliday(dt) - self.assertHoliday(nl_holidays, dt) - self.assertNoHoliday(nl_holidays, "1899-03-20") + self.assertHolidayName(name_observed, dts) + self.assertHolidayName(name_observed, self.gov_hols, dts) + for prov, holidays in self.prov_hols.items(): + self.assertHolidayName(name_observed, holidays, dts) + self.assertNoNonObservedHoliday(CA(subdiv=prov, observed=False), dts) + self.assertNoNonObservedHoliday(dts) def test_good_friday(self): - self.assertHoliday( + name = "Good Friday" + dts = ( "1900-04-13", "1901-04-05", "1902-03-28", @@ -137,118 +69,436 @@ def test_good_friday(self): "2018-03-30", "2019-04-19", "2020-04-10", + "2021-04-02", + "2022-04-15", + "2023-04-07", ) + self.assertHolidayName(name, dts) + self.assertHolidayName(name, range(1867, 2050)) + self.assertHolidayName(name, self.gov_hols, dts) + self.assertHolidayName(name, self.gov_hols, range(1867, 2050)) + for _, holidays in self.prov_hols.items(): + self.assertHolidayName(name, holidays, dts) + self.assertHolidayName(name, holidays, range(1867, 2050)) - def test_easter_monday(self): - self.assertHoliday( - "1900-04-16", - "1901-04-08", - "1902-03-31", - "1999-04-05", - "2000-04-24", - "2010-04-05", - "2018-04-02", - "2019-04-22", - "2020-04-13", + def test_canada_day(self): + name_1 = "Dominion Day" + name_2 = "Canada Day" + self.assertHolidayName(name_1, (f"{year}-07-01" for year in range(1879, 1983))) + self.assertHolidayName(name_2, (f"{year}-07-01" for year in range(1983, 2050))) + self.assertNoHolidayName(name_1, range(1867, 1879), range(1983, 2050)) + self.assertNoHolidayName(name_2, range(1867, 1983)) + self.assertNoHoliday(f"{year}-07-01" for year in range(1867, 1879)) + + dts_sat = ( + "2000-07-03", + "2006-07-03", + "2017-07-03", + "2023-07-03", + ) + dts_sun = ( + "2001-07-02", + "2007-07-02", + "2012-07-02", + "2018-07-02", + ) + name_observed = f"{name_2} (Observed)" + self.assertNoHoliday(dts_sat, dts_sun) + self.assertHolidayName(name_observed, self.gov_hols, dts_sat, dts_sun) + self.assertNoNonObservedHoliday( + CA(observed=False, categories=(GOVERNMENT,)), dts_sat, dts_sun + ) + for prov, holidays in self.prov_hols.items(): + if prov in {"AB", "BC", "QC"}: + self.assertHolidayName(name_observed, self.prov_hols[prov], dts_sun) + self.assertNoHoliday(self.prov_hols[prov], dts_sat) + elif prov in {"NL", "PE", "SK", "YT"}: + self.assertHoliday(self.prov_hols[prov], dts_sat, dts_sun) + else: + self.assertNoHoliday(self.prov_hols[prov], dts_sat, dts_sun) + self.assertNoNonObservedHoliday(CA(subdiv=prov, observed=False), dts_sat, dts_sun) + + def test_labour_day(self): + name = "Labour Day" + self.assertNoHolidayName(name, range(1867, 1894)) + self.assertHolidayName(name, range(1894, 2050)) + self.assertNoHolidayName(name, self.gov_hols, range(1867, 1894)) + self.assertHolidayName(name, self.gov_hols, range(1894, 2050)) + + dts = ( + "1894-09-03", + "1900-09-03", + "1999-09-06", + "2000-09-04", + "2014-09-01", + "2015-09-07", + "2018-09-03", + "2019-09-02", + "2020-09-07", + "2021-09-06", + "2022-09-05", + "2023-09-04", ) + self.assertHolidayName(name, dts) + self.assertHolidayName(name, self.gov_hols, dts) + for _, holidays in self.prov_hols.items(): + self.assertHolidayName(name, holidays, dts) + self.assertHolidayName(name, holidays, range(1894, 2050)) + self.assertNoHolidayName(name, holidays, range(1867, 1894)) - def test_st_georges_day(self): - dt = ( - "1990-04-23", - "1999-04-26", - "2010-04-19", - "2016-04-25", - "2020-04-20", + def test_christmas_day(self): + name = "Christmas Day" + name_observed = f"{name} (Observed)" + self.assertHolidayName(name, (f"{year}-12-25" for year in range(1867, 2050))) + for _, holidays in self.prov_hols.items(): + self.assertHolidayName(name, holidays, (f"{year}-12-25" for year in range(1867, 2050))) + + dts_sat = ( + "2004-12-27", + "2010-12-27", + "2021-12-27", + ) + dts_sun_without_boxing = ( + "2005-12-26", + "2011-12-26", + "2016-12-26", + "2022-12-26", + ) + dts_sun_with_boxing = ( + "2005-12-27", + "2011-12-27", + "2016-12-27", + "2022-12-27", + ) + self.assertHolidayName(name_observed, dts_sat, dts_sun_without_boxing) + self.assertNoHoliday(dts_sun_with_boxing) + self.assertNoNonObservedHoliday(dts_sat, dts_sun_with_boxing, dts_sun_without_boxing) + self.assertHolidayName(name_observed, self.gov_hols, dts_sat, dts_sun_with_boxing) + self.assertNoNonObservedHoliday( + CA(observed=False, categories=(GOVERNMENT,)), + dts_sat, + dts_sun_with_boxing, + ) + self.assertHolidayName( + name_observed, CA(categories=(OPTIONAL,)), dts_sat, dts_sun_with_boxing + ) + self.assertNoNonObservedHoliday( + CA(observed=False, categories=(OPTIONAL,)), + dts_sat, + dts_sun_with_boxing, ) - self.assertNoHoliday(dt) - self.assertHoliday(self.prov_hols["NL"], dt) + for prov, holidays in self.prov_hols.items(): + self.assertHolidayName(name_observed, holidays, dts_sat, dts_sun_without_boxing) + self.assertNoHoliday(holidays, dts_sun_with_boxing) + self.assertNoNonObservedHoliday( + CA(subdiv=prov, observed=False), + dts_sat, + dts_sun_with_boxing, + ) def test_victoria_day(self): - dt = ("1953-05-18", "1999-05-24", "2000-05-22") - self.assertHoliday(dt) + name = "Victoria Day" + dts = ( + "1953-05-18", + "2000-05-22", + "2010-05-24", + "2018-05-21", + "2019-05-20", + "2020-05-18", + "2021-05-24", + "2022-05-23", + "2023-05-22", + ) + self.assertNoHolidayName(name) + self.assertHolidayName(name, self.gov_hols, dts) + self.assertHolidayName(name, self.gov_hols, range(1953, 2050)) + self.assertNoHolidayName(name, self.gov_hols, range(1867, 1953)) + for prov, holidays in self.prov_hols.items(): - if prov in {"NL", "NS", "PE", "QC"}: - self.assertNoHoliday(holidays, dt) + if prov in {"AB", "BC", "MB", "NT", "NU", "ON", "SK", "YT"}: + self.assertHolidayName(name, holidays, dts) + self.assertHolidayName(name, holidays, range(1953, 2050)) + self.assertNoHolidayName(name, holidays, range(1867, 1953)) else: - self.assertHoliday(holidays, dt) + self.assertNoHolidayName(name, holidays) + + def test_national_day_for_truth_and_reconciliation(self): + name = "National Day for Truth and Reconciliation" + name_observed = f"{name} (Observed)" + self.assertNoHolidayName(name) + self.assertHolidayName( + name, self.gov_hols, (f"{year}-09-30" for year in range(2021, 2050)) + ) + self.assertNoHolidayName(name, self.gov_hols, range(1867, 2021)) - dt = ("2010-05-24", "2015-05-18", "2020-05-18") - self.assertHoliday(dt) + dts = ( + "2023-10-02", + "2028-10-02", + "2029-10-01", + ) + self.assertHolidayName(name_observed, self.gov_hols, dts) + self.assertNoNonObservedHoliday(CA(observed=False, categories=(GOVERNMENT,)), dts) + + start_years = { + "AB": 2021, + "BC": 2023, + "NT": 2022, + "NU": 2022, + "PE": 2022, + "YT": 2023, + } for prov, holidays in self.prov_hols.items(): - if prov in {"NL", "NS", "PE"}: - self.assertNoHoliday(holidays, dt) + if prov in {"BC", "NT", "NU", "PE", "YT"}: + self.assertHolidayName( + name, holidays, (f"{year}-09-30" for year in range(start_years[prov], 2050)) + ) + self.assertNoHolidayName(name, holidays, range(1867, start_years[prov])) else: - self.assertHoliday(holidays, dt) + self.assertNoHolidayName(name, holidays) + self.assertNoNonObservedHoliday(CA(subdiv=prov, observed=False), dts) - def test_national_patriots_day(self): self.assertHolidayName( - "National Patriots' Day", - self.prov_hols["QC"], - "2010-05-24", - "2015-05-18", - "2020-05-18", - "2021-05-24", - "2022-05-23", + name, + CA(subdiv="AB", categories=(OPTIONAL,)), + (f"{year}-09-30" for year in range(2021, 2050)), ) - def test_national_aboriginal_day(self): - nt_holidays = self.prov_hols["NT"] - self.assertNoHoliday(nt_holidays, "1995-06-21") - self.assertNoHoliday(f"{year}-06-21" for year in range(1996, 2050)) - self.assertHoliday(nt_holidays, (f"{year}-06-21" for year in range(1996, 2050))) + def test_thanksgiving_day(self): + name = "Thanksgiving Day" + self.assertNoHolidayName(name) + self.assertHolidayName(name, self.gov_hols, range(1931, 2050)) + self.assertNoHolidayName(name, self.gov_hols, range(1867, 1931)) - def test_st_jean_baptiste_day(self): - qc_holidays = self.prov_hols["QC"] - self.assertNoHoliday(qc_holidays, "1924-06-24") - self.assertNoHoliday(f"{year}-06-24" for year in range(1925, 2050)) - self.assertHoliday(qc_holidays, (f"{year}-06-24" for year in range(1925, 2050))) - self.assertHoliday(qc_holidays, "2001-06-25") - self.assertNoNonObservedHoliday(Canada(subdiv="QC", observed=False), "2001-06-25") + dts = ( + "1931-10-12", + "1935-10-25", + "1990-10-08", + "1999-10-11", + "2000-10-09", + "2013-10-14", + "2018-10-08", + "2019-10-14", + "2020-10-12", + "2021-10-11", + "2022-10-10", + "2023-10-09", + ) + self.assertHolidayName(name, self.gov_hols, dts) + for prov, holidays in self.prov_hols.items(): + if prov in {"AB", "BC", "MB", "NT", "NU", "ON", "QC", "SK", "YT"}: + self.assertHolidayName(name, holidays, dts) + self.assertHolidayName(name, holidays, range(1931, 2050)) + self.assertNoHolidayName(name, holidays, range(1867, 1931)) + else: + self.assertNoHolidayName(name, holidays, dts) - def test_discovery_day(self): - nl_holidays = self.prov_hols["NL"] - yt_holidays = self.prov_hols["YT"] - dt = ( - "1997-06-23", - "1999-06-21", - "2000-06-26", - "2010-06-21", - "2016-06-27", - "2020-06-22", + for prov in ("NB", "NL"): + self.assertHolidayName(name, CA(subdiv=prov, categories=(OPTIONAL,)), dts) + + def test_remembrance_day(self): + name = "Remembrance Day" + name_observed = f"{name} (Observed)" + self.assertNoHolidayName(name) + self.assertHolidayName( + name, self.gov_hols, (f"{year}-11-11" for year in range(1931, 2050)) + ) + self.assertNoHoliday(self.gov_hols, (f"{year}-11-11" for year in range(1900, 1931))) + self.assertNoHolidayName(name, self.gov_hols, range(1900, 1931)) + + dts = ( + "2006-11-13", + "2007-11-12", + "2012-11-12", + "2017-11-13", + "2018-11-12", + "2023-11-13", ) - self.assertNoHoliday(dt) - self.assertHoliday(nl_holidays, dt) - self.assertNoHoliday(yt_holidays, dt) + self.assertHolidayName(name_observed, self.gov_hols, dts) + self.assertNoNonObservedHoliday(CA(observed=False, categories=(GOVERNMENT,)), dts) - dt = ( - "1912-08-19", - "1999-08-16", - "2000-08-21", - "2006-08-21", - "2016-08-15", - "2020-08-17", + for prov, holidays in self.prov_hols.items(): + if prov in {"AB", "BC", "NB", "NL", "NS", "NT", "NU", "PE", "SK", "YT"}: + start_year = 1981 if prov == "NS" else 1931 + self.assertHolidayName( + name, holidays, (f"{year}-11-11" for year in range(start_year, 2050)) + ) + self.assertNoHoliday( + holidays, (f"{year}-11-11" for year in range(1900, start_year)) + ) + self.assertNoHolidayName(name, holidays, range(1900, start_year)) + else: + self.assertNoHolidayName(name, holidays) + + if prov in {"AB", "NL", "NS", "PE", "SK", "YT"}: + self.assertHolidayName(name_observed, self.prov_hols[prov], dts) + self.assertNoNonObservedHoliday(CA(subdiv=prov, observed=False), dts) + else: + self.assertNoHoliday(holidays, dts) + + self.assertHolidayName( + name, + CA(subdiv="MB", categories=(OPTIONAL,)), + (f"{year}-11-11" for year in range(1931, 2050)), ) - self.assertNoHoliday(dt) - self.assertNoHoliday(nl_holidays, dt) - self.assertHoliday(yt_holidays, dt) - def test_canada_day(self): - self.assertHoliday(f"{year}-07-01" for year in range(1900, 2050)) - self.assertHoliday("2006-07-03", "2007-07-02") - self.assertNoNonObservedHoliday("2006-07-03", "2007-07-02") + def test_boxing_day(self): + name = "Boxing Day" + name_observed = f"{name} (Observed)" + self.assertNoHolidayName(name) + self.assertHolidayName( + name, self.gov_hols, (f"{year}-12-26" for year in range(1867, 2050)) + ) - def test_nunavut_day(self): - nu_holidays = self.prov_hols["NU"] - self.assertNoHoliday(nu_holidays, "1999-07-09", "2000-07-09") - self.assertHoliday(nu_holidays, "2000-04-01") - self.assertNoHoliday(f"{year}-07-09" for year in range(2001, 2050)) - self.assertHoliday(nu_holidays, (f"{year}-07-09" for year in range(2001, 2050))) - self.assertHoliday(nu_holidays, "2017-07-10") - self.assertNoNonObservedHoliday(Canada(subdiv="NU", observed=False), "2017-07-10") + opt_holidays = CA(years=range(1867, 2050), categories=(OPTIONAL,)) + self.assertHolidayName(name, opt_holidays, (f"{year}-12-26" for year in range(1867, 2050))) + + dts = ( + "2004-12-28", + "2009-12-28", + "2010-12-28", + "2015-12-28", + "2020-12-28", + "2021-12-28", + ) + self.assertHolidayName(name_observed, opt_holidays, dts) + self.assertNoNonObservedHoliday(CA(observed=False, categories=(OPTIONAL,)), dts) + + dts = ( + "2004-12-28", + "2010-12-28", + "2021-12-28", + ) + self.assertHolidayName(name_observed, self.prov_hols["ON"], dts) + self.assertNoNonObservedHoliday(CA(subdiv="ON", observed=False), dts) + + for prov in ("AB", "NB", "NL"): + self.assertHolidayName( + name, + CA(subdiv=prov, categories=(OPTIONAL,)), + (f"{year}-12-26" for year in range(1867, 2050)), + ) + + def test_family_day(self): + start_years = { + "AB": 1990, + "BC": 2019, + "MB": 2008, + "NB": 2018, + "NS": 2015, + "ON": 2008, + "PE": 2010, + "SK": 2007, + } + dts = ( + "1990-02-19", + "1991-02-18", + "1992-02-17", + "1993-02-15", + "1994-02-21", + "1995-02-20", + "1996-02-19", + "1997-02-17", + "1998-02-16", + "1999-02-15", + "2000-02-21", + "2001-02-19", + "2002-02-18", + "2003-02-17", + "2004-02-16", + "2005-02-21", + "2006-02-20", + "2007-02-19", + "2008-02-18", + "2009-02-16", + "2010-02-15", + "2011-02-21", + "2012-02-20", + "2013-02-18", + "2014-02-17", + "2015-02-16", + "2016-02-15", + "2017-02-20", + "2018-02-19", + "2019-02-18", + "2020-02-17", + "2021-02-15", + "2022-02-21", + "2023-02-20", + ) + prov_names = { + "MB": "Louis Riel Day", + "NS": "Heritage Day", + "PE": "Islander Day", + } + for prov, holidays in self.prov_hols.items(): + name = prov_names.get(prov, "Family Day") + for year, dt in enumerate(dts, 1990): + if prov in start_years and year >= start_years[prov]: + self.assertHolidayName(name, holidays, dt) + else: + self.assertNoHoliday(holidays, dt) + self.assertNoHoliday(dts) + self.assertNoHolidayName("Family Day") + for name in prov_names.values(): + self.assertNoHolidayName(name) + + self.assertHoliday( + self.prov_hols["BC"], + "2013-02-11", + "2014-02-10", + "2015-02-09", + "2016-02-08", + "2017-02-13", + "2018-02-12", + ) + self.assertHoliday(self.prov_hols["PE"], "2009-02-09") + + def test_easter_monday(self): + name = "Easter Monday" + dts = ( + "1900-04-16", + "1901-04-08", + "1902-03-31", + "1999-04-05", + "2000-04-24", + "2010-04-05", + "2018-04-02", + "2019-04-22", + "2020-04-13", + ) + self.assertNoHoliday(dts) + self.assertNoHolidayName(name) + self.assertNoHoliday(self.gov_hols, dts) + self.assertNoHolidayName(name, self.gov_hols) + + for prov, holidays in self.prov_hols.items(): + self.assertNoHoliday(holidays, dts) + self.assertNoHolidayName(name, holidays) + + if prov in {"AB", "QC"}: + self.assertHolidayName(name, CA(subdiv=prov, categories=(OPTIONAL,)), dts) + + def test_civic_holiday_ab(self): + name = "Heritage Day" + self.assertNoHolidayName(name, self.prov_hols["AB"]) + ab_opt_holidays = CA(subdiv="AB", categories=(OPTIONAL,)) + dts = ( + "1974-08-05", + "1999-08-02", + "2000-08-07", + "2010-08-02", + "2015-08-03", + "2020-08-03", + ) + self.assertNoHoliday(dts) + self.assertNoHolidayName(name) + self.assertHoliday(ab_opt_holidays, dts) + self.assertNoHoliday(ab_opt_holidays, "1973-08-06") def test_civic_holiday_bc(self): + name = "British Columbia Day" bc_holidays = self.prov_hols["BC"] - dt = ( + dts = ( "1974-08-05", "1999-08-02", "2000-08-07", @@ -256,12 +506,19 @@ def test_civic_holiday_bc(self): "2015-08-03", "2020-08-03", ) - self.assertHoliday(bc_holidays, dt) + self.assertNoHoliday(dts) + self.assertNoHolidayName(name) + self.assertHolidayName(name, bc_holidays, dts) self.assertNoHoliday(bc_holidays, "1973-08-06") def test_civic_holiday_mb(self): - mb_holidays = self.prov_hols["MB"] - dt = ( + old_name = "Civic Holiday" + new_name = "Terry Fox Day" + self.assertNoHolidayName(old_name, self.prov_hols["MB"]) + self.assertNoHolidayName(new_name, self.prov_hols["MB"]) + + mb_opt_holidays = CA(subdiv="MB", categories=(OPTIONAL,)) + dts = ( "1900-08-06", "1999-08-02", "2000-08-07", @@ -269,20 +526,57 @@ def test_civic_holiday_mb(self): "2015-08-03", "2020-08-03", ) - self.assertHoliday(mb_holidays, dt) - self.assertNoHoliday(mb_holidays, "1899-08-07") - old_name = "Civic Holiday" - new_name = "Terry Fox Day" - self.assertHolidayName(old_name, mb_holidays, "2014-08-04") - self.assertHolidayName(new_name, mb_holidays, "2015-08-03") - self.assertNoHolidayName(old_name, mb_holidays, 2015) - self.assertNoHolidayName(new_name, mb_holidays, 2014) - - def test_civic_holiday_nb_nt_sk(self): + self.assertNoHoliday(dts) + self.assertNoHolidayName(old_name) + self.assertNoHolidayName(new_name) + self.assertHoliday(mb_opt_holidays, dts) + self.assertNoHoliday(mb_opt_holidays, "1899-08-07") + self.assertHolidayName(old_name, mb_opt_holidays, "2014-08-04") + self.assertHolidayName(new_name, mb_opt_holidays, "2015-08-03") + self.assertNoHolidayName(old_name, mb_opt_holidays, 2015) + self.assertNoHolidayName(new_name, mb_opt_holidays, 2014) + + def test_civic_holiday_nb(self): + name = "New Brunswick Day" nb_holidays = self.prov_hols["NB"] + dts = ( + "1975-08-04", + "1999-08-02", + "2000-08-07", + "2010-08-02", + "2015-08-03", + "2020-08-03", + ) + self.assertNoHoliday(dts) + self.assertNoHolidayName(name) + self.assertHolidayName(name, nb_holidays, dts) + self.assertNoHoliday(nb_holidays, "1974-08-05") + self.assertNoHolidayName(name, nb_holidays, range(1867, 1975)) + + def test_civic_holiday_ns(self): + name = "Natal Day" + self.assertNoHolidayName(name, self.prov_hols["NS"]) + ns_opt_holidays = CA(subdiv="NS", categories=(OPTIONAL,)) + dts = ( + "1996-08-05", + "1999-08-02", + "2000-08-07", + "2010-08-02", + "2015-08-03", + "2020-08-03", + ) + self.assertNoHoliday(dts) + self.assertNoHolidayName(name) + self.assertHoliday(ns_opt_holidays, dts) + self.assertNoHoliday(ns_opt_holidays, "1995-08-07") + + def test_civic_holiday_nt_nu_on_sk(self): + name = "Civic Holiday" nt_holidays = self.prov_hols["NT"] + nu_holidays = self.prov_hols["NU"] + on_opt_holidays = CA(subdiv="ON", categories=(OPTIONAL,)) sk_holidays = self.prov_hols["SK"] - dt = ( + dts = ( "1900-08-06", "1999-08-02", "2000-08-07", @@ -290,92 +584,170 @@ def test_civic_holiday_nb_nt_sk(self): "2015-08-03", "2020-08-03", ) - self.assertHoliday(nb_holidays, dt) - self.assertHoliday(nt_holidays, dt) - self.assertHoliday(sk_holidays, dt) - self.assertNoHoliday(nb_holidays, "1899-08-07") + self.assertNoHoliday(dts) + self.assertNoHolidayName(name) + self.assertHolidayName(name, nt_holidays, dts) + self.assertHolidayName(name, nu_holidays, dts) + self.assertHolidayName(name, on_opt_holidays, dts) + self.assertHolidayName("Saskatchewan Day", sk_holidays, dts) self.assertNoHoliday(nt_holidays, "1899-08-07") + self.assertNoHoliday(nu_holidays, "1899-08-07") + self.assertNoHoliday(on_opt_holidays, "1899-08-07") self.assertNoHoliday(sk_holidays, "1899-08-07") - def test_labour_day(self): - self.assertNoHoliday("1893-09-04") - self.assertHoliday( - "1894-09-03", - "1900-09-03", - "1999-09-06", - "2000-09-04", - "2014-09-01", - "2015-09-07", + def test_memorial_day(self): + name = "Memorial Day" + self.assertNoHolidayName(name) + nl_holidays = self.prov_hols["NL"] + self.assertHolidayName(name, nl_holidays, (f"{year}-07-01" for year in range(1917, 2050))) + self.assertNoHolidayName(name, nl_holidays, range(1900, 1917)) + + def test_st_patricks_day(self): + name = "St. Patrick's Day" + self.assertNoHolidayName(name, self.prov_hols["NL"]) + nl_opt_holidays = CA(subdiv="NL", categories=(OPTIONAL,)) + dts = ( + "1900-03-19", + "1999-03-15", + "2000-03-20", + "2012-03-19", + "2013-03-18", + "2014-03-17", + "2015-03-16", + "2016-03-14", + "2020-03-16", ) + self.assertNoHoliday(dts) + self.assertNoHolidayName(name) + self.assertHolidayName(name, nl_opt_holidays, dts) + self.assertNoHoliday(nl_opt_holidays, "1899-03-20") - def test_national_day_for_truth_and_reconciliation(self): - bc_holidays = self.prov_hols["BC"] - mb_holidays = self.prov_hols["MB"] - ns_holidays = self.prov_hols["NS"] - - dt = ("1991-09-30", "2020-09-30") - self.assertNoHoliday(dt) - self.assertNoHoliday(mb_holidays, dt) - self.assertNoHoliday(ns_holidays, dt) - - dt = ("2021-09-30", "2022-09-30") - self.assertHoliday(mb_holidays, dt) - self.assertHoliday(ns_holidays, dt) - self.assertNoHoliday(dt) - - dt = ("2023-09-30", "2024-09-30", "2030-09-30") - self.assertHoliday(mb_holidays, dt) - self.assertHoliday(ns_holidays, dt) - self.assertHoliday(bc_holidays, dt) - self.assertNoHoliday(dt) - - def test_thanksgiving(self): - nb_holidays = self.prov_hols["NB"] - nl_holidays = self.prov_hols["NL"] - ns_holidays = self.prov_hols["NS"] - pe_holidays = self.prov_hols["PE"] + def test_st_georges_day(self): + name = "St. George's Day" + self.assertNoHolidayName(name, self.prov_hols["NL"]) + nl_opt_holidays = CA(subdiv="NL", categories=(OPTIONAL,)) + dts = ( + "1990-04-23", + "1999-04-26", + "2010-04-19", + "2016-04-25", + "2020-04-20", + ) + self.assertNoHoliday(dts) + self.assertNoHolidayName(name) + self.assertHolidayName(name, nl_opt_holidays, dts) + self.assertNoHoliday(nl_opt_holidays, "1989-04-24") + + def test_discovery_day_nl(self): + name = "Discovery Day" + self.assertNoHolidayName(name, self.prov_hols["NL"]) + nl_opt_holidays = CA(subdiv="NL", categories=(OPTIONAL,)) + dts = ( + "1997-06-23", + "1999-06-21", + "2000-06-26", + "2010-06-21", + "2016-06-27", + "2020-06-22", + ) + self.assertNoHoliday(dts) + self.assertNoHolidayName(name) + self.assertHoliday(nl_opt_holidays, dts) + self.assertNoHoliday(nl_opt_holidays, "1996-06-24") + + def test_orangemans_day(self): + name = "Orangemen's Day" + self.assertNoHolidayName(name, self.prov_hols["NL"]) + nl_opt_holidays = CA(subdiv="NL", categories=(OPTIONAL,)) + dts = ( + "1900-07-09", + "1999-07-12", + "2000-07-10", + "2010-07-12", + "2016-07-11", + "2020-07-13", + ) + self.assertNoHoliday(dts) + self.assertNoHolidayName(name) + self.assertHoliday(nl_opt_holidays, dts) + self.assertNoHoliday(nl_opt_holidays, "1899-07-10") - dt = ( - "1931-10-12", - "1935-10-25", - "1990-10-08", - "1999-10-11", - "2000-10-09", - "2013-10-14", - "2020-10-12", + def test_discovery_day_yt(self): + name = "Discovery Day" + yt_holidays = self.prov_hols["YT"] + dts = ( + "1912-08-19", + "1999-08-16", + "2000-08-21", + "2006-08-21", + "2016-08-15", + "2020-08-17", ) - self.assertHoliday(dt) - self.assertNoHoliday(nb_holidays, dt) - self.assertNoHoliday(nl_holidays, dt) - self.assertNoHoliday(ns_holidays, dt) - self.assertNoHoliday(pe_holidays, dt) + self.assertNoHoliday(dts) + self.assertNoHolidayName(name) + self.assertHoliday(yt_holidays, dts) + self.assertNoHoliday(yt_holidays, "1911-08-21") - def test_remembrance_day(self): - ab_holidays = self.prov_hols["AB"] - nl_holidays = self.prov_hols["NL"] - self.assertNoHoliday(nl_holidays, "1930-11-11") - self.assertNoHoliday(ab_holidays, "1930-11-11") + def test_national_aboriginal_day(self): + name = "National Aboriginal Day" + nt_holidays = self.prov_hols["NT"] + yt_holidays = self.prov_hols["YT"] + self.assertHolidayName(name, nt_holidays, (f"{year}-06-21" for year in range(1996, 2050))) + self.assertNoHolidayName(nt_holidays, range(1867, 1996)) + self.assertHolidayName(name, yt_holidays, (f"{year}-06-21" for year in range(2017, 2050))) + self.assertNoHolidayName(yt_holidays, range(1867, 2017)) + self.assertNoHoliday(f"{year}-06-21" for year in range(1996, 2050)) + self.assertNoHolidayName(name) - self.assertNoHoliday(f"{year}-11-11" for year in range(1931, 2050)) - self.assertHoliday(ab_holidays, (f"{year}-11-11" for year in range(1931, 2050))) - self.assertHoliday(nl_holidays, (f"{year}-11-11" for year in range(1931, 2050))) + def test_nunavut_day(self): + name = "Nunavut Day" + self.assertNoHolidayName(name) + self.assertNoHoliday(f"{year}-07-09" for year in range(2001, 2050)) + self.assertNoHolidayName(name, self.prov_hols["NU"]) + nu_opt_holidays = CA(subdiv="NU", categories=(OPTIONAL,)) + self.assertNoHoliday(nu_opt_holidays, "1999-07-09", "2000-07-09") + self.assertHoliday(nu_opt_holidays, "2000-04-01") + self.assertHoliday(nu_opt_holidays, (f"{year}-07-09" for year in range(2001, 2050))) - self.assertNoHoliday(ab_holidays, "2007-11-12") - self.assertHoliday(nl_holidays, "2007-11-12") - self.assertNoNonObservedHoliday(Canada(subdiv="AB", observed=False), "2007-11-12") - self.assertNoNonObservedHoliday(Canada(subdiv="NL", observed=False), "2007-11-12") + def test_national_patriots_day(self): + name = "National Patriots' Day" + self.assertNoHolidayName(name) + qc_holidays = self.prov_hols["QC"] + self.assertHolidayName( + name, + qc_holidays, + "2010-05-24", + "2015-05-18", + "2020-05-18", + "2021-05-24", + "2022-05-23", + ) + self.assertNoHolidayName(name, qc_holidays, range(1867, 2003)) - def test_christmas_day(self): - self.assertHoliday(f"{year}-12-25" for year in range(1900, 2050)) - self.assertHoliday("2010-12-27", "2011-12-27") - self.assertNoNonObservedHoliday("2010-12-27", "2011-12-27") - self.assertNotIn("Christmas Day (Observed)", self.holidays["2011-12-26"]) - self.assertHolidayName("Christmas Day (Observed)", "2011-12-27") + def test_st_jean_baptiste_day(self): + name = "St. Jean Baptiste Day" + self.assertNoHolidayName(name) + qc_holidays = self.prov_hols["QC"] + self.assertHoliday(qc_holidays, (f"{year}-06-24" for year in range(1925, 2050))) + self.assertNoHoliday(qc_holidays, (f"{year}-06-24" for year in range(1867, 1925))) + self.assertNoHoliday(f"{year}-06-24" for year in range(1925, 2050)) + self.assertHoliday(qc_holidays, "2001-06-25") + self.assertNoNonObservedHoliday(Canada(subdiv="QC", observed=False), "2001-06-25") - def test_boxing_day(self): - self.assertHoliday(f"{year}-12-26" for year in range(1900, 2050)) - self.assertHoliday("2009-12-28", "2010-12-27") - self.assertNoNonObservedHoliday("2009-12-28", "2010-12-27") + def test_yukon_heritage_day(self): + name = "Heritage Day" + self.assertNoHolidayName(name) + self.assertNoHolidayName(name, self.prov_hols["YT"]) + yt_opt_holidays = CA(subdiv="YT", categories=(OPTIONAL,)) + dts = ( + "2017-02-24", + "2018-02-23", + "2019-02-22", + "2020-02-21", + "2021-02-26", + "2022-02-25", + ) + self.assertHolidayName(name, yt_opt_holidays, dts) def test_queens_funeral(self): for prov, holidays in self.prov_hols.items(): @@ -384,20 +756,94 @@ def test_queens_funeral(self): else: self.assertNoHoliday(holidays, "2022-09-19") + def test_public_2022(self): + self.assertHolidays( + Canada(years=2022), + ("2022-01-01", "New Year's Day"), + ("2022-01-03", "New Year's Day (Observed)"), + ("2022-04-15", "Good Friday"), + ("2022-07-01", "Canada Day"), + ("2022-09-05", "Labour Day"), + ("2022-12-25", "Christmas Day"), + ("2022-12-26", "Christmas Day (Observed)"), + ) + + def test_government_2022(self): + self.assertHolidays( + Canada(years=2022, categories=(GOVERNMENT,)), + ("2022-01-01", "New Year's Day"), + ("2022-01-03", "New Year's Day (Observed)"), + ("2022-04-15", "Good Friday"), + ("2022-05-23", "Victoria Day"), + ("2022-07-01", "Canada Day"), + ("2022-09-05", "Labour Day"), + ("2022-09-30", "National Day for Truth and Reconciliation"), + ("2022-10-10", "Thanksgiving Day"), + ("2022-11-11", "Remembrance Day"), + ("2022-12-25", "Christmas Day"), + ("2022-12-26", "Boxing Day"), + ("2022-12-27", "Christmas Day (Observed)"), + ) + + def test_optional_2022(self): + self.assertHolidays( + Canada(years=2022, categories=(OPTIONAL,)), + ("2022-12-25", "Christmas Day"), + ("2022-12-26", "Boxing Day"), + ("2022-12-27", "Christmas Day (Observed)"), + ) + + def test_all_holidays_present(self): + y_2022 = set() + for prov in Canada.subdivisions: + y_2022.update(Canada(years=2022, subdiv=prov, observed=False).values()) + all_h = { # Holidays names in their chronological order. + "New Year's Day", + "Family Day", + "Heritage Day", + "Islander Day", + "Louis Riel Day", + "Good Friday", + "National Patriots' Day", + "Victoria Day", + "National Aboriginal Day", + "St. Jean Baptiste Day", + "Canada Day", + "Canada Day; Memorial Day", + "British Columbia Day", + "Civic Holiday", + "New Brunswick Day", + "Saskatchewan Day", + "Discovery Day", + "Labour Day", + "Funeral of Her Majesty the Queen Elizabeth II", + "National Day for Truth and Reconciliation", + "Thanksgiving Day", + "Remembrance Day", + "Christmas Day", + "Boxing Day", + } + + self.assertEqual( + all_h, + y_2022, + f"missing: {all_h - y_2022 if len(all_h - y_2022) > 0 else 'no'}," + f" extra: {y_2022 - all_h if len(y_2022 - all_h) > 0 else 'no'}", + ) + def test_l10n_default(self): self.assertLocalizedHolidays( ("2022-01-01", "New Year's Day"), ("2022-01-03", "New Year's Day (Observed)"), - ("2022-02-21", "Family Day"), ("2022-04-15", "Good Friday"), - ("2022-04-18", "Easter Monday"), ("2022-05-23", "Victoria Day"), ("2022-07-01", "Canada Day"), - ("2022-08-01", "Civic Holiday"), ("2022-09-05", "Labour Day"), - ("2022-10-10", "Thanksgiving"), + ("2022-09-30", "National Day for Truth and Reconciliation"), + ("2022-10-10", "Thanksgiving Day"), + ("2022-11-11", "Remembrance Day"), ("2022-12-25", "Christmas Day"), - ("2022-12-26", "Boxing Day"), + ("2022-12-26", "Boxing Day; Christmas Day (Observed)"), ("2022-12-27", "Christmas Day (Observed)"), ) @@ -406,16 +852,15 @@ def test_l10n_ar(self): "ar", ("2022-01-01", "يوم السنة الجديدة"), ("2022-01-03", "(تمت ملاحظته) يوم السنة الجديدة"), - ("2022-02-21", "يوم العائلة"), ("2022-04-15", "جمعة جيدة"), - ("2022-04-18", "عيد الفصح الاثنين"), ("2022-05-23", "يوم فيكتوريا"), ("2022-07-01", "يوم كندا"), - ("2022-08-01", "عطلة المدنية"), ("2022-09-05", "عيد العمال"), + ("2022-09-30", "اليوم الوطني للحقيقة والمصالحة"), ("2022-10-10", "عيد الشكر"), + ("2022-11-11", "يوم الذكرى"), ("2022-12-25", "عيد الميلاد"), - ("2022-12-26", "يوم الملاكمة"), + ("2022-12-26", "(تمت ملاحظته) عيد الميلاد; يوم الملاكمة"), ("2022-12-27", "(تمت ملاحظته) عيد الميلاد"), ) @@ -424,16 +869,15 @@ def test_l10n_fr(self): "fr", ("2022-01-01", "Jour de l'an"), ("2022-01-03", "Jour de l'an (Observé)"), - ("2022-02-21", "Fête de la famille"), ("2022-04-15", "Vendredi saint"), - ("2022-04-18", "Lundi de Pâques"), ("2022-05-23", "Fête de la Reine"), ("2022-07-01", "Fête du Canada"), - ("2022-08-01", "Premier lundi d'août"), ("2022-09-05", "Fête du Travail"), + ("2022-09-30", "Journée nationale de la vérité et de la réconciliation"), ("2022-10-10", "Action de grâce"), + ("2022-11-11", "Jour du Souvenir"), ("2022-12-25", "Jour de Noël"), - ("2022-12-26", "Boxing Day"), + ("2022-12-26", "Boxing Day; Jour de Noël (Observé)"), ("2022-12-27", "Jour de Noël (Observé)"), ) @@ -442,15 +886,14 @@ def test_l10n_th(self): "th", ("2022-01-01", "วันขึ้นปีใหม่"), ("2022-01-03", "ชดเชยวันขึ้นปีใหม่"), - ("2022-02-21", "วันครอบครัว"), ("2022-04-15", "วันศุกร์ประเสริฐ"), - ("2022-04-18", "วันจันทร์อีสเตอร์"), ("2022-05-23", "วันวิคตอเรีย"), ("2022-07-01", "วันชาติแคนาดา"), - ("2022-08-01", "วันหยุดราชการ"), ("2022-09-05", "วันแรงงาน"), + ("2022-09-30", "วันชาติแห่งความจริงและการปรองดอง"), ("2022-10-10", "วันขอบคุณพระเจ้า"), + ("2022-11-11", "วันรำลึก"), ("2022-12-25", "วันคริสต์มาส"), - ("2022-12-26", "วันเปิดกล่องของขวัญ"), + ("2022-12-26", "ชดเชยวันคริสต์มาส; วันเปิดกล่องของขวัญ"), ("2022-12-27", "ชดเชยวันคริสต์มาส"), ) From ce88c1bd8fb8098bf50b222df19ceb6eab81de4b Mon Sep 17 00:00:00 2001 From: ~Jhellico Date: Mon, 11 Sep 2023 18:44:18 +0300 Subject: [PATCH 08/21] Update Brazil holidays: specify optional holidays (#1452) --- holidays/countries/brazil.py | 23 +++++++++++++---------- tests/countries/test_brazil.py | 25 +++++++++++++++---------- 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/holidays/countries/brazil.py b/holidays/countries/brazil.py index c117ffbef..6768b5fa4 100644 --- a/holidays/countries/brazil.py +++ b/holidays/countries/brazil.py @@ -13,6 +13,7 @@ from datetime import date from holidays.calendars.gregorian import JAN, MAR, SEP, NOV, FRI, _get_nth_weekday_from +from holidays.constants import OPTIONAL, PUBLIC from holidays.groups import ChristianHolidays, InternationalHolidays from holidays.holiday_base import HolidayBase @@ -57,37 +58,37 @@ class Brazil(HolidayBase, ChristianHolidays, InternationalHolidays): "SP", # São Paulo "TO", # Tocantins ) + supported_categories = {OPTIONAL, PUBLIC} def __init__(self, *args, **kwargs) -> None: ChristianHolidays.__init__(self) InternationalHolidays.__init__(self) super().__init__(*args, **kwargs) - def _populate(self, year): + def _populate_public_holidays(self): # Decreto n. 155-B, de 14.01.1890 - if year <= 1889: + if self._year <= 1889: return None - super()._populate(year) # New Year's Day. self._add_new_years_day("Confraternização Universal") - if 1892 <= year <= 1930: + if 1892 <= self._year <= 1930: # Republic Constitution Day. self._add_holiday_feb_24("Constituição da Republica") # Good Friday. self._add_good_friday("Sexta-feira Santa") - if year not in {1931, 1932}: + if self._year not in {1931, 1932}: # Tiradentes' Day. self._add_holiday_apr_21("Tiradentes") - if year >= 1925: + if self._year >= 1925: # Labor Day. self._add_labor_day("Dia do Trabalhador") - if year <= 1930: + if self._year <= 1930: # Discovery of Brazil. self._add_holiday_may_3("Descobrimento do Brasil") @@ -100,7 +101,7 @@ def _populate(self, year): # Independence Day. self._add_holiday_sep_7("Independência do Brasil") - if year <= 1930 or year >= 1980: + if self._year <= 1930 or self._year >= 1980: # Our Lady of Aparecida. self._add_holiday_oct_12("Nossa Senhora Aparecida") @@ -110,11 +111,13 @@ def _populate(self, year): # Republic Proclamation Day. self._add_holiday_nov_15("Proclamação da República") - if year >= 1922: + if self._year >= 1922: # Christmas Day. self._add_christmas_day("Natal") - # Optional holidays + def _populate_optional_holidays(self): + if self._year <= 1889: + return None # Carnival. self._add_carnival_monday("Carnaval") diff --git a/tests/countries/test_brazil.py b/tests/countries/test_brazil.py index 7ac7e7625..3382e00f9 100644 --- a/tests/countries/test_brazil.py +++ b/tests/countries/test_brazil.py @@ -9,6 +9,7 @@ # Website: https://github.com/dr-prodigy/python-holidays # License: MIT (see LICENSE file) +from holidays.constants import OPTIONAL, PUBLIC from holidays.countries.brazil import Brazil, BR, BRA from tests.common import TestCase @@ -22,7 +23,7 @@ def test_country_aliases(self): self.assertCountryAliases(Brazil, BR, BRA) def test_no_holidays(self): - self.assertNoHolidays(Brazil(years=1889)) + self.assertNoHolidays(Brazil(categories=(OPTIONAL, PUBLIC), years=1889)) def test_new_years_day(self): self.assertHoliday(f"{year}-01-01" for year in range(1890, 2050)) @@ -89,8 +90,8 @@ def test_christmas_day(self): self.assertNoHolidayName("Natal", range(1890, 1922)) def test_optional_holidays(self): - self.assertHolidayName( - "Carnaval", + holidays = Brazil(categories=(OPTIONAL,)) + dt = ( "2018-02-12", "2018-02-13", "2019-03-04", @@ -102,28 +103,32 @@ def test_optional_holidays(self): "2022-02-28", "2022-03-01", ) + self.assertHolidayName("Carnaval", holidays, dt) + self.assertNoHoliday(dt) - self.assertHolidayName( - "Início da Quaresma", + dt = ( "2018-02-14", "2019-03-06", "2020-02-26", "2021-02-17", "2022-03-02", ) + self.assertHolidayName("Início da Quaresma", holidays, dt) + self.assertNoHoliday(dt) - self.assertHolidayName( - "Corpus Christi", + dt = ( "2018-05-31", "2019-06-20", "2020-06-11", "2021-06-03", "2022-06-16", ) + self.assertHolidayName("Corpus Christi", holidays, dt) + self.assertNoHoliday(dt) - self.assertHoliday(f"{year}-10-28" for year in range(1950, 2050)) - self.assertHoliday(f"{year}-12-24" for year in range(1950, 2050)) - self.assertHoliday(f"{year}-12-31" for year in range(1950, 2050)) + for year in range(1950, 2050): + self.assertHoliday(holidays, f"{year}-10-28", f"{year}-12-24", f"{year}-12-31") + self.assertNoHoliday(f"{year}-10-28", f"{year}-12-24", f"{year}-12-31") def test_AC_holidays(self): ac_holidays = Brazil(subdiv="AC", years=range(1995, 2030)) From 7ce0b50d06242548502c662cdd28fb6ea8b445e4 Mon Sep 17 00:00:00 2001 From: ~Jhellico Date: Tue, 12 Sep 2023 19:02:16 +0300 Subject: [PATCH 09/21] Update United Kingdom holidays (#1454) --- holidays/countries/united_kingdom.py | 16 +++++------ tests/countries/test_united_kingdom.py | 37 ++++++++++++++++++++------ 2 files changed, 37 insertions(+), 16 deletions(-) diff --git a/holidays/countries/united_kingdom.py b/holidays/countries/united_kingdom.py index 5d7e66a50..21c87cc80 100644 --- a/holidays/countries/united_kingdom.py +++ b/holidays/countries/united_kingdom.py @@ -16,8 +16,6 @@ from holidays.observed_holiday_base import ( ObservedHolidayBase, MON_TO_NEXT_TUE, - SAT_TO_NEXT_TUE, - SUN_TO_NEXT_MON, SAT_SUN_TO_NEXT_MON, SAT_SUN_TO_NEXT_MON_TUE, ) @@ -25,7 +23,10 @@ class UnitedKingdom(ObservedHolidayBase, ChristianHolidays, InternationalHolidays): """ - https://en.wikipedia.org/wiki/Public_holidays_in_the_United_Kingdom + References: + - https://en.wikipedia.org/wiki/Public_holidays_in_the_United_Kingdom + - https://www.gov.uk/bank-holidays + - https://www.timeanddate.com/holidays/uk/ """ country = "GB" @@ -133,7 +134,7 @@ def _add_subdiv_nir_holidays(self): self._add_easter_monday("Easter Monday") # Battle of the Boyne - self._add_holiday_jul_12("Battle of the Boyne") + self._add_observed(self._add_holiday_jul_12("Battle of the Boyne")) # Late Summer bank holiday (last Monday in August) if self._year >= 1971: @@ -141,21 +142,20 @@ def _add_subdiv_nir_holidays(self): def _add_subdiv_sct_holidays(self): # New Year's Day - jan_1 = self._add_new_years_day("New Year's Day") + self._add_observed(self._add_new_years_day("New Year's Day")) # New Year Holiday self._add_observed( self._add_new_years_day_two("New Year Holiday"), - rule=MON_TO_NEXT_TUE + SAT_SUN_TO_NEXT_MON, + rule=SAT_SUN_TO_NEXT_MON_TUE + MON_TO_NEXT_TUE, ) - self._add_observed(jan_1, rule=SAT_TO_NEXT_TUE + SUN_TO_NEXT_MON) # Summer bank holiday (first Monday in August) self._add_holiday_1st_mon_of_aug("Summer Bank Holiday") if self._year >= 2006: # St. Andrew's Day - self._add_holiday_nov_30("St. Andrew's Day") + self._add_observed(self._add_holiday_nov_30("St. Andrew's Day")) # Christmas Day self._add_observed( diff --git a/tests/countries/test_united_kingdom.py b/tests/countries/test_united_kingdom.py index 285b4c09d..e08a5709b 100644 --- a/tests/countries/test_united_kingdom.py +++ b/tests/countries/test_united_kingdom.py @@ -72,25 +72,27 @@ def test_new_year_scotland(self): name_new_year = "New Year's Day" name_new_year_holiday = "New Year Holiday" ny_obs_dt = ( - "2000-01-04", - "2005-01-04", + "2000-01-03", + "2005-01-03", "2006-01-02", - "2011-01-04", + "2011-01-03", "2012-01-02", "2017-01-02", - "2022-01-04", + "2022-01-03", + "2023-01-02", ) nyh_obs_dt = ( - "2000-01-03", - "2005-01-03", + "2000-01-04", + "2005-01-04", "2006-01-03", "2010-01-04", - "2011-01-03", + "2011-01-04", "2012-01-03", "2016-01-04", "2017-01-03", "2021-01-04", - "2022-01-03", + "2022-01-04", + "2023-01-03", ) for subdiv in ("SCT", "Scotland"): @@ -221,10 +223,20 @@ def test_spring_bank_holiday(self): def test_battle_of_the_boyne_day(self): name = "Battle of the Boyne" + obs_dt = ( + "2003-07-14", + "2008-07-14", + "2009-07-13", + "2014-07-14", + "2015-07-13", + "2020-07-13", + ) for subdiv in ("NIR", "Northern Ireland"): self.assertHolidayName( name, self.subdiv_holidays[subdiv], (f"{year}-07-12" for year in range(1950, 2050)) ) + self.assertHolidayName(f"{name} (Observed)", self.subdiv_holidays[subdiv], obs_dt) + self.assertNoNonObservedHoliday(UnitedKingdom(subdiv=subdiv, observed=False), obs_dt) for subdiv in ("ENG", "SCT", "WLS", "England", "Scotland", "Wales"): self.assertNoHoliday( @@ -305,6 +317,12 @@ def test_late_summer_bank_holiday(self): def test_st_andrews_day(self): name = "St. Andrew's Day" + obs_dt = ( + "2008-12-01", + "2013-12-02", + "2014-12-01", + "2019-12-02", + ) for subdiv in ("SCT", "Scotland"): self.assertHolidayName( name, self.subdiv_holidays[subdiv], (f"{year}-11-30" for year in range(2006, 2050)) @@ -313,6 +331,9 @@ def test_st_andrews_day(self): self.subdiv_holidays[subdiv], (f"{year}-11-30" for year in range(1950, 2006)) ) self.assertNoHolidayName(name, self.subdiv_holidays[subdiv], range(1950, 2006)) + self.assertHolidayName(f"{name} (Observed)", self.subdiv_holidays[subdiv], obs_dt) + self.assertNoNonObservedHoliday(UnitedKingdom(subdiv=subdiv, observed=False), obs_dt) + for subdiv in ( "ENG", "NIR", From f239ad5e1b0bfb76cab418ac427941f877c7e72f Mon Sep 17 00:00:00 2001 From: abh31000 Date: Tue, 12 Sep 2023 17:26:08 +0100 Subject: [PATCH 10/21] Update l10n files: fix ar, en_US headers (#1468) --- holidays/locale/ar/LC_MESSAGES/AE.po | 6 +++--- holidays/locale/ar/LC_MESSAGES/BH.po | 6 +++--- holidays/locale/ar/LC_MESSAGES/EG.po | 6 +++--- holidays/locale/ar/LC_MESSAGES/TN.po | 6 +++--- holidays/locale/en_US/LC_MESSAGES/AE.po | 6 +++--- holidays/locale/en_US/LC_MESSAGES/BH.po | 6 +++--- holidays/locale/en_US/LC_MESSAGES/EG.po | 6 +++--- holidays/locale/en_US/LC_MESSAGES/TN.po | 6 +++--- 8 files changed, 24 insertions(+), 24 deletions(-) diff --git a/holidays/locale/ar/LC_MESSAGES/AE.po b/holidays/locale/ar/LC_MESSAGES/AE.po index 79ea54ba7..ae77928bb 100644 --- a/holidays/locale/ar/LC_MESSAGES/AE.po +++ b/holidays/locale/ar/LC_MESSAGES/AE.po @@ -5,9 +5,9 @@ msgid "" msgstr "" "Project-Id-Version: Python Holidays 0.29\n" "POT-Creation-Date: 2023-06-28 00:13+0100\n" -"PO-Revision-Date: 2023-07-05 17:07+0100\n" -"Last-Translator: \n" -"Language-Team: Abdelkhalek Boukli Hacene \n" +"PO-Revision-Date: 2023-09-12 15:54+0100\n" +"Last-Translator: Abdelkhalek Boukli Hacene \n" +"Language-Team: Python Holidays localization team\n" "Language: ar\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" diff --git a/holidays/locale/ar/LC_MESSAGES/BH.po b/holidays/locale/ar/LC_MESSAGES/BH.po index 7a93c4dce..18b037bbe 100644 --- a/holidays/locale/ar/LC_MESSAGES/BH.po +++ b/holidays/locale/ar/LC_MESSAGES/BH.po @@ -5,9 +5,9 @@ msgid "" msgstr "" "Project-Id-Version: Python Holidays 0.29\n" "POT-Creation-Date: 2023-06-28 00:13+0100\n" -"PO-Revision-Date: 2023-07-05 17:40+0100\n" -"Last-Translator: \n" -"Language-Team: Abdelkhalek Boukli Hacene \n" +"PO-Revision-Date: 2023-09-12 15:52+0100\n" +"Last-Translator: Abdelkhalek Boukli Hacene \n" +"Language-Team: Python Holidays localization team\n" "Language: ar\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" diff --git a/holidays/locale/ar/LC_MESSAGES/EG.po b/holidays/locale/ar/LC_MESSAGES/EG.po index 4f0b2a020..a930434b5 100644 --- a/holidays/locale/ar/LC_MESSAGES/EG.po +++ b/holidays/locale/ar/LC_MESSAGES/EG.po @@ -5,9 +5,9 @@ msgid "" msgstr "" "Project-Id-Version: Python Holidays 0.29\n" "POT-Creation-Date: 2023-06-28 00:13+0100\n" -"PO-Revision-Date: 2023-07-11 02:08+0100\n" -"Last-Translator: \n" -"Language-Team: Abdelkhalek Boukli Hacene \n" +"PO-Revision-Date: 2023-09-12 15:50+0100\n" +"Last-Translator: Abdelkhalek Boukli Hacene \n" +"Language-Team: Python Holidays localization team\n" "Language: ar\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" diff --git a/holidays/locale/ar/LC_MESSAGES/TN.po b/holidays/locale/ar/LC_MESSAGES/TN.po index 6e8238ff9..2619e6de2 100644 --- a/holidays/locale/ar/LC_MESSAGES/TN.po +++ b/holidays/locale/ar/LC_MESSAGES/TN.po @@ -5,9 +5,9 @@ msgid "" msgstr "" "Project-Id-Version: Python Holidays 0.29\n" "POT-Creation-Date: 2023-06-28 00:13+0100\n" -"PO-Revision-Date: 2023-07-05 17:41+0100\n" -"Last-Translator: \n" -"Language-Team: Abdelkhalek Boukli Hacene \n" +"PO-Revision-Date: 2023-09-12 15:48+0100\n" +"Last-Translator: Abdelkhalek Boukli Hacene \n" +"Language-Team: Python Holidays localization team\n" "Language: ar\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" diff --git a/holidays/locale/en_US/LC_MESSAGES/AE.po b/holidays/locale/en_US/LC_MESSAGES/AE.po index 3c430f2ad..3e28ebed2 100644 --- a/holidays/locale/en_US/LC_MESSAGES/AE.po +++ b/holidays/locale/en_US/LC_MESSAGES/AE.po @@ -5,9 +5,9 @@ msgid "" msgstr "" "Project-Id-Version: Python Holidays 0.29\n" "POT-Creation-Date: 2023-06-28 00:13+0100\n" -"PO-Revision-Date: 2023-07-11 03:42+0100\n" -"Last-Translator: \n" -"Language-Team: Abdelkhalek Boukli Hacene \n" +"PO-Revision-Date: 2023-09-12 15:59+0100\n" +"Last-Translator: Abdelkhalek Boukli Hacene \n" +"Language-Team: Python Holidays localization team\n" "Language: en_US\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" diff --git a/holidays/locale/en_US/LC_MESSAGES/BH.po b/holidays/locale/en_US/LC_MESSAGES/BH.po index 8659d68c3..98dbb8deb 100644 --- a/holidays/locale/en_US/LC_MESSAGES/BH.po +++ b/holidays/locale/en_US/LC_MESSAGES/BH.po @@ -5,9 +5,9 @@ msgid "" msgstr "" "Project-Id-Version: Python Holidays 0.29\n" "POT-Creation-Date: 2023-06-28 00:13+0100\n" -"PO-Revision-Date: 2023-07-11 03:43+0100\n" -"Last-Translator: \n" -"Language-Team: Abdelkhalek Boukli Hacene \n" +"PO-Revision-Date: 2023-09-12 16:02+0100\n" +"Last-Translator: Abdelkhalek Boukli Hacene \n" +"Language-Team: Python Holidays localization team\n" "Language: en_US\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" diff --git a/holidays/locale/en_US/LC_MESSAGES/EG.po b/holidays/locale/en_US/LC_MESSAGES/EG.po index 3b7ef2b42..5ece5ad09 100644 --- a/holidays/locale/en_US/LC_MESSAGES/EG.po +++ b/holidays/locale/en_US/LC_MESSAGES/EG.po @@ -5,9 +5,9 @@ msgid "" msgstr "" "Project-Id-Version: Python Holidays 0.29\n" "POT-Creation-Date: 2023-06-28 00:13+0100\n" -"PO-Revision-Date: 2023-07-11 02:08+0100\n" -"Last-Translator: \n" -"Language-Team: Abdelkhalek Boukli Hacene \n" +"PO-Revision-Date: 2023-09-12 16:05+0100\n" +"Last-Translator: Abdelkhalek Boukli Hacene \n" +"Language-Team: Python Holidays localization team\n" "Language: en_US\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" diff --git a/holidays/locale/en_US/LC_MESSAGES/TN.po b/holidays/locale/en_US/LC_MESSAGES/TN.po index 7da068c10..df784f678 100644 --- a/holidays/locale/en_US/LC_MESSAGES/TN.po +++ b/holidays/locale/en_US/LC_MESSAGES/TN.po @@ -5,9 +5,9 @@ msgid "" msgstr "" "Project-Id-Version: Python Holidays 0.29\n" "POT-Creation-Date: 2023-06-28 00:13+0100\n" -"PO-Revision-Date: 2023-07-11 03:44+0100\n" -"Last-Translator: \n" -"Language-Team: Abdelkhalek Boukli Hacene \n" +"PO-Revision-Date: 2023-09-12 16:08+0100\n" +"Last-Translator: Abdelkhalek Boukli Hacene \n" +"Language-Team: Python Holidays localization team\n" "Language: en_US\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" From 56f773288e3c85f74f6d6749ee7b2f63e4aee476 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20van=20Amerongen?= Date: Tue, 12 Sep 2023 18:33:35 +0200 Subject: [PATCH 11/21] Update examples.rst (#1456) Co-authored-by: ~Jhellico Co-authored-by: Arkadii Yakovets --- docs/source/examples.rst | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docs/source/examples.rst b/docs/source/examples.rst index 44284c6ca..6f31393f7 100644 --- a/docs/source/examples.rst +++ b/docs/source/examples.rst @@ -123,6 +123,25 @@ fly and the holiday list will be adjusted accordingly: >> date(2012, 1, 2) in us_holidays True +Language support +---------------- +To change the language translation, you can set the language explicitly. + +.. code-block:: python + + >>> for dt, name in sorted(holidays.ES(years=2023, language="es").items()): + >>> print(dt, name) + 2023-01-06 Epifanía del Señor + 2023-04-06 Jueves Santo + 2023-04-07 Viernes Santo + 2023-05-01 Día del Trabajador + 2023-08-15 Asunción de la Virgen + 2023-10-12 Día de la Hispanidad + 2023-11-01 Todos los Santos + 2023-12-06 Día de la Constitución Española + 2023-12-08 La Inmaculada Concepción + 2023-12-25 Navidad + Date from holiday name ---------------------- From 06bc39f0ea66303c537a04c2e57ad184ab44d486 Mon Sep 17 00:00:00 2001 From: PPsyrius <19505219+PPsyrius@users.noreply.github.com> Date: Wed, 13 Sep 2023 00:03:35 +0700 Subject: [PATCH 12/21] Update Thailand holidays: add holiday categories (#1346) Co-authored-by: ~Jhellico Co-authored-by: ~Jhellico Co-authored-by: Arkadii Yakovets --- holidays/constants.py | 2 + holidays/countries/thailand.py | 254 +++++++++++++++++++----- holidays/locale/en_US/LC_MESSAGES/TH.po | 63 +++++- holidays/locale/th/LC_MESSAGES/TH.po | 61 +++++- tests/countries/test_thailand.py | 235 ++++++++++++++++++++-- 5 files changed, 543 insertions(+), 72 deletions(-) diff --git a/holidays/constants.py b/holidays/constants.py index 130648eca..d32ed101d 100644 --- a/holidays/constants.py +++ b/holidays/constants.py @@ -37,6 +37,7 @@ HOLIDAY_NAME_DELIMITER = "; " # Holiday names separator. # Supported holiday categories. +ARMED_FORCES = "armed_forces" BANK = "bank" GOVERNMENT = "government" HALF_DAY = "half_day" @@ -52,6 +53,7 @@ ISLAMIC = "islamic" ALL_CATEGORIES = { + ARMED_FORCES, BANK, CHINESE, CHRISTIAN, diff --git a/holidays/countries/thailand.py b/holidays/countries/thailand.py index 77d6bca1b..9ef2f7193 100644 --- a/holidays/countries/thailand.py +++ b/holidays/countries/thailand.py @@ -13,6 +13,7 @@ from gettext import gettext as tr from holidays.calendars.gregorian import JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC +from holidays.constants import ARMED_FORCES, BANK, GOVERNMENT, PUBLIC, SCHOOL, WORKDAY from holidays.groups import InternationalHolidays, ThaiCalendarHolidays from holidays.observed_holiday_base import ( ObservedHolidayBase, @@ -40,6 +41,8 @@ class Thailand(ObservedHolidayBase, InternationalHolidays, ThaiCalendarHolidays) https://github.com/dr-prodigy/python-holidays/pull/929 - [New Year's Day] (wikisource.org 's wbm) http://tiny.cc/wa_wiki_thai_newyear_2483 + - [National Children's Day] + https://thainews.prd.go.th/banner/th/children'sday/ - [Chakri Memorial Day] (ocac.got.th 's wbm) http://tiny.cc/wa_ocac_chakri - [Songkran Festival] @@ -89,15 +92,17 @@ class Thailand(ObservedHolidayBase, InternationalHolidays, ThaiCalendarHolidays) https://en.wikipedia.org/wiki/Royal_Ploughing_Ceremony https://www.lib.ru.ac.th/journal/may/may_phauchmongkol.html https://www.myhora.com/ปฏิทิน/ปฏิทิน-พ.ศ.2540.aspx + - [Royal Thai Armed Forces Day] + https://th.wikipedia.org/wiki/วันกองทัพไทย + - [Teacher's Day] + https://www.cabinet.soc.go.th/doc_image/2500/718941.pdf !!! If Public Holiday falls on weekends, (in lieu) on workday !!! Despite the wording, this usually only applies to Monday only for holidays, consecutive holidays all have their own special in lieu declared separately. - Data from 1992-1994 and 1998-2000 are declared discretely in special_holidays declarations above. - Applied Automatically for Monday if on Weekends: 1961-1973 **NOTE: No New Year's Eve (in lieu) for this period No In Lieu days available: 1974-1988 @@ -131,9 +136,11 @@ class Thailand(ObservedHolidayBase, InternationalHolidays, ThaiCalendarHolidays) """ country = "TH" + supported_categories = {ARMED_FORCES, BANK, GOVERNMENT, PUBLIC, SCHOOL, WORKDAY} default_language = "th" # %s (in lieu). observed_label = tr("ชดเชย%s") + supported_languages = ("en_US", "th") # วันหยุดพิเศษ (เพิ่มเติม) - see Bank of Thailand's DB for Cross-Check. @@ -171,7 +178,7 @@ class Thailand(ObservedHolidayBase, InternationalHolidays, ThaiCalendarHolidays) # Songkran Festival (in lieu). songkran_festival_in_lieu_covid = tr("ชดเชยวันสงกรานต์") - special_holidays = { + special_public_holidays = { # 1992-1994 (include In Lieus, Checked with Bank of Thailand Data). 1992: ( (MAY, 18, thai_special_in_lieu_holidays), @@ -277,7 +284,6 @@ class Thailand(ObservedHolidayBase, InternationalHolidays, ThaiCalendarHolidays) (JUL, 31, thai_bridge_public_holiday), ), } - supported_languages = ("en_US", "th") def __init__(self, *args, **kwargs): InternationalHolidays.__init__(self) @@ -287,14 +293,12 @@ def __init__(self, *args, **kwargs): def _is_observed(self, dt: date) -> bool: return 1961 <= self._year <= 1973 or 1995 <= self._year <= 1997 or self._year >= 2001 - def _populate(self, year): + def _populate_public_holidays(self): # Due to Thai Calendar Migration, this is capped off at 1941. # But certain holidays were implemented before 1941. - if year <= 1940: + if self._year <= 1940: return None - super()._populate(year) - # Fixed Date Holidays # วันขึ้นปีใหม่ @@ -305,6 +309,21 @@ def _populate(self, year): # New Year's Day. self._add_observed(self._add_new_years_day(tr("วันขึ้นปีใหม่"))) + # วันเด็กแห่งชาติ + # Status: In-Use. + # Starts in 1955 as the 1st Monday of October. + # No event was held in 1964 due to date changes came into effect too late. + # Moved to 2nd Saturday of January since 1965. + # No in-lieus are observed, and still remain a Public Holidays than just Observed. + + if self._year >= 1955 and self._year != 1964: + # National Children's Day + childrens_day = tr("วันเด็กแห่งชาติ") + if self._year <= 1963: + self._add_holiday_1st_mon_of_oct(childrens_day) + else: + self._add_holiday_2nd_sat_of_jan(childrens_day) + # วันจักรี # Status: In-Use. # Starts in present form in 1918 (B.E. 2461). @@ -326,12 +345,12 @@ def _populate(self, year): # (Except for 2020 due to Covid-19 outbreaks) # This has its own in-lieu trigger. - if 1948 <= year <= 1953 or (1957 <= year != 2020): + if 1948 <= self._year <= 1953 or 1957 <= self._year != 2020: # Songkran Festival. songkran_festival = tr("วันสงกรานต์") - if 1957 <= year <= 1988: + if 1957 <= self._year <= 1988: self._add_observed(self._add_holiday_apr_13(songkran_festival)) - elif 1989 <= year <= 1997: + elif 1989 <= self._year <= 1997: dt = self._add_holiday_apr_12(songkran_festival) self._add_holiday_apr_13(songkran_festival) self._add_holiday_apr_14(songkran_festival) @@ -348,7 +367,8 @@ def _populate(self, year): # - CASE 3: SAT-SUN-MON -> 1 in-lieu on TUE # See in lieu logic in `_add_observed(dt: date)`. # Status: In Use. - if year >= 1995: + + if self._year >= 1995: self._add_observed(dt, rule=THU_FRI_TO_NEXT_MON + SAT_TO_NEXT_TUE) # วันแรงงานแห่งชาติ @@ -357,7 +377,7 @@ def _populate(self, year): # Does existed officially since 1956 (B.E. 2499), but wasn't a public holiday until then. # *** NOTE: only observed by financial and private sectors. - if year >= 1974: + if self._year >= 1974: # National Labour day. self._add_observed(self._add_labor_day(tr("วันแรงงานแห่งชาติ"))) @@ -368,9 +388,11 @@ def _populate(self, year): # TODO: Add check for 1939 if we support earlier dates. # National Day. - name = tr("วันชาติ") + national_day = tr("วันชาติ") self._add_observed( - self._add_holiday_jun_24(name) if year <= 1959 else self._add_holiday_dec_5(name) + self._add_holiday_jun_24(national_day) + if self._year <= 1959 + else self._add_holiday_dec_5(national_day) ) # วันฉัตรมงคล @@ -381,19 +403,19 @@ def _populate(self, year): # Coronation Day. coronation_day = tr("วันฉัตรมงคล") - if 1958 <= year <= 2016: + if 1958 <= self._year <= 2016: self._add_observed(self._add_holiday_may_5(coronation_day)) - elif year >= 2020: + elif self._year >= 2020: self._add_observed(self._add_holiday_may_4(coronation_day)) # วันเฉลิมพระชนมพรรษา พระราชินี # Status: In-Use. # Starts in 2019 (B.E. 2562). - if year >= 2019: + if self._year >= 2019: self._add_observed( - # HM Queen Suthida's Birthday. self._add_holiday_jun_3( + # HM Queen Suthida's Birthday. tr("วันเฉลิมพระชนมพรรษาสมเด็จพระนางเจ้าสุทิดา พัชรสุธาพิมลลักษณ พระบรมราชินี") ) ) @@ -402,7 +424,7 @@ def _populate(self, year): # Status: In-Use. # Started in 2017 (B.E 2560). - if year >= 2017: + if self._year >= 2017: self._add_observed( self._add_holiday_jul_28( # HM King Maha Vajiralongkorn's Birthday. @@ -420,15 +442,15 @@ def _populate(self, year): # Initial celebration as HM Queen Sirikit's Birthday. # Now acts as the Queen Mother from 2017 onwards. - if year >= 1976: - name = ( + if self._year >= 1976: + q_sirikit_bday = ( # HM Queen Sirikit the Queen Mother's Birthday. tr("วันเฉลิมพระชนมพรรษาสมเด็จพระบรมราชชนนีพันปีหลวง") - if year >= 2017 + if self._year >= 2017 # HM Queen Sirikit's Birthday. else tr("วันเฉลิมพระชนมพรรษาสมเด็จพระนางเจ้าสิริกิติ์ พระบรมราชินีนาถ") ) - self._add_observed(self._add_holiday_aug_12(name)) + self._add_observed(self._add_holiday_aug_12(q_sirikit_bday)) # วันแม่แห่งชาติ # Status: In-Use. @@ -440,9 +462,9 @@ def _populate(self, year): # National Mother's Day. thai_mothers_day = tr("วันแม่แห่งชาติ") - if 1950 <= year <= 1957: + if 1950 <= self._year <= 1957: self._add_observed(self._add_holiday_apr_15(thai_mothers_day)) - elif year >= 1976: + elif self._year >= 1976: self._add_observed(self._add_holiday_aug_12(thai_mothers_day)) # วันคล้ายวันสวรรคตพระบาทสมเด็จพระปรมินทร มหาภูมิพลอดุลยเดช บรมนาถบพิตร @@ -450,18 +472,18 @@ def _populate(self, year): # Started in 2017 (B.E 2560). # Got conferred with 'the Great' title in 2019 (B.E. 2562). - if year >= 2017: - name = ( + if self._year >= 2017: + k_bhumibol_memorial = ( # Anniversary for the Death of King Bhumibol Adulyadej the Great. tr( "วันคล้ายวันสวรรคตพระบาทสมเด็จพระบรมชนกาธิเบศร " "มหาภูมิพลอดุลยเดชมหาราช บรมนาถบพิตร" ) - if year >= 2019 + if self._year >= 2019 # Anniversary for the Death of King Bhumibol Adulyadej. else tr("วันคล้ายวันสวรรคตพระบาทสมเด็จพระปรมินทรมหาภูมิพลอดุลยเดช บรมนาถบพิตร") ) - self._add_observed(self._add_holiday_oct_13(name)) + self._add_observed(self._add_holiday_oct_13(k_bhumibol_memorial)) # วันปิยมหาราช # Status: In-Use. @@ -478,28 +500,28 @@ def _populate(self, year): # Confirmed as still in-use in 2017. # Got conferred with 'the Great' title in 2019 (B.E. 2562). - if year >= 1960: - name = ( + if self._year >= 1960: + k_bhumibol_bday = ( # HM King Bhumibol Adulyadej's the Great's Birthday Anniversary. tr( "วันคล้ายวันเฉลิมพระชนมพรรษาพระบาทสมเด็จพระบรม" "ชนกาธิเบศร มหาภูมิพลอดุลยเดชมหาราช บรมนาถบพิตร" ) - if year >= 2019 + if self._year >= 2019 else ( # HM King Bhumibol Adulyadej Birthday Anniversary. tr( "วันคล้ายวันเฉลิมพระชนมพรรษา" "พระบาทสมเด็จพระปรมินทรมหาภูมิพลอดุลยเดช บรมนาถบพิตร" ) - if year >= 2016 + if self._year >= 2016 # HM King Bhumibol Adulyadej Birthday Anniversary. else tr( "วันเฉลิมพระชนมพรรษาพระบาทสมเด็จพระปรมินทรมหาภูมิพลอดุลยเดช บรมนาถบพิตร" ) ) ) - self._add_observed(self._add_holiday_dec_5(name)) + self._add_observed(self._add_holiday_dec_5(k_bhumibol_bday)) # วันพ่อแห่งชาติ # Status: In-Use. @@ -507,7 +529,7 @@ def _populate(self, year): # Technically, a replication of HM King Bhumibol Adulyadej's Birthday # but it's in the official calendar, so may as well have this here. - if year >= 1980: + if self._year >= 1980: # National Father's Day. self._add_observed(self._add_holiday_dec_5(tr("วันพ่อแห่งชาติ"))) @@ -539,8 +561,8 @@ def _populate(self, year): # - CASE 2: SUN-MON -> 1 in-lieu on TUE. # See in lieu logic in `_add_observed(dt: date)`. - if year >= 1995: - self._add_observed(date(year - 1, DEC, 31), name=name, rule=SAT_SUN_TO_NEXT_TUE) + if self._year >= 1995: + self._add_observed(date(self._year - 1, DEC, 31), name=name, rule=SAT_SUN_TO_NEXT_TUE) # Thai Lunar Calendar Holidays # See `_ThaiLunisolar` in holidays/utils.py for more details. @@ -564,8 +586,6 @@ def _populate(self, year): # วันอาสาฬหบูชา # Status: In-Use. - # วันหยุดชดเชยวันอาสาฬหบูชา - # วันหยุดชดเชยวันเข้าพรรษา # - CASE 1: FRI-SAT -> 1 in-lieu on MON # - CASE 2: SAT-SUN -> 1 in-lieu on MON # - CASE 3: SUN-MON -> 1 in-lieu on TUE @@ -577,26 +597,78 @@ def _populate(self, year): # วันเข้าพรรษา # Status: In-Use. + # - CASE 1: FRI-SAT -> 1 in-lieu on MON + # - CASE 2: SAT-SUN -> 1 in-lieu on MON + # - CASE 3: SUN-MON -> 1 in-lieu on TUE # Buddhist Lent Day. khao_phansa_date = self._add_khao_phansa(tr("วันเข้าพรรษา")) if khao_phansa_date: self._add_observed(khao_phansa_date, rule=SAT_TO_NEXT_MON) + def _populate_armed_forces_holidays(self): + # วันกองทัพไทย + # Status: In-Use. + # First started in 1959 on the foundation of Ministry of Defense Day (APR 8). + # Moved to JAN 25 (Supposedly King Naresuan's Decisive Battle) in 1980. + # Corrected to the battle's actual date (JAN 18) in 2007. + # Only applys to members of the Royal Thai Armed Forces. + + if self._year >= 1959: + # Royal Thai Armed Forces Day + armed_forces_day = tr("วันกองทัพไทย") + if self._year >= 2007: + self._add_holiday_jan_18(armed_forces_day) + elif self._year >= 1980: + self._add_holiday_jan_25(armed_forces_day) + else: + self._add_holiday_apr_8(armed_forces_day) + + def _populate_bank_holidays(self): + # Bank of Thailand, the ones who decreed this wasn't found until December 10, 1942 + # So it's safe to assume with that as our start date. + if self._year <= 1942: + return None + + # Bank Holidays + + # วันหยุดเพิ่มเติมสำหรับการปิดบัญชีประจำปีของธนาคารเพื่อการเกษตรและสหกรณ์การเกษตร + # Status: Defunct. + # If held on the weekends, no in-lieus. + # Abandoned in 2022. + + if self._year <= 2021: + self._add_holiday_apr_1( + # Additional Closing Day for Bank for Agriculture and Agricultural Cooperatives + tr( + "วันหยุดเพิ่มเติมสำหรับการปิดบัญชีประจำปีของ" + "ธนาคารเพื่อการเกษตรและสหกรณ์การเกษตร" + ), + ) + + # วันหยุดภาคครึ่งปีของสถาบันการเงินและสถาบันการเงินเฉพาะกิจ + # Status: Defunct. + # If held on the weekends, no in-lieus. + # Abandoned in 2019. + + if self._year <= 2018: + self._add_holiday_jul_1( + # Mid-Year Closing Day + tr("วันหยุดภาคครึ่งปีของสถาบันการเงินและสถาบันการเงินเฉพาะกิจ"), + ) + + def _populate_government_holidays(self): # No Future Fixed Date Holidays # วันพืชมงคล # Restarts in 1957 (B.E. 2500). - # Is dated on an annual basis by the Royal Palace. + # Is dated on an annual basis by the Royal Palace, always on weekdays. # This isn't even fixed even by the Thai Lunar Calendar, but instead # by Court Astrologers; All chosen dates are all around May, so we # can technically assign it to 13 May for years prior with no data. # *** NOTE: only observed by government sectors. # TODO: Update this annually around Dec of each year. - # Royal Ploughing Ceremony. - raeknakhwan = tr("วันพืชมงคล") - raeknakhwan_dates = { 1997: (MAY, 13), 1998: (MAY, 13), @@ -626,12 +698,94 @@ def _populate(self, year): 2022: (MAY, 17), 2023: (MAY, 11), } - # For years with exact date data. - if year in raeknakhwan_dates: - self._add_observed(self._add_holiday(raeknakhwan, raeknakhwan_dates[year])) - # Approx. otherwise for 1957-2013. - elif 1957 <= year <= 1996: - self._add_observed(self._add_holiday_may_13(raeknakhwan)) + if 1957 <= self._year <= 2023 and self._year != 1999: + self._add_observed( + # Royal Ploughing Ceremony. + self._add_holiday(tr("วันพืชมงคล"), raeknakhwan_dates.get(self._year, (MAY, 13))) + ) + + def _populate_school_holidays(self): + # วันครู + # Status: In-Use. + # Started in 1957. + # Only applies to Ministry of Education (Students, Teachers, etc.), no in-lieus are given. + + if self._year >= 1957: + # Teacher's Day + self._add_holiday_jan_16(tr("วันครู")) + + def _populate_workday_holidays(self): + # These are classes as "วันสำคัญ" (Date of National Observance) by the government + # but are not holidays. + + if self._year >= 1948: + # วันทหารผ่านศึก + # Status: In-Use. + # Started in 1948. + + # Thai Veterans Day + self._add_holiday_feb_3(tr("วันทหารผ่านศึก")) + + if self._year >= 1982: + # วันวิทยาศาสตร์แห่งชาติ + # Status: In-Use. + # Started in 1982. + + # National Science Day + self._add_holiday_aug_18(tr("วันวิทยาศาสตร์แห่งชาติ")) + + if self._year >= 1985: + # วันศิลปินแห่งชาติ + # Status: In-Use. + # Started in 1985. + + # National Artist Day + self._add_holiday_feb_26(tr("วันศิลปินแห่งชาติ")) + + if self._year >= 1989: + # วันสตรีสากล + # Status: In-Use. + # Started in 1989. + + # International Women's Day + self._add_womens_day(tr("วันสตรีสากล")) + + if self._year >= 1990: + # วันอนุรักษ์ทรัพยากรป่าไม้ของชาติ + # Status: In-Use. + # Started in 1990. + + # National Forest Conservation Day + self._add_holiday_jan_14(tr("วันอนุรักษ์ทรัพยากรป่าไม้ของชาติ")) + + # วันพ่อขุนรามคำแหงมหาราช + # Status: In-Use. + # Started in 1990. + + # HM King Ramkamhaeng Memorial Day + self._add_holiday_jan_17(tr("วันพ่อขุนรามคำแหงมหาราช")) + + if self._year >= 1995: + # วันการบินแห่งชาติ + # Status: In-Use. + # Started in 1995. + + # National Aviation Day + self._add_holiday_jan_13(tr("วันการบินแห่งชาติ")) + + if self._year >= 2017: + # วันพระราชทานธงชาติไทย + # Status: In-Use. + # Started in 2017. + + # Thai National Flag Day + self._add_holiday_sep_28(tr("วันพระราชทานธงชาติไทย")) + + # วันลอยกระทง + # Status: In-Use. + + # Loy Krathong + self._add_loy_krathong(tr("วันลอยกระทง")) class TH(Thailand): diff --git a/holidays/locale/en_US/LC_MESSAGES/TH.po b/holidays/locale/en_US/LC_MESSAGES/TH.po index f6aeda739..fd244b5b3 100644 --- a/holidays/locale/en_US/LC_MESSAGES/TH.po +++ b/holidays/locale/en_US/LC_MESSAGES/TH.po @@ -3,7 +3,7 @@ # msgid "" msgstr "" -"Project-Id-Version: Python Holidays 0.20\n" +"Project-Id-Version: Python Holidays 0.33\n" "POT-Creation-Date: 2023-03-18 15:58-0700\n" "PO-Revision-Date: \n" "Last-Translator: PPsyrius \n" @@ -80,6 +80,10 @@ msgstr "New Year's Day" msgid "วันสิ้นปี" msgstr "New Year's Eve" +#. National Children's Day +msgid "วันเด็กแห่งชาติ" +msgstr "National Children's Day" + #. Chakri Memorial Day. msgid "วันจักรี" msgstr "Chakri Memorial Day" @@ -100,6 +104,7 @@ msgstr "National Day" msgid "วันฉัตรมงคล" msgstr "Coronation Day" +#. HM Queen Suthida's Birthday. msgid "" "วันเฉลิมพระชนมพรรษาสมเด็จพระนางเจ้าสุทิดา พัชรสุธาพิมลลักษณ พระบรมราชินี" msgstr "HM Queen Suthida's Birthday" @@ -176,6 +181,62 @@ msgstr "Asarnha Bucha" msgid "วันเข้าพรรษา" msgstr "Buddhist Lent Day" +#. Royal Thai Armed Forces Day +msgid "วันกองทัพไทย" +msgstr "Royal Thai Armed Forces Day" + +#. Additional Closing Day for Bank for Agriculture and Agricultural +#. Cooperatives +msgid "" +"วันหยุดเพิ่มเติมสำหรับการปิดบัญชีประจำปีของธนาคารเพื่อการเกษตรและสหกรณ์การเกษตร" +msgstr "" +"Additional Closing Day for Bank for Agriculture and Agricultural " +"Cooperatives" + +#. Mid-Year Closing Day +msgid "วันหยุดภาคครึ่งปีของสถาบันการเงินและสถาบันการเงินเฉพาะกิจ" +msgstr "Mid-Year Closing Day" + #. Royal Ploughing Ceremony. msgid "วันพืชมงคล" msgstr "Royal Ploughing Ceremony" + +#. Teacher's Day +msgid "วันครู" +msgstr "Teacher's Day" + +#. National Aviation Day +msgid "วันการบินแห่งชาติ" +msgstr "National Aviation Day" + +#. National Forest Conservation Day +msgid "วันอนุรักษ์ทรัพยากรป่าไม้ของชาติ" +msgstr "National Forest Conservation Day" + +#. National Artist Day +msgid "วันศิลปินแห่งชาติ" +msgstr "National Artist Day" + +#. International Women's Day +msgid "วันสตรีสากล" +msgstr "International Women's Day" + +#. Loy Krathong +msgid "วันลอยกระทง" +msgstr "Loy Krathong" + +#. Thai Veterans Day +msgid "วันทหารผ่านศึก" +msgstr "Thai Veterans Day" + +#. National Science Day +msgid "วันวิทยาศาสตร์แห่งชาติ" +msgstr "National Science Day" + +#. HM King Ramkamhaeng Memorial Day +msgid "วันพ่อขุนรามคำแหงมหาราช" +msgstr "HM King Ramkamhaeng Memorial Day" + +#. Thai National Flag Day +msgid "วันพระราชทานธงชาติไทย" +msgstr "Thai National Flag Day" diff --git a/holidays/locale/th/LC_MESSAGES/TH.po b/holidays/locale/th/LC_MESSAGES/TH.po index 047329385..c7c0b7956 100644 --- a/holidays/locale/th/LC_MESSAGES/TH.po +++ b/holidays/locale/th/LC_MESSAGES/TH.po @@ -3,7 +3,7 @@ # msgid "" msgstr "" -"Project-Id-Version: Python Holidays 0.24\n" +"Project-Id-Version: Python Holidays 0.33\n" "POT-Creation-Date: 2023-03-02 00:37+0700\n" "PO-Revision-Date: \n" "Last-Translator: PPsyrius \n" @@ -80,6 +80,10 @@ msgstr "" msgid "วันสิ้นปี" msgstr "" +#. National Children's Day +msgid "วันเด็กแห่งชาติ" +msgstr "" + #. Chakri Memorial Day. msgid "วันจักรี" msgstr "" @@ -100,6 +104,7 @@ msgstr "" msgid "วันฉัตรมงคล" msgstr "" +#. HM Queen Suthida's Birthday. msgid "" "วันเฉลิมพระชนมพรรษาสมเด็จพระนางเจ้าสุทิดา พัชรสุธาพิมลลักษณ พระบรมราชินี" msgstr "" @@ -176,6 +181,60 @@ msgstr "" msgid "วันเข้าพรรษา" msgstr "" +#. Royal Thai Armed Forces Day +msgid "วันกองทัพไทย" +msgstr "" + +#. Additional Closing Day for Bank for Agriculture and Agricultural +#. Cooperatives +msgid "" +"วันหยุดเพิ่มเติมสำหรับการปิดบัญชีประจำปีของธนาคารเพื่อการเกษตรและสหกรณ์การเกษตร" +msgstr "" + +#. Mid-Year Closing Day +msgid "วันหยุดภาคครึ่งปีของสถาบันการเงินและสถาบันการเงินเฉพาะกิจ" +msgstr "" + #. Royal Ploughing Ceremony. msgid "วันพืชมงคล" msgstr "" + +#. Teacher's Day +msgid "วันครู" +msgstr "" + +#. National Aviation Day +msgid "วันการบินแห่งชาติ" +msgstr "" + +#. National Forest Conservation Day +msgid "วันอนุรักษ์ทรัพยากรป่าไม้ของชาติ" +msgstr "" + +#. National Artist Day +msgid "วันศิลปินแห่งชาติ" +msgstr "" + +#. International Women's Day +msgid "วันสตรีสากล" +msgstr "" + +#. Loy Krathong +msgid "วันลอยกระทง" +msgstr "" + +#. Thai Veterans Day +msgid "วันทหารผ่านศึก" +msgstr "" + +#. National Science Day +msgid "วันวิทยาศาสตร์แห่งชาติ" +msgstr "" + +#. HM King Ramkamhaeng Memorial Day +msgid "วันพ่อขุนรามคำแหงมหาราช" +msgstr "" + +#. Thai National Flag Day +msgid "วันพระราชทานธงชาติไทย" +msgstr "" diff --git a/tests/countries/test_thailand.py b/tests/countries/test_thailand.py index 3921554a2..ebe28e5bd 100644 --- a/tests/countries/test_thailand.py +++ b/tests/countries/test_thailand.py @@ -9,6 +9,7 @@ # Website: https://github.com/dr-prodigy/python-holidays # License: MIT (see LICENSE file) +from holidays.constants import ARMED_FORCES, BANK, GOVERNMENT, PUBLIC, SCHOOL, WORKDAY from holidays.countries.thailand import Thailand, TH, THA from tests.common import TestCase @@ -100,12 +101,21 @@ def test_special_holidays(self): "2023-07-31", ) - def test_2022(self): + def test_2022_all(self): self.assertHolidays( - Thailand(years=2022), + Thailand(categories=(ARMED_FORCES, GOVERNMENT, PUBLIC, SCHOOL, WORKDAY), years=2022), ("2022-01-01", "วันขึ้นปีใหม่"), ("2022-01-03", "ชดเชยวันขึ้นปีใหม่"), + ("2022-01-08", "วันเด็กแห่งชาติ"), + ("2022-01-13", "วันการบินแห่งชาติ"), + ("2022-01-14", "วันอนุรักษ์ทรัพยากรป่าไม้ของชาติ"), + ("2022-01-16", "วันครู"), + ("2022-01-17", "วันพ่อขุนรามคำแหงมหาราช"), + ("2022-01-18", "วันกองทัพไทย"), + ("2022-02-03", "วันทหารผ่านศึก"), ("2022-02-16", "วันมาฆบูชา"), + ("2022-02-26", "วันศิลปินแห่งชาติ"), + ("2022-03-08", "วันสตรีสากล"), ("2022-04-06", "วันจักรี"), ("2022-04-13", "วันสงกรานต์"), ("2022-04-14", "วันสงกรานต์"), @@ -132,6 +142,8 @@ def test_2022(self): ), ("2022-07-29", "วันหยุดพิเศษ (เพิ่มเติม)"), ("2022-08-12", "วันเฉลิมพระชนมพรรษาสมเด็จพระบรมราชชนนีพันปีหลวง; วันแม่แห่งชาติ"), + ("2022-08-18", "วันวิทยาศาสตร์แห่งชาติ"), + ("2022-09-28", "วันพระราชทานธงชาติไทย"), ( "2022-10-13", ( @@ -142,6 +154,7 @@ def test_2022(self): ("2022-10-14", "วันหยุดพิเศษ (เพิ่มเติม)"), ("2022-10-23", "วันปิยมหาราช"), ("2022-10-24", "ชดเชยวันปิยมหาราช"), + ("2022-11-08", "วันลอยกระทง"), ( "2022-12-05", ( @@ -167,6 +180,33 @@ def test_new_years_day(self): "2028-01-03", ) + def test_national_childrens_day(self): + name = "วันเด็กแห่งชาติ" + + dt = ( + # First Iteration + "1955-10-03", + # Second and Current Iteration + "2010-01-09", + "2011-01-08", + "2012-01-14", + "2013-01-12", + "2014-01-11", + "2015-01-10", + "2016-01-09", + "2017-01-14", + "2018-01-13", + "2019-01-12", + "2020-01-11", + "2021-01-09", + "2022-01-08", + "2023-01-14", + "2024-01-13", + "2025-01-11", + ) + self.assertHolidayName(name, dt) + self.assertNoHolidayName(name, 1954, 1964) + def test_chakri_memorial_day(self): self.assertHoliday(f"{year}-04-06" for year in range(1941, 2058)) @@ -561,33 +601,173 @@ def test_khao_phansa(self): def test_raeknakhwan(self): name = "วันพืชมงคล" - dt = ( - "2011-05-13", - "2012-05-09", - "2013-05-13", - "2014-05-09", - "2015-05-13", - "2016-05-09", - "2017-05-12", - "2018-05-14", - "2019-05-09", - "2020-05-11", - "2021-05-13", - "2022-05-17", - "2023-05-11", + self.assertHolidays( + Thailand(categories=(GOVERNMENT,), years=range(1997, 2024)), + ("1997-05-13", name), + ("1998-05-13", name), + ("2000-05-15", name), + ("2001-05-16", name), + ("2002-05-09", name), + ("2003-05-08", name), + ("2004-05-07", name), + ("2005-05-11", name), + ("2006-05-11", name), + ("2007-05-10", name), + ("2008-05-09", name), + ("2009-05-11", name), + ("2010-05-10", name), + ("2011-05-13", name), + ("2012-05-09", name), + ("2013-05-13", name), + ("2014-05-09", name), + ("2015-05-13", name), + ("2016-05-09", name), + ("2017-05-12", name), + ("2018-05-14", name), + ("2019-05-09", name), + ("2020-05-11", name), + ("2021-05-13", name), + ("2022-05-17", name), + ("2023-05-11", name), + ) + + def test_armed_forces_holiday(self): + name = "วันกองทัพไทย" + self.assertHolidays( + Thailand(categories=(ARMED_FORCES,), years=range(1958, 1960)), ("1959-04-08", name) + ) + self.assertHolidays( + Thailand(categories=(ARMED_FORCES,), years=range(1979, 1981)), + ("1979-04-08", name), + ("1980-01-25", name), + ) + self.assertHolidays( + Thailand(categories=(ARMED_FORCES,), years=range(2006, 2008)), + ("2006-01-25", name), + ("2007-01-18", name), + ) + + def test_bank_holiday(self): + a_name = "วันหยุดเพิ่มเติมสำหรับการปิดบัญชีประจำปีของธนาคารเพื่อการเกษตรและสหกรณ์การเกษตร" + m_name = "วันหยุดภาคครึ่งปีของสถาบันการเงินและสถาบันการเงินเฉพาะกิจ" + + # Start + self.assertHolidays( + Thailand(categories=(BANK,), years=range(1942, 1944)), + ("1943-04-01", a_name), + ("1943-07-01", m_name), + ) + # End + self.assertHolidays( + Thailand(categories=(BANK,), years=range(2017, 2023)), + ("2017-04-01", a_name), + ("2017-07-01", m_name), + ("2018-04-01", a_name), + ("2018-07-01", m_name), + ("2019-04-01", a_name), + ("2020-04-01", a_name), + ("2021-04-01", a_name), + ) + + def test_school_holiday(self): + self.assertHolidays( + Thailand(categories=(SCHOOL,), years=range(1956, 1958)), + ("1957-01-16", "วันครู"), + ) + + def test_workday_1947(self): + self.assertHolidays( + Thailand(categories=(WORKDAY,), years=1947), + ("1947-11-27", "วันลอยกระทง"), + ) + + def test_workday_1948(self): + self.assertHolidays( + Thailand(categories=(WORKDAY,), years=1948), + ("1948-02-03", "วันทหารผ่านศึก"), + ("1948-11-15", "วันลอยกระทง"), + ) + + def test_workday_1982(self): + self.assertHolidays( + Thailand(categories=(WORKDAY,), years=1982), + ("1982-02-03", "วันทหารผ่านศึก"), + ("1982-08-18", "วันวิทยาศาสตร์แห่งชาติ"), + ("1982-10-31", "วันลอยกระทง"), ) - self.assertHolidayName(name, dt) - self.assertNoHolidayName(name, 1956, 1999) - self.assertHolidayName(name, (f"{year}-05-13" for year in range(1957, 1997))) + def test_workday_1985(self): + self.assertHolidays( + Thailand(categories=(WORKDAY,), years=1985), + ("1985-02-03", "วันทหารผ่านศึก"), + ("1985-02-26", "วันศิลปินแห่งชาติ"), + ("1985-08-18", "วันวิทยาศาสตร์แห่งชาติ"), + ("1985-11-26", "วันลอยกระทง"), + ) - # No Royal Ploughing Ceremony on weekend for 1997-2023 + def test_workday_1989(self): + self.assertHolidays( + Thailand(categories=(WORKDAY,), years=1989), + ("1989-02-03", "วันทหารผ่านศึก"), + ("1989-02-26", "วันศิลปินแห่งชาติ"), + ("1989-03-08", "วันสตรีสากล"), + ("1989-08-18", "วันวิทยาศาสตร์แห่งชาติ"), + ("1989-11-12", "วันลอยกระทง"), + ) + + def test_workday_1990(self): + self.assertHolidays( + Thailand(categories=(WORKDAY,), years=1990), + ("1990-01-14", "วันอนุรักษ์ทรัพยากรป่าไม้ของชาติ"), + ("1990-01-17", "วันพ่อขุนรามคำแหงมหาราช"), + ("1990-02-03", "วันทหารผ่านศึก"), + ("1990-02-26", "วันศิลปินแห่งชาติ"), + ("1990-03-08", "วันสตรีสากล"), + ("1990-08-18", "วันวิทยาศาสตร์แห่งชาติ"), + ("1990-11-02", "วันลอยกระทง"), + ) + + def test_workday_1995(self): + self.assertHolidays( + Thailand(categories=(WORKDAY,), years=1995), + ("1995-01-13", "วันการบินแห่งชาติ"), + ("1995-01-14", "วันอนุรักษ์ทรัพยากรป่าไม้ของชาติ"), + ("1995-01-17", "วันพ่อขุนรามคำแหงมหาราช"), + ("1995-02-03", "วันทหารผ่านศึก"), + ("1995-02-26", "วันศิลปินแห่งชาติ"), + ("1995-03-08", "วันสตรีสากล"), + ("1995-08-18", "วันวิทยาศาสตร์แห่งชาติ"), + ("1995-11-06", "วันลอยกระทง"), + ) + + def test_workday_2017(self): + self.assertHolidays( + Thailand(categories=(WORKDAY,), years=2017), + ("2017-01-13", "วันการบินแห่งชาติ"), + ("2017-01-14", "วันอนุรักษ์ทรัพยากรป่าไม้ของชาติ"), + ("2017-01-17", "วันพ่อขุนรามคำแหงมหาราช"), + ("2017-02-03", "วันทหารผ่านศึก"), + ("2017-02-26", "วันศิลปินแห่งชาติ"), + ("2017-03-08", "วันสตรีสากล"), + ("2017-08-18", "วันวิทยาศาสตร์แห่งชาติ"), + ("2017-09-28", "วันพระราชทานธงชาติไทย"), + ("2017-11-03", "วันลอยกระทง"), + ) def test_l10n_default(self): self.assertLocalizedHolidays( ("2022-01-01", "วันขึ้นปีใหม่"), ("2022-01-03", "ชดเชยวันขึ้นปีใหม่"), + ("2022-01-08", "วันเด็กแห่งชาติ"), + ("2022-01-13", "วันการบินแห่งชาติ"), + ("2022-01-14", "วันอนุรักษ์ทรัพยากรป่าไม้ของชาติ"), + ("2022-01-16", "วันครู"), + ("2022-01-17", "วันพ่อขุนรามคำแหงมหาราช"), + ("2022-01-18", "วันกองทัพไทย"), + ("2022-02-03", "วันทหารผ่านศึก"), ("2022-02-16", "วันมาฆบูชา"), + ("2022-02-26", "วันศิลปินแห่งชาติ"), + ("2022-03-08", "วันสตรีสากล"), ("2022-04-06", "วันจักรี"), ("2022-04-13", "วันสงกรานต์"), ("2022-04-14", "วันสงกรานต์"), @@ -614,6 +794,8 @@ def test_l10n_default(self): ), ("2022-07-29", "วันหยุดพิเศษ (เพิ่มเติม)"), ("2022-08-12", "วันเฉลิมพระชนมพรรษาสมเด็จพระบรมราชชนนีพันปีหลวง; วันแม่แห่งชาติ"), + ("2022-08-18", "วันวิทยาศาสตร์แห่งชาติ"), + ("2022-09-28", "วันพระราชทานธงชาติไทย"), ( "2022-10-13", ( @@ -624,6 +806,7 @@ def test_l10n_default(self): ("2022-10-14", "วันหยุดพิเศษ (เพิ่มเติม)"), ("2022-10-23", "วันปิยมหาราช"), ("2022-10-24", "ชดเชยวันปิยมหาราช"), + ("2022-11-08", "วันลอยกระทง"), ( "2022-12-05", ( @@ -642,7 +825,16 @@ def test_l10n_en_US(self): "en_US", ("2022-01-01", "New Year's Day"), ("2022-01-03", "New Year's Day (in lieu)"), + ("2022-01-08", "National Children's Day"), + ("2022-01-13", "National Aviation Day"), + ("2022-01-14", "National Forest Conservation Day"), + ("2022-01-16", "Teacher's Day"), + ("2022-01-17", "HM King Ramkamhaeng Memorial Day"), + ("2022-01-18", "Royal Thai Armed Forces Day"), + ("2022-02-03", "Thai Veterans Day"), ("2022-02-16", "Makha Bucha"), + ("2022-02-26", "National Artist Day"), + ("2022-03-08", "International Women's Day"), ("2022-04-06", "Chakri Memorial Day"), ("2022-04-13", "Songkran Festival"), ("2022-04-14", "Songkran Festival"), @@ -663,10 +855,13 @@ def test_l10n_en_US(self): "2022-08-12", "HM Queen Sirikit The Queen Mother's Birthday; National Mother's Day", ), + ("2022-08-18", "National Science Day"), + ("2022-09-28", "Thai National Flag Day"), ("2022-10-13", "HM King Bhumibol Adulyadej the Great Memorial Day"), ("2022-10-14", "Bridge Public Holiday"), ("2022-10-23", "HM King Chulalongkorn Memorial Day"), ("2022-10-24", "HM King Chulalongkorn Memorial Day (in lieu)"), + ("2022-11-08", "Loy Krathong"), ( "2022-12-05", ( From 0b40a49d3d00bd285682d7316a92d0f829fe92e6 Mon Sep 17 00:00:00 2001 From: Arkadii Yakovets Date: Wed, 13 Sep 2023 09:51:46 -0700 Subject: [PATCH 13/21] Update pre commit automatic update workflow (#1469) --- .github/workflows/pre-commit-autoupdate.yml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/workflows/pre-commit-autoupdate.yml b/.github/workflows/pre-commit-autoupdate.yml index 49e2eadd9..f31f370ce 100644 --- a/.github/workflows/pre-commit-autoupdate.yml +++ b/.github/workflows/pre-commit-autoupdate.yml @@ -1,4 +1,4 @@ -name: Pre-commit hooks autoupdate +name: Chores on: schedule: @@ -7,6 +7,11 @@ on: jobs: auto-update: + name: Update pre-commit hooks + if: ${{ github.repository }} == "vacanza/python-holidays" + permissions: + contents: write + pull-requests: write runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -14,11 +19,12 @@ jobs: - uses: browniebroke/pre-commit-autoupdate-action@v1.0.0 - uses: peter-evans/create-pull-request@v5.0.2 with: + author: github-actions[bot] base: beta + body: Update versions of pre-commit hooks to the latest version. branch: update/pre-commit-hooks - title: Update pre-commit hooks - author: github-actions[bot] commit-message: "Chore: Update pre-commit hooks" committer: github-actions[bot] - body: Update versions of pre-commit hooks to latest version. + delete-branch: true + title: Update pre-commit hooks token: ${{ secrets.GITHUB_TOKEN }} From c3bac4799222b9bf441fb10440870be1959a8702 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 13 Sep 2023 11:35:10 -0700 Subject: [PATCH 14/21] Update pre-commit hooks (#1470) Co-authored-by: github-actions[bot] --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4b8f6f03a..099b60c77 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -13,7 +13,7 @@ repos: - id: trailing-whitespace - repo: https://github.com/python/black - rev: 23.7.0 + rev: 23.9.1 hooks: - id: black exclude: ^(docs) @@ -50,7 +50,7 @@ repos: - id: rst-backticks - repo: https://github.com/myint/rstcheck - rev: v6.1.2 + rev: v6.2.0 hooks: - id: rstcheck additional_dependencies: [rstcheck, sphinx] From 02aa5d6ce2871dd3a38d97fb398edd67dad47012 Mon Sep 17 00:00:00 2001 From: ~Jhellico Date: Fri, 15 Sep 2023 19:04:59 +0300 Subject: [PATCH 15/21] Update Belgium holidays: add bank holidays (#1457) --- holidays/countries/belgium.py | 22 ++++++-- holidays/locale/de/LC_MESSAGES/BE.po | 20 ++++++-- holidays/locale/en_US/LC_MESSAGES/BE.po | 20 ++++++-- holidays/locale/fr/LC_MESSAGES/BE.po | 20 ++++++-- holidays/locale/nl/LC_MESSAGES/BE.po | 20 ++++++-- holidays/locale/uk/LC_MESSAGES/BE.po | 20 ++++++-- tests/countries/test_belgium.py | 68 +++++++++++++++++-------- 7 files changed, 143 insertions(+), 47 deletions(-) diff --git a/holidays/countries/belgium.py b/holidays/countries/belgium.py index 21c2f57dc..cffcb59cd 100644 --- a/holidays/countries/belgium.py +++ b/holidays/countries/belgium.py @@ -9,8 +9,10 @@ # Website: https://github.com/dr-prodigy/python-holidays # License: MIT (see LICENSE file) +from datetime import timedelta as td from gettext import gettext as tr +from holidays.constants import BANK, PUBLIC from holidays.groups import ChristianHolidays, InternationalHolidays from holidays.holiday_base import HolidayBase @@ -20,10 +22,12 @@ class Belgium(HolidayBase, ChristianHolidays, InternationalHolidays): https://en.wikipedia.org/wiki/Public_holidays_in_Belgium https://www.belgium.be/nl/over_belgie/land/belgie_in_een_notendop/feestdagen https://nl.wikipedia.org/wiki/Feestdagen_in_Belgi%C3%AB + https://www.nbb.be/en/about-national-bank/national-bank-belgium/public-holidays """ country = "BE" default_language = "nl" + supported_categories = {BANK, PUBLIC} supported_languages = ("de", "en_US", "fr", "nl", "uk") def __init__(self, *args, **kwargs): @@ -31,9 +35,7 @@ def __init__(self, *args, **kwargs): InternationalHolidays.__init__(self) super().__init__(*args, **kwargs) - def _populate(self, year): - super()._populate(year) - + def _populate_public_holidays(self): # New Year's Day. self._add_new_years_day(tr("Nieuwjaar")) @@ -47,7 +49,7 @@ def _populate(self, year): self._add_labor_day(tr("Dag van de Arbeid")) # Ascension Day. - self._add_ascension_thursday(tr("Hemelvaart")) + self._add_ascension_thursday(tr("O. L. H. Hemelvaart")) # Whit Sunday. self._add_whit_sunday(tr("Pinksteren")) @@ -59,7 +61,7 @@ def _populate(self, year): self._add_holiday_jul_21(tr("Nationale feestdag")) # Assumption of Mary. - self._add_assumption_of_mary_day(tr("Onze Lieve Vrouw hemelvaart")) + self._add_assumption_of_mary_day(tr("O. L. V. Hemelvaart")) # All Saints' Day. self._add_all_saints_day(tr("Allerheiligen")) @@ -70,6 +72,16 @@ def _populate(self, year): # Christmas Day. self._add_christmas_day(tr("Kerstmis")) + def _populate_bank_holidays(self): + # Good Friday. + self._add_good_friday(tr("Goede Vrijdag")) + + # Friday after Ascension Day. + self._add_holiday(tr("Vrijdag na O. L. H. Hemelvaart"), self._easter_sunday + td(days=+40)) + + # Bank Holiday. + self._add_christmas_day_two(tr("Banksluitingsdag")) + class BE(Belgium): pass diff --git a/holidays/locale/de/LC_MESSAGES/BE.po b/holidays/locale/de/LC_MESSAGES/BE.po index 55050d83c..6cdbf7f7e 100644 --- a/holidays/locale/de/LC_MESSAGES/BE.po +++ b/holidays/locale/de/LC_MESSAGES/BE.po @@ -3,9 +3,9 @@ # msgid "" msgstr "" -"Project-Id-Version: Python Holidays 0.28\n" +"Project-Id-Version: Python Holidays 0.33\n" "POT-Creation-Date: 2023-06-27 13:17+0300\n" -"PO-Revision-Date: 2023-06-28 15:14+0300\n" +"PO-Revision-Date: 2023-09-06 20:49+0300\n" "Last-Translator: ~Jhellico \n" "Language-Team: Python Holidays localization team\n" "Language: de\n" @@ -33,7 +33,7 @@ msgid "Dag van de Arbeid" msgstr "Tag der Arbeit" #. Ascension Day. -msgid "Hemelvaart" +msgid "O. L. H. Hemelvaart" msgstr "Christi Himmelfahrt" #. Whit Sunday. @@ -49,7 +49,7 @@ msgid "Nationale feestdag" msgstr "Nationalfeiertag" #. Assumption of Mary. -msgid "Onze Lieve Vrouw hemelvaart" +msgid "O. L. V. Hemelvaart" msgstr "Mariä Himmelfahrt" #. All Saints' Day. @@ -63,3 +63,15 @@ msgstr "Waffenstillstand" #. Christmas Day. msgid "Kerstmis" msgstr "Weihnachten" + +#. Good Friday. +msgid "Goede Vrijdag" +msgstr "Karfreitag" + +#. Friday after Ascension Day. +msgid "Vrijdag na O. L. H. Hemelvaart" +msgstr "Freitag nach Christi Himmelfahrt" + +#. Bank Holiday. +msgid "Banksluitingsdag" +msgstr "Bankschlusstag" diff --git a/holidays/locale/en_US/LC_MESSAGES/BE.po b/holidays/locale/en_US/LC_MESSAGES/BE.po index 91cc7b736..8f935f878 100644 --- a/holidays/locale/en_US/LC_MESSAGES/BE.po +++ b/holidays/locale/en_US/LC_MESSAGES/BE.po @@ -3,9 +3,9 @@ # msgid "" msgstr "" -"Project-Id-Version: Python Holidays 0.28\n" +"Project-Id-Version: Python Holidays 0.33\n" "POT-Creation-Date: 2023-06-27 13:17+0300\n" -"PO-Revision-Date: 2023-06-27 13:44+0300\n" +"PO-Revision-Date: 2023-09-06 20:47+0300\n" "Last-Translator: ~Jhellico \n" "Language-Team: Python Holidays localization team\n" "Language: en_US\n" @@ -33,7 +33,7 @@ msgid "Dag van de Arbeid" msgstr "Labor Day" #. Ascension Day. -msgid "Hemelvaart" +msgid "O. L. H. Hemelvaart" msgstr "Ascension Day" #. Whit Sunday. @@ -49,7 +49,7 @@ msgid "Nationale feestdag" msgstr "National Day" #. Assumption of Mary. -msgid "Onze Lieve Vrouw hemelvaart" +msgid "O. L. V. Hemelvaart" msgstr "Assumption of Mary" #. All Saints' Day. @@ -63,3 +63,15 @@ msgstr "Armistice Day" #. Christmas Day. msgid "Kerstmis" msgstr "Christmas Day" + +#. Good Friday. +msgid "Goede Vrijdag" +msgstr "Good Friday" + +#. Friday after Ascension Day. +msgid "Vrijdag na O. L. H. Hemelvaart" +msgstr "Friday after Ascension Day" + +#. Bank Holiday. +msgid "Banksluitingsdag" +msgstr "Bank Holiday" diff --git a/holidays/locale/fr/LC_MESSAGES/BE.po b/holidays/locale/fr/LC_MESSAGES/BE.po index a1d6aaae4..a1965e87d 100644 --- a/holidays/locale/fr/LC_MESSAGES/BE.po +++ b/holidays/locale/fr/LC_MESSAGES/BE.po @@ -3,9 +3,9 @@ # msgid "" msgstr "" -"Project-Id-Version: Python Holidays 0.28\n" +"Project-Id-Version: Python Holidays 0.33\n" "POT-Creation-Date: 2023-06-27 13:17+0300\n" -"PO-Revision-Date: 2023-06-28 15:28+0300\n" +"PO-Revision-Date: 2023-09-06 20:51+0300\n" "Last-Translator: ~Jhellico \n" "Language-Team: Python Holidays localization team\n" "Language: fr\n" @@ -33,7 +33,7 @@ msgid "Dag van de Arbeid" msgstr "Fête du Travail" #. Ascension Day. -msgid "Hemelvaart" +msgid "O. L. H. Hemelvaart" msgstr "Ascension" #. Whit Sunday. @@ -49,7 +49,7 @@ msgid "Nationale feestdag" msgstr "Fête nationale" #. Assumption of Mary. -msgid "Onze Lieve Vrouw hemelvaart" +msgid "O. L. V. Hemelvaart" msgstr "Assomption" #. All Saints' Day. @@ -63,3 +63,15 @@ msgstr "Jour de l'Armistice" #. Christmas Day. msgid "Kerstmis" msgstr "Noël" + +#. Good Friday. +msgid "Goede Vrijdag" +msgstr "Vendredi Saint" + +#. Friday after Ascension Day. +msgid "Vrijdag na O. L. H. Hemelvaart" +msgstr "Vendredi suivant l'Ascension" + +#. Bank Holiday. +msgid "Banksluitingsdag" +msgstr "Jour de fermeture bancaire" diff --git a/holidays/locale/nl/LC_MESSAGES/BE.po b/holidays/locale/nl/LC_MESSAGES/BE.po index 2f1498021..004398c73 100644 --- a/holidays/locale/nl/LC_MESSAGES/BE.po +++ b/holidays/locale/nl/LC_MESSAGES/BE.po @@ -3,9 +3,9 @@ # msgid "" msgstr "" -"Project-Id-Version: Python Holidays 0.28\n" +"Project-Id-Version: Python Holidays 0.33\n" "POT-Creation-Date: 2023-06-27 13:17+0300\n" -"PO-Revision-Date: 2023-06-27 13:41+0300\n" +"PO-Revision-Date: 2023-09-06 20:46+0300\n" "Last-Translator: ~Jhellico \n" "Language-Team: Python Holidays localization team\n" "Language: nl\n" @@ -33,7 +33,7 @@ msgid "Dag van de Arbeid" msgstr "" #. Ascension Day. -msgid "Hemelvaart" +msgid "O. L. H. Hemelvaart" msgstr "" #. Whit Sunday. @@ -49,7 +49,7 @@ msgid "Nationale feestdag" msgstr "" #. Assumption of Mary. -msgid "Onze Lieve Vrouw hemelvaart" +msgid "O. L. V. Hemelvaart" msgstr "" #. All Saints' Day. @@ -63,3 +63,15 @@ msgstr "" #. Christmas Day. msgid "Kerstmis" msgstr "" + +#. Good Friday. +msgid "Goede Vrijdag" +msgstr "" + +#. Friday after Ascension Day. +msgid "Vrijdag na O. L. H. Hemelvaart" +msgstr "" + +#. Bank Holiday. +msgid "Banksluitingsdag" +msgstr "" diff --git a/holidays/locale/uk/LC_MESSAGES/BE.po b/holidays/locale/uk/LC_MESSAGES/BE.po index c623fe425..e0b5b01cd 100644 --- a/holidays/locale/uk/LC_MESSAGES/BE.po +++ b/holidays/locale/uk/LC_MESSAGES/BE.po @@ -3,9 +3,9 @@ # msgid "" msgstr "" -"Project-Id-Version: Python Holidays 0.28\n" +"Project-Id-Version: Python Holidays 0.33\n" "POT-Creation-Date: 2023-06-27 13:17+0300\n" -"PO-Revision-Date: 2023-06-27 14:11+0300\n" +"PO-Revision-Date: 2023-09-06 20:53+0300\n" "Last-Translator: ~Jhellico \n" "Language-Team: Python Holidays localization team\n" "Language: uk\n" @@ -33,7 +33,7 @@ msgid "Dag van de Arbeid" msgstr "День праці" #. Ascension Day. -msgid "Hemelvaart" +msgid "O. L. H. Hemelvaart" msgstr "Вознесіння Господнє" #. Whit Sunday. @@ -49,7 +49,7 @@ msgid "Nationale feestdag" msgstr "Національне свято" #. Assumption of Mary. -msgid "Onze Lieve Vrouw hemelvaart" +msgid "O. L. V. Hemelvaart" msgstr "Внебовзяття Пресвятої Діви Марії" #. All Saints' Day. @@ -63,3 +63,15 @@ msgstr "День перемирʼя" #. Christmas Day. msgid "Kerstmis" msgstr "Різдво Христове" + +#. Good Friday. +msgid "Goede Vrijdag" +msgstr "Страсна пʼятниця" + +#. Friday after Ascension Day. +msgid "Vrijdag na O. L. H. Hemelvaart" +msgstr "Пʼятниця після Вознесіння Господнього" + +#. Bank Holiday. +msgid "Banksluitingsdag" +msgstr "Банківський вихідний" diff --git a/tests/countries/test_belgium.py b/tests/countries/test_belgium.py index d00b84de3..9f858a6a2 100644 --- a/tests/countries/test_belgium.py +++ b/tests/countries/test_belgium.py @@ -9,6 +9,7 @@ # Website: https://github.com/dr-prodigy/python-holidays # License: MIT (see LICENSE file) +from holidays.constants import BANK from holidays.countries.belgium import Belgium, BE, BEL from tests.common import TestCase @@ -21,33 +22,17 @@ def setUpClass(cls): def test_country_aliases(self): self.assertCountryAliases(Belgium, BE, BEL) - def test_2019(self): - self.assertHolidays( - ("2019-01-01", "Nieuwjaar"), - ("2019-04-21", "Pasen"), - ("2019-04-22", "Paasmaandag"), - ("2019-05-01", "Dag van de Arbeid"), - ("2019-05-30", "Hemelvaart"), - ("2019-06-09", "Pinksteren"), - ("2019-06-10", "Pinkstermaandag"), - ("2019-07-21", "Nationale feestdag"), - ("2019-08-15", "Onze Lieve Vrouw hemelvaart"), - ("2019-11-01", "Allerheiligen"), - ("2019-11-11", "Wapenstilstand"), - ("2019-12-25", "Kerstmis"), - ) - def test_2020(self): self.assertHolidays( ("2020-01-01", "Nieuwjaar"), ("2020-04-12", "Pasen"), ("2020-04-13", "Paasmaandag"), ("2020-05-01", "Dag van de Arbeid"), - ("2020-05-21", "Hemelvaart"), + ("2020-05-21", "O. L. H. Hemelvaart"), ("2020-05-31", "Pinksteren"), ("2020-06-01", "Pinkstermaandag"), ("2020-07-21", "Nationale feestdag"), - ("2020-08-15", "Onze Lieve Vrouw hemelvaart"), + ("2020-08-15", "O. L. V. Hemelvaart"), ("2020-11-01", "Allerheiligen"), ("2020-11-11", "Wapenstilstand"), ("2020-12-25", "Kerstmis"), @@ -59,40 +44,69 @@ def test_2021(self): ("2021-04-04", "Pasen"), ("2021-04-05", "Paasmaandag"), ("2021-05-01", "Dag van de Arbeid"), - ("2021-05-13", "Hemelvaart"), + ("2021-05-13", "O. L. H. Hemelvaart"), ("2021-05-23", "Pinksteren"), ("2021-05-24", "Pinkstermaandag"), ("2021-07-21", "Nationale feestdag"), - ("2021-08-15", "Onze Lieve Vrouw hemelvaart"), + ("2021-08-15", "O. L. V. Hemelvaart"), ("2021-11-01", "Allerheiligen"), ("2021-11-11", "Wapenstilstand"), ("2021-12-25", "Kerstmis"), ) + def test_2022(self): + self.assertHolidays( + ("2022-01-01", "Nieuwjaar"), + ("2022-04-17", "Pasen"), + ("2022-04-18", "Paasmaandag"), + ("2022-05-01", "Dag van de Arbeid"), + ("2022-05-26", "O. L. H. Hemelvaart"), + ("2022-06-05", "Pinksteren"), + ("2022-06-06", "Pinkstermaandag"), + ("2022-07-21", "Nationale feestdag"), + ("2022-08-15", "O. L. V. Hemelvaart"), + ("2022-11-01", "Allerheiligen"), + ("2022-11-11", "Wapenstilstand"), + ("2022-12-25", "Kerstmis"), + ) + + def test_bank_2022(self): + self.assertHolidays( + Belgium(categories=(BANK,), years=2022), + ("2022-04-15", "Goede Vrijdag"), + ("2022-05-27", "Vrijdag na O. L. H. Hemelvaart"), + ("2022-12-26", "Banksluitingsdag"), + ) + def test_l10n_default(self): self.assertLocalizedHolidays( ("2022-01-01", "Nieuwjaar"), + ("2022-04-15", "Goede Vrijdag"), ("2022-04-17", "Pasen"), ("2022-04-18", "Paasmaandag"), ("2022-05-01", "Dag van de Arbeid"), - ("2022-05-26", "Hemelvaart"), + ("2022-05-26", "O. L. H. Hemelvaart"), + ("2022-05-27", "Vrijdag na O. L. H. Hemelvaart"), ("2022-06-05", "Pinksteren"), ("2022-06-06", "Pinkstermaandag"), ("2022-07-21", "Nationale feestdag"), - ("2022-08-15", "Onze Lieve Vrouw hemelvaart"), + ("2022-08-15", "O. L. V. Hemelvaart"), ("2022-11-01", "Allerheiligen"), ("2022-11-11", "Wapenstilstand"), ("2022-12-25", "Kerstmis"), + ("2022-12-26", "Banksluitingsdag"), ) def test_l10n_de(self): self.assertLocalizedHolidays( "de", ("2022-01-01", "Neujahr"), + ("2022-04-15", "Karfreitag"), ("2022-04-17", "Ostern"), ("2022-04-18", "Ostermontag"), ("2022-05-01", "Tag der Arbeit"), ("2022-05-26", "Christi Himmelfahrt"), + ("2022-05-27", "Freitag nach Christi Himmelfahrt"), ("2022-06-05", "Pfingsten"), ("2022-06-06", "Pfingstmontag"), ("2022-07-21", "Nationalfeiertag"), @@ -100,16 +114,19 @@ def test_l10n_de(self): ("2022-11-01", "Allerheiligen"), ("2022-11-11", "Waffenstillstand"), ("2022-12-25", "Weihnachten"), + ("2022-12-26", "Bankschlusstag"), ) def test_l10n_en_us(self): self.assertLocalizedHolidays( "en_US", ("2022-01-01", "New Year's Day"), + ("2022-04-15", "Good Friday"), ("2022-04-17", "Easter"), ("2022-04-18", "Easter Monday"), ("2022-05-01", "Labor Day"), ("2022-05-26", "Ascension Day"), + ("2022-05-27", "Friday after Ascension Day"), ("2022-06-05", "Whit Sunday"), ("2022-06-06", "Whit Monday"), ("2022-07-21", "National Day"), @@ -117,16 +134,19 @@ def test_l10n_en_us(self): ("2022-11-01", "All Saints' Day"), ("2022-11-11", "Armistice Day"), ("2022-12-25", "Christmas Day"), + ("2022-12-26", "Bank Holiday"), ) def test_l10n_fr(self): self.assertLocalizedHolidays( "fr", ("2022-01-01", "Nouvel An"), + ("2022-04-15", "Vendredi Saint"), ("2022-04-17", "Pâques"), ("2022-04-18", "Lundi de Pâques"), ("2022-05-01", "Fête du Travail"), ("2022-05-26", "Ascension"), + ("2022-05-27", "Vendredi suivant l'Ascension"), ("2022-06-05", "Pentecôte"), ("2022-06-06", "Lundi de Pentecôte"), ("2022-07-21", "Fête nationale"), @@ -134,16 +154,19 @@ def test_l10n_fr(self): ("2022-11-01", "Toussaint"), ("2022-11-11", "Jour de l'Armistice"), ("2022-12-25", "Noël"), + ("2022-12-26", "Jour de fermeture bancaire"), ) def test_l10n_uk(self): self.assertLocalizedHolidays( "uk", ("2022-01-01", "Новий рік"), + ("2022-04-15", "Страсна пʼятниця"), ("2022-04-17", "Великдень"), ("2022-04-18", "Великодній понеділок"), ("2022-05-01", "День праці"), ("2022-05-26", "Вознесіння Господнє"), + ("2022-05-27", "Пʼятниця після Вознесіння Господнього"), ("2022-06-05", "Трійця"), ("2022-06-06", "День Святого Духа"), ("2022-07-21", "Національне свято"), @@ -151,4 +174,5 @@ def test_l10n_uk(self): ("2022-11-01", "День усіх святих"), ("2022-11-11", "День перемирʼя"), ("2022-12-25", "Різдво Христове"), + ("2022-12-26", "Банківський вихідний"), ) From 1f03de39e3692aa96a3003a5b624b14091d21f0a Mon Sep 17 00:00:00 2001 From: ~Jhellico Date: Fri, 15 Sep 2023 19:05:09 +0300 Subject: [PATCH 16/21] Update Liechtenstein holidays: specify bank holidays (#1462) --- holidays/countries/liechtenstein.py | 36 +++++++++--------- tests/countries/test_liechtenstein.py | 55 ++++++++++++++++++++------- 2 files changed, 61 insertions(+), 30 deletions(-) diff --git a/holidays/countries/liechtenstein.py b/holidays/countries/liechtenstein.py index b0dfae973..924a974a0 100644 --- a/holidays/countries/liechtenstein.py +++ b/holidays/countries/liechtenstein.py @@ -12,6 +12,7 @@ from gettext import gettext as tr +from holidays.constants import BANK, PUBLIC from holidays.groups import ChristianHolidays, InternationalHolidays from holidays.holiday_base import HolidayBase @@ -19,12 +20,14 @@ class Liechtenstein(HolidayBase, ChristianHolidays, InternationalHolidays): """ Liechtenstein holidays. - See https://en.wikipedia.org/wiki/Public_holidays_in_Liechtenstein - for details. + References: + - https://en.wikipedia.org/wiki/Public_holidays_in_Liechtenstein + - https://www.llb.li/en/contact/bank-holidays """ country = "LI" default_language = "de" + supported_categories = {BANK, PUBLIC} supported_languages = ("de", "en_US", "uk") def __init__(self, *args, **kwargs) -> None: @@ -32,30 +35,19 @@ def __init__(self, *args, **kwargs) -> None: InternationalHolidays.__init__(self) super().__init__(*args, **kwargs) - def _populate(self, year): - super()._populate(year) - + def _populate_public_holidays(self): # New Year's Day. self._add_new_years_day(tr("Neujahr")) - # Saint Berchtold's Day. - self._add_new_years_day_two(tr("Berchtoldstag")) - # Epiphany. self._add_epiphany_day(tr("Heilige Drei Könige")) # Candlemas. self._add_candlemas(tr("Mariä Lichtmess")) - # Shrove Tuesday. - self._add_carnival_tuesday(tr("Fasnachtsdienstag")) - # Saint Joseph's Day. self._add_saint_josephs_day(tr("Josefstag")) - # Good Friday. - self._add_good_friday(tr("Karfreitag")) - # Easter Sunday. self._add_easter_sunday(tr("Ostersonntag")) @@ -89,15 +81,25 @@ def _populate(self, year): # Immaculate Conception. self._add_immaculate_conception_day(tr("Mariä Empfängnis")) - # Christmas Eve. - self._add_christmas_eve(tr("Heiligabend")) - # Christmas Day. self._add_christmas_day(tr("Weihnachten")) # St. Stephen's Day. self._add_christmas_day_two(tr("Stefanstag")) + def _populate_bank_holidays(self): + # Saint Berchtold's Day. + self._add_new_years_day_two(tr("Berchtoldstag")) + + # Shrove Tuesday. + self._add_carnival_tuesday(tr("Fasnachtsdienstag")) + + # Good Friday. + self._add_good_friday(tr("Karfreitag")) + + # Christmas Eve. + self._add_christmas_eve(tr("Heiligabend")) + # New Year's Eve. self._add_new_years_eve(tr("Silvester")) diff --git a/tests/countries/test_liechtenstein.py b/tests/countries/test_liechtenstein.py index 750b7c078..da925dd0c 100644 --- a/tests/countries/test_liechtenstein.py +++ b/tests/countries/test_liechtenstein.py @@ -10,14 +10,17 @@ # License: MIT (see LICENSE file) # Copyright: Kateryna Golovanova , 2022 +from holidays.constants import BANK from holidays.countries.liechtenstein import Liechtenstein, LI, LIE from tests.common import TestCase -class TestLI(TestCase): +class TestLiechtenstein(TestCase): @classmethod def setUpClass(cls): - super().setUpClass(Liechtenstein, years=range(1900, 2050)) + years = range(1900, 2050) + super().setUpClass(Liechtenstein, years=years) + cls.bank_holidays = Liechtenstein(categories=(BANK,), years=years) def test_country_aliases(self): self.assertCountryAliases(Liechtenstein, LI, LIE) @@ -26,7 +29,12 @@ def test_new_years(self): self.assertHolidayName("Neujahr", (f"{year}-01-01" for year in range(1900, 2050))) def test_saint_berchtolds_day(self): - self.assertHolidayName("Berchtoldstag", (f"{year}-01-02" for year in range(1900, 2050))) + name = "Berchtoldstag" + self.assertHolidayName( + name, self.bank_holidays, (f"{year}-01-02" for year in range(1900, 2050)) + ) + self.assertNoHoliday(f"{year}-01-02" for year in range(1900, 2050)) + self.assertNoHolidayName(name) def test_epiphany(self): self.assertHolidayName( @@ -37,8 +45,8 @@ def test_candlemas(self): self.assertHolidayName("Mariä Lichtmess", (f"{year}-02-02" for year in range(1900, 2050))) def test_shrove_tuesday(self): - self.assertHolidayName( - "Fasnachtsdienstag", + name = "Fasnachtsdienstag" + dt = ( "1900-02-27", "1901-02-19", "1902-02-11", @@ -50,13 +58,16 @@ def test_shrove_tuesday(self): "2021-02-16", "2022-03-01", ) + self.assertHolidayName(name, self.bank_holidays, dt) + self.assertNoHoliday(dt) + self.assertNoHolidayName(name) def test_saint_josephs_day(self): self.assertHolidayName("Josefstag", (f"{year}-03-19" for year in range(1900, 2050))) def test_good_friday(self): - self.assertHolidayName( - "Karfreitag", + name = "Karfreitag" + dt = ( "1900-04-13", "1901-04-05", "1902-03-28", @@ -68,6 +79,9 @@ def test_good_friday(self): "2021-04-02", "2022-04-15", ) + self.assertHolidayName(name, self.bank_holidays, dt) + self.assertNoHoliday(dt) + self.assertNoHolidayName(name) def test_easter(self): self.assertHolidayName( @@ -175,7 +189,12 @@ def test_immaculate_conception(self): self.assertHolidayName("Mariä Empfängnis", (f"{year}-12-08" for year in range(1900, 2050))) def test_christmas_eve(self): - self.assertHolidayName("Heiligabend", (f"{year}-12-24" for year in range(1900, 2050))) + name = "Heiligabend" + self.assertHolidayName( + name, self.bank_holidays, (f"{year}-12-24" for year in range(1900, 2050)) + ) + self.assertNoHoliday(f"{year}-12-24" for year in range(1900, 2050)) + self.assertNoHolidayName(name) def test_christmas_day(self): self.assertHolidayName("Weihnachten", (f"{year}-12-25" for year in range(1900, 2050))) @@ -184,18 +203,20 @@ def test_st_stephens_day(self): self.assertHolidayName("Stefanstag", (f"{year}-12-26" for year in range(1900, 2050))) def test_new_years_eve(self): - self.assertHolidayName("Silvester", (f"{year}-12-31" for year in range(1900, 2050))) + name = "Silvester" + self.assertHolidayName( + name, self.bank_holidays, (f"{year}-12-31" for year in range(1900, 2050)) + ) + self.assertNoHoliday(f"{year}-12-31" for year in range(1900, 2050)) + self.assertNoHolidayName(name) def test_2022(self): self.assertHolidays( Liechtenstein(years=2022), ("2022-01-01", "Neujahr"), - ("2022-01-02", "Berchtoldstag"), ("2022-01-06", "Heilige Drei Könige"), ("2022-02-02", "Mariä Lichtmess"), - ("2022-03-01", "Fasnachtsdienstag"), ("2022-03-19", "Josefstag"), - ("2022-04-15", "Karfreitag"), ("2022-04-17", "Ostersonntag"), ("2022-04-18", "Ostermontag"), ("2022-05-01", "Tag der Arbeit"), @@ -207,9 +228,17 @@ def test_2022(self): ("2022-09-08", "Mariä Geburt"), ("2022-11-01", "Allerheiligen"), ("2022-12-08", "Mariä Empfängnis"), - ("2022-12-24", "Heiligabend"), ("2022-12-25", "Weihnachten"), ("2022-12-26", "Stefanstag"), + ) + + def test_2022_bank(self): + self.assertHolidays( + Liechtenstein(categories=(BANK,), years=2022), + ("2022-01-02", "Berchtoldstag"), + ("2022-03-01", "Fasnachtsdienstag"), + ("2022-04-15", "Karfreitag"), + ("2022-12-24", "Heiligabend"), ("2022-12-31", "Silvester"), ) From df19d58442599a38c6f1b81e2ab41dd66c8dc767 Mon Sep 17 00:00:00 2001 From: ~Jhellico Date: Fri, 15 Sep 2023 19:05:25 +0300 Subject: [PATCH 17/21] Migrate remaining countries to ObservedHolidayBase (#1463) --- holidays/countries/barbados.py | 28 +++++++-------- holidays/countries/bulgaria.py | 39 ++++++++++++-------- holidays/countries/burundi.py | 39 ++++++++------------ holidays/countries/hongkong.py | 47 +++++++++++++++---------- holidays/countries/vanuatu.py | 20 ++++------- holidays/locale/bg/LC_MESSAGES/BG.po | 1 + holidays/locale/en_US/LC_MESSAGES/BG.po | 1 + holidays/locale/uk/LC_MESSAGES/BG.po | 1 + tests/countries/test_bulgaria.py | 4 +-- tests/countries/test_vanuatu.py | 3 ++ 10 files changed, 94 insertions(+), 89 deletions(-) diff --git a/holidays/countries/barbados.py b/holidays/countries/barbados.py index c66c0f43b..9ee77ce42 100644 --- a/holidays/countries/barbados.py +++ b/holidays/countries/barbados.py @@ -9,15 +9,17 @@ # Website: https://github.com/dr-prodigy/python-holidays # License: MIT (see LICENSE file) -from datetime import date -from datetime import timedelta as td - from holidays.calendars.gregorian import JAN, JUL from holidays.groups import ChristianHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ( + ObservedHolidayBase, + MON_TO_NEXT_TUE, + SUN_TO_NEXT_MON, + SUN_TO_NEXT_TUE, +) -class Barbados(HolidayBase, ChristianHolidays, InternationalHolidays): +class Barbados(ObservedHolidayBase, ChristianHolidays, InternationalHolidays): """ https://en.wikipedia.org/wiki/Public_holidays_in_Barbados https://www.timeanddate.com/holidays/barbados/ @@ -40,14 +42,10 @@ class Barbados(HolidayBase, ChristianHolidays, InternationalHolidays): 2023: (JUL, 31, "50th Anniversary of CARICOM Holiday"), } - def __init__(self, *args, **kwargs) -> None: + def __init__(self, *args, **kwargs): ChristianHolidays.__init__(self) InternationalHolidays.__init__(self) - super().__init__(*args, **kwargs) - - def _add_observed(self, dt: date, days: int = +1) -> None: - if self.observed and self._is_sunday(dt): - self._add_holiday(self.observed_label % self[dt], dt + td(days=days)) + super().__init__(observed_rule=SUN_TO_NEXT_MON, *args, **kwargs) def _populate(self, year): # Public Holidays Act Cap.352, 1968-12-30 @@ -80,10 +78,8 @@ def _populate(self, year): # Emancipation Day name = "Emancipation Day" - self._add_observed(aug_1 := self._add_holiday_aug_1(name), days=+2) - # If Aug 1 is Kadooment Day. - if self.observed and self._is_monday(aug_1): - self._add_holiday(self.observed_label % name, aug_1 + td(days=+1)) + # If Aug 1 is Kadooment Day (i.e. Monday), observed on Tuesday. + self._add_observed(self._add_holiday_aug_1(name), rule=SUN_TO_NEXT_TUE + MON_TO_NEXT_TUE) # Kadooment Day self._add_holiday_1st_mon_of_aug("Kadooment Day") @@ -92,7 +88,7 @@ def _populate(self, year): self._add_observed(self._add_holiday_nov_30("Independence Day")) # Christmas - self._add_observed(self._add_christmas_day("Christmas Day"), days=+2) + self._add_observed(self._add_christmas_day("Christmas Day"), rule=SUN_TO_NEXT_TUE) # Boxing Day self._add_observed(self._add_christmas_day_two("Boxing Day")) diff --git a/holidays/countries/bulgaria.py b/holidays/countries/bulgaria.py index 1a713ab46..8bb2fb0fc 100644 --- a/holidays/countries/bulgaria.py +++ b/holidays/countries/bulgaria.py @@ -9,16 +9,17 @@ # Website: https://github.com/dr-prodigy/python-holidays # License: MIT (see LICENSE file) -from datetime import timedelta as td +from datetime import date from gettext import gettext as tr +from typing import Set from holidays.calendars.julian_revised import JULIAN_REVISED_CALENDAR from holidays.constants import PUBLIC, SCHOOL from holidays.groups import ChristianHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ObservedHolidayBase, SAT_SUN_TO_NEXT_WORKDAY -class Bulgaria(HolidayBase, ChristianHolidays, InternationalHolidays): +class Bulgaria(ObservedHolidayBase, ChristianHolidays, InternationalHolidays): """ Official holidays in Bulgaria in their current form. This class does not any return holidays before 1990, as holidays in the People's Republic of @@ -43,13 +44,25 @@ class Bulgaria(HolidayBase, ChristianHolidays, InternationalHolidays): country = "BG" default_language = "bg" + # %s (Observed). + observed_label = tr("%s (почивен ден)") supported_categories = {PUBLIC, SCHOOL} supported_languages = ("bg", "en_US", "uk") def __init__(self, *args, **kwargs): ChristianHolidays.__init__(self, JULIAN_REVISED_CALENDAR) InternationalHolidays.__init__(self) - super().__init__(*args, **kwargs) + super().__init__( + observed_rule=SAT_SUN_TO_NEXT_WORKDAY, observed_since=2017, *args, **kwargs + ) + + def _populate_observed(self, dts: Set[date], excluded_names: Set[str]) -> None: + for dt in sorted(dts): + if not self._is_observed(dt): + continue + for name in self.get_list(dt): + if name not in excluded_names: + self._add_observed(dt, name) def _populate_public_holidays(self): if self._year <= 1989: @@ -110,19 +123,15 @@ def _populate_public_holidays(self): dts_observed.add(self._add_christmas_day(name)) dts_observed.add(self._add_christmas_day_two(name)) - if self.observed and self._year >= 2017: - excluded_names = {self.tr("Велика събота"), self.tr("Великден")} - for dt in sorted(dts_observed): - if not self._is_weekend(dt): - continue - dt_observed = dt + td(days=+2 if self._is_saturday(dt) else +1) - while dt_observed in self: - dt_observed += td(days=+1) - for name in self.get_list(dt): - if name not in excluded_names: - self._add_holiday(self.tr("%s (почивен ден)") % name, dt_observed) + if self.observed: + self._populate_observed( + dts_observed, excluded_names={self.tr("Велика събота"), self.tr("Великден")} + ) def _populate_school_holidays(self): + if self._year <= 1989: + return None + # National Awakening Day. self._add_holiday_nov_1(tr("Ден на народните будители")) diff --git a/holidays/countries/burundi.py b/holidays/countries/burundi.py index 2f14b08b6..b541716d0 100644 --- a/holidays/countries/burundi.py +++ b/holidays/countries/burundi.py @@ -9,9 +9,6 @@ # Website: https://github.com/dr-prodigy/python-holidays # License: MIT (see LICENSE file) -from datetime import date -from typing import Optional - from holidays.groups import ChristianHolidays, IslamicHolidays, InternationalHolidays from holidays.observed_holiday_base import ObservedHolidayBase, SUN_TO_NEXT_MON @@ -37,12 +34,6 @@ def __init__(self, *args, **kwargs): IslamicHolidays.__init__(self) super().__init__(observed_rule=SUN_TO_NEXT_MON, *args, **kwargs) - def _add_holiday(self, name: str, *args) -> Optional[date]: - dt_added = super()._add_holiday(name, *args) - if dt_added: - self._add_observed(dt_added) - return dt_added - def _populate(self, year): if year <= 1961: return None @@ -50,52 +41,52 @@ def _populate(self, year): super()._populate(year) # New Year's Day - self._add_new_years_day("New Year's Day") + self._add_observed(self._add_new_years_day("New Year's Day")) # Unity Day if year >= 1992: - self._add_holiday_feb_5("Unity Day") + self._add_observed(self._add_holiday_feb_5("Unity Day")) # President Ntaryamira Day if year >= 1995: - self._add_holiday_apr_6("President Ntaryamira Day") + self._add_observed(self._add_holiday_apr_6("President Ntaryamira Day")) # Labour Day - self._add_labor_day("Labour Day") + self._add_observed(self._add_labor_day("Labour Day")) # Ascension Day self._add_ascension_thursday("Ascension Day") # President Nkurunziza Day if year >= 2022: - self._add_holiday_jun_8("President Nkurunziza Day") + self._add_observed(self._add_holiday_jun_8("President Nkurunziza Day")) # Independence Day - self._add_holiday_jul_1("Independence Day") + self._add_observed(self._add_holiday_jul_1("Independence Day")) # Assumption Day - self._add_assumption_of_mary_day("Assumption Day") + self._add_observed(self._add_assumption_of_mary_day("Assumption Day")) # Prince Louis Rwagasore Day - self._add_holiday_oct_13("Prince Louis Rwagasore Day") + self._add_observed(self._add_holiday_oct_13("Prince Louis Rwagasore Day")) # President Ndadaye's Day if year >= 1994: - self._add_holiday_oct_21("President Ndadaye's Day") + self._add_observed(self._add_holiday_oct_21("President Ndadaye's Day")) # All Saints' Day - self._add_all_saints_day("All Saints' Day") + self._add_observed(self._add_all_saints_day("All Saints' Day")) # Christmas Day - self._add_christmas_day("Christmas Day") + self._add_observed(self._add_christmas_day("Christmas Day")) # Eid ul Fitr - # date of observance is announced yearly - self._add_eid_al_fitr_day("Eid ul Fitr") + for dt in self._add_eid_al_fitr_day("Eid ul Fitr"): + self._add_observed(dt) # Eid al Adha - # date of observance is announced yearly - self._add_eid_al_adha_day("Eid al Adha") + for dt in self._add_eid_al_adha_day("Eid al Adha"): + self._add_observed(dt) class BI(Burundi): diff --git a/holidays/countries/hongkong.py b/holidays/countries/hongkong.py index d2a055732..e11298ad5 100644 --- a/holidays/countries/hongkong.py +++ b/holidays/countries/hongkong.py @@ -13,12 +13,20 @@ from datetime import timedelta as td from typing import Optional -from holidays.calendars.gregorian import JUL, AUG, SEP, MON, _get_nth_weekday_of_month +from holidays.calendars.gregorian import JUL, AUG, SEP, MON, SUN, _get_nth_weekday_of_month from holidays.groups import ChineseCalendarHolidays, ChristianHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase - - -class HongKong(HolidayBase, ChineseCalendarHolidays, ChristianHolidays, InternationalHolidays): +from holidays.observed_holiday_base import ( + ObservedHolidayBase, + WORKDAY_TO_NEXT_WORKDAY, + MON_TO_NEXT_TUE, + SUN_TO_NEXT_WORKDAY, + SAT_SUN_TO_NEXT_WORKDAY, +) + + +class HongKong( + ObservedHolidayBase, ChineseCalendarHolidays, ChristianHolidays, InternationalHolidays +): """ https://en.wikipedia.org/wiki/Public_holidays_in_Hong_Kong Holidays for 2007–2023 (government source): @@ -26,6 +34,7 @@ class HongKong(HolidayBase, ChineseCalendarHolidays, ChristianHolidays, Internat """ country = "HK" + observed_label = "The day following %s" special_holidays = { 1997: (JUL, 2, "Hong Kong Special Administrative Region Establishment Day"), 2015: ( @@ -42,15 +51,18 @@ def __init__(self, *args, **kwargs): ChineseCalendarHolidays.__init__(self) ChristianHolidays.__init__(self) InternationalHolidays.__init__(self) - super().__init__(*args, **kwargs) - - def _add_holiday(self, name: str, dt: date) -> Optional[date]: - if self.observed and (self._is_sunday(dt) or dt in self): - while self._is_sunday(dt) or dt in self: - dt += td(days=+1) - name = self.tr("The day following %s") % self.tr(name) - - return super()._add_holiday(name, dt) + super().__init__(observed_rule=SUN_TO_NEXT_WORKDAY, *args, **kwargs) + + def _add_holiday(self, name: str, *args) -> Optional[date]: + dt = args if len(args) > 1 else args[0] + dt = dt if isinstance(dt, date) else date(self._year, *dt) + rule = ( + WORKDAY_TO_NEXT_WORKDAY + SAT_SUN_TO_NEXT_WORKDAY + if dt in self + else self._observed_rule + ) + is_obs, dt_observed = self._add_observed(dt, name=name, rule=rule) + return dt_observed if is_obs else super()._add_holiday(name, dt) # type: ignore[arg-type] def _populate(self, year): # Current set of holidays actually valid since 1946 @@ -58,8 +70,7 @@ def _populate(self, year): return None super()._populate(year) - - day_following = "The day following" + self.weekend = {SUN} # The first day of January self._add_new_years_day("The first day of January") @@ -96,7 +107,7 @@ def _populate(self, year): name = "Ching Ming Festival" dt_qingming = self._qingming_date if self.observed and dt_qingming == self._easter_sunday + td(days=+1): - self._add_holiday(f"{day_following} {name}", dt_qingming + td(days=+1)) + self._add_observed(dt_qingming, name=name, rule=MON_TO_NEXT_TUE) elif dt_qingming not in { self._easter_sunday + td(days=-2), self._easter_sunday + td(days=-1), @@ -107,7 +118,7 @@ def _populate(self, year): good_friday = "Good Friday" easter_monday = "Easter Monday" self._add_good_friday(good_friday) - self._add_holy_saturday(f"{day_following} {good_friday}") + self._add_holy_saturday(self.observed_label % good_friday) self._add_easter_monday(easter_monday) if dt_qingming in { diff --git a/holidays/countries/vanuatu.py b/holidays/countries/vanuatu.py index ef0ce5f08..4df9f68af 100644 --- a/holidays/countries/vanuatu.py +++ b/holidays/countries/vanuatu.py @@ -9,15 +9,12 @@ # Website: https://github.com/dr-prodigy/python-holidays # License: MIT (see LICENSE file) -from datetime import date -from datetime import timedelta as td - from holidays.calendars.gregorian import JUL, OCT from holidays.groups import ChristianHolidays, InternationalHolidays -from holidays.holiday_base import HolidayBase +from holidays.observed_holiday_base import ObservedHolidayBase, SUN_TO_NEXT_MON, MON_TO_NEXT_TUE -class Vanuatu(HolidayBase, ChristianHolidays, InternationalHolidays): +class Vanuatu(ObservedHolidayBase, ChristianHolidays, InternationalHolidays): """ https://en.wikipedia.org/wiki/Public_holidays_in_Vanuatu https://www.timeanddate.com/holidays/vanuatu/ @@ -41,11 +38,7 @@ class Vanuatu(HolidayBase, ChristianHolidays, InternationalHolidays): def __init__(self, *args, **kwargs): ChristianHolidays.__init__(self) InternationalHolidays.__init__(self) - super().__init__(*args, **kwargs) - - def _add_observed(self, dt: date) -> None: - if self.observed and self._is_sunday(dt): - self._add_holiday(self.observed_label % self[dt], dt + td(days=+1)) + super().__init__(observed_rule=SUN_TO_NEXT_MON, *args, **kwargs) def _populate(self, year): # On 30 July 1980, Vanuatu gained independence from Britain and France. @@ -95,10 +88,9 @@ def _populate(self, year): self._add_christmas_day("Christmas Day") # Family Day. - name = "Family Day" - dec_26 = self._add_christmas_day_two(name) - if self.observed and self._is_monday(dec_26): - self._add_holiday(self.observed_label % name, dec_26 + td(days=+1)) + self._add_observed( + self._add_christmas_day_two("Family Day"), rule=SUN_TO_NEXT_MON + MON_TO_NEXT_TUE + ) class VU(Vanuatu): diff --git a/holidays/locale/bg/LC_MESSAGES/BG.po b/holidays/locale/bg/LC_MESSAGES/BG.po index 6b377b076..4e77e0142 100644 --- a/holidays/locale/bg/LC_MESSAGES/BG.po +++ b/holidays/locale/bg/LC_MESSAGES/BG.po @@ -66,6 +66,7 @@ msgstr "" msgid "Рождество Христово" msgstr "" +#. %s (Observed). #, c-format msgid "%s (почивен ден)" msgstr "" diff --git a/holidays/locale/en_US/LC_MESSAGES/BG.po b/holidays/locale/en_US/LC_MESSAGES/BG.po index d6735ed45..f40f177ef 100644 --- a/holidays/locale/en_US/LC_MESSAGES/BG.po +++ b/holidays/locale/en_US/LC_MESSAGES/BG.po @@ -66,6 +66,7 @@ msgstr "Christmas Eve" msgid "Рождество Христово" msgstr "Christmas Day" +#. %s (Observed). #, c-format msgid "%s (почивен ден)" msgstr "%s (Observed)" diff --git a/holidays/locale/uk/LC_MESSAGES/BG.po b/holidays/locale/uk/LC_MESSAGES/BG.po index 35ce35832..b62ca5206 100644 --- a/holidays/locale/uk/LC_MESSAGES/BG.po +++ b/holidays/locale/uk/LC_MESSAGES/BG.po @@ -68,6 +68,7 @@ msgstr "Святий вечір" msgid "Рождество Христово" msgstr "Різдво Христове" +#. %s (Observed). #, c-format msgid "%s (почивен ден)" msgstr "%s (вихідний)" diff --git a/tests/countries/test_bulgaria.py b/tests/countries/test_bulgaria.py index 495076b3b..e6f6e5806 100644 --- a/tests/countries/test_bulgaria.py +++ b/tests/countries/test_bulgaria.py @@ -10,7 +10,7 @@ # License: MIT (see LICENSE file) -from holidays.constants import SCHOOL +from holidays.constants import PUBLIC, SCHOOL from holidays.countries.bulgaria import Bulgaria, BG, BLG from tests.common import TestCase @@ -24,7 +24,7 @@ def test_country_aliases(self): self.assertCountryAliases(Bulgaria, BG, BLG) def test_no_holidays(self): - self.assertNoHolidays(Bulgaria(years=1989)) + self.assertNoHolidays(Bulgaria(categories=(PUBLIC, SCHOOL), years=1989)) def test_new_years_day(self): name = "Нова година" diff --git a/tests/countries/test_vanuatu.py b/tests/countries/test_vanuatu.py index 52395b97c..b13377e91 100644 --- a/tests/countries/test_vanuatu.py +++ b/tests/countries/test_vanuatu.py @@ -186,9 +186,12 @@ def test_family_day(self): name = "Family Day" self.assertHolidayName(name, (f"{year}-12-26" for year in range(1981, 2050))) dt = ( + "2004-12-27", "2005-12-27", + "2010-12-27", "2011-12-27", "2016-12-27", + "2021-12-27", "2022-12-27", ) self.assertHolidayName(f"{name} (Observed)", dt) From 5807a7927bdb4e85e74b3b1df0c14612403a9019 Mon Sep 17 00:00:00 2001 From: abh31000 Date: Fri, 15 Sep 2023 17:08:13 +0100 Subject: [PATCH 18/21] Update Algeria holidays: add fr localization (#1467) Co-authored-by: Arkadii Yakovets --- README.rst | 2 +- holidays/countries/algeria.py | 27 +++++----- holidays/locale/ar/LC_MESSAGES/DZ.po | 30 +++++------ holidays/locale/en_US/LC_MESSAGES/DZ.po | 30 +++++------ holidays/locale/fr/LC_MESSAGES/DZ.po | 70 +++++++++++++++++++++++++ tests/countries/test_algeria.py | 17 ++++++ 6 files changed, 130 insertions(+), 46 deletions(-) create mode 100644 holidays/locale/fr/LC_MESSAGES/DZ.po diff --git a/README.rst b/README.rst index 1de0b2817..25ee8bfc8 100644 --- a/README.rst +++ b/README.rst @@ -140,7 +140,7 @@ The list of supported countries, their subdivisions and supported languages * - Algeria - DZ - - - **ar**, en_US + - **ar**, en_US, fr * - American Samoa - AS - Can also be loaded as country US, subdivision AS diff --git a/holidays/countries/algeria.py b/holidays/countries/algeria.py index 0fd29bc9a..458ccbbeb 100644 --- a/holidays/countries/algeria.py +++ b/holidays/countries/algeria.py @@ -25,7 +25,7 @@ class Algeria(HolidayBase, InternationalHolidays, IslamicHolidays): default_language = "ar" # Estimated label. estimated_label = tr("(تقدير*) *%s") - supported_languages = ("ar", "en_US") + supported_languages = ("ar", "en_US", "fr") def __init__(self, *args, **kwargs): InternationalHolidays.__init__(self) @@ -38,43 +38,44 @@ def _populate(self, year): # New Year's Day. self._add_new_years_day(tr("رأس السنة الميلادية")) - # In January 2018, Algeria declared Yennayer a national holiday + # In January 2018, Algeria declared Yennayer a national holiday. if year >= 2018: - # Amazigh New Year / Yennayer + # Amazigh New Year / Yennayer. self._add_holiday_jan_12(tr("رأس السنة الأمازيغية")) - # Labour Day + # Labor Day. self._add_labor_day(tr("عيد العمال")) if year >= 1962: - # Independence Day + # Independence Day. self._add_holiday_jul_5(tr("عيد الإستقلال")) if year >= 1963: - # Revolution Day + # Revolution Day. self._add_holiday_nov_1(tr("عيد الثورة")) - # Islamic New Year + # Islamic New Year. self._add_islamic_new_year_day(tr("رأس السنة الهجرية")) - # Ashura + # Ashura. self._add_ashura_day(tr("عاشورة")) - # Mawlid / Prophet's Birthday + # Mawlid / Prophet's Birthday. self._add_mawlid_day(tr("عيد المولد النبوي")) # As of April 30, 2023. Algeria has 3 days of Eid holidays # (https://www.horizons.dz/english/archives/amp/12021) - # Eid al-Fitr - Feast Festive + + # Eid al-Fitr - Feast Festive. self._add_eid_al_fitr_day(tr("عيد الفطر")) - # Eid al-Fitr Holiday + # Eid al-Fitr Holiday. self._add_eid_al_fitr_day_two(tr("عطلة عيد الفطر")) if year >= 2024: self._add_eid_al_fitr_day_three(tr("عطلة عيد الفطر")) - # Eid al-Adha - Scarfice Festive + # Eid al-Adha - Scarfice Festive. self._add_eid_al_adha_day(tr("عيد الأضحى")) - # Eid al-Adha Holiday + # Eid al-Adha Holiday. self._add_eid_al_adha_day_two(tr("عطلة عيد الأضحى")) if year >= 2023: self._add_eid_al_adha_day_three(tr("عطلة عيد الأضحى")) diff --git a/holidays/locale/ar/LC_MESSAGES/DZ.po b/holidays/locale/ar/LC_MESSAGES/DZ.po index 611cbed56..149019c75 100644 --- a/holidays/locale/ar/LC_MESSAGES/DZ.po +++ b/holidays/locale/ar/LC_MESSAGES/DZ.po @@ -5,9 +5,9 @@ msgid "" msgstr "" "Project-Id-Version: Python Holidays 0.29\n" "POT-Creation-Date: 2023-06-28 00:13+0100\n" -"PO-Revision-Date: 2023-07-05 17:40+0100\n" -"Last-Translator: \n" -"Language-Team: Abdelkhalek Boukli Hacene \n" +"PO-Revision-Date: 2023-09-12 14:56+0100\n" +"Last-Translator: Abdelkhalek Boukli Hacene \n" +"Language-Team: Python Holidays localization team\n" "Language: ar\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -24,48 +24,46 @@ msgstr "" msgid "رأس السنة الميلادية" msgstr "" -#. Amazigh New Year / Yennayer +#. Amazigh New Year / Yennayer. msgid "رأس السنة الأمازيغية" msgstr "" -#. Labour Day +#. Labor Day. msgid "عيد العمال" msgstr "" -#. Independence Day +#. Independence Day. msgid "عيد الإستقلال" msgstr "" -#. Revolution Day +#. Revolution Day. msgid "عيد الثورة" msgstr "" -#. Islamic New Year +#. Islamic New Year. msgid "رأس السنة الهجرية" msgstr "" -#. Ashura +#. Ashura. msgid "عاشورة" msgstr "" -#. Mawlid / Prophet's Birthday +#. Mawlid / Prophet's Birthday. msgid "عيد المولد النبوي" msgstr "" -#. As of April 30, 2023. Algeria has 3 days of Eid holidays -#. (https://www.horizons.dz/english/archives/amp/12021) Eid al-Fitr - Feast -#. Festive +#. Eid al-Fitr - Feast Festive. msgid "عيد الفطر" msgstr "" -#. Eid al-Fitr Holiday +#. Eid al-Fitr Holiday. msgid "عطلة عيد الفطر" msgstr "" -#. Eid al-Adha - Scarfice Festive +#. Eid al-Adha - Scarfice Festive. msgid "عيد الأضحى" msgstr "" -#. Eid al-Adha Holiday +#. Eid al-Adha Holiday. msgid "عطلة عيد الأضحى" msgstr "" diff --git a/holidays/locale/en_US/LC_MESSAGES/DZ.po b/holidays/locale/en_US/LC_MESSAGES/DZ.po index 25fa5231a..b64648f21 100644 --- a/holidays/locale/en_US/LC_MESSAGES/DZ.po +++ b/holidays/locale/en_US/LC_MESSAGES/DZ.po @@ -5,9 +5,9 @@ msgid "" msgstr "" "Project-Id-Version: Python Holidays 0.29\n" "POT-Creation-Date: 2023-06-28 00:13+0100\n" -"PO-Revision-Date: 2023-07-11 03:43+0100\n" -"Last-Translator: \n" -"Language-Team: Abdelkhalek Boukli Hacene \n" +"PO-Revision-Date: 2023-09-12 14:56+0100\n" +"Last-Translator: Abdelkhalek Boukli Hacene \n" +"Language-Team: Python Holidays localization team\n" "Language: en_US\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -25,48 +25,46 @@ msgstr "%s* (*estimated)" msgid "رأس السنة الميلادية" msgstr "New Year's Day" -#. Amazigh New Year / Yennayer +#. Amazigh New Year / Yennayer. msgid "رأس السنة الأمازيغية" msgstr "Amazigh New Year" -#. Labour Day +#. Labor Day. msgid "عيد العمال" msgstr "Labor Day" -#. Independence Day +#. Independence Day. msgid "عيد الإستقلال" msgstr "Independence Day" -#. Revolution Day +#. Revolution Day. msgid "عيد الثورة" msgstr "Revolution Day" -#. Islamic New Year +#. Islamic New Year. msgid "رأس السنة الهجرية" msgstr "Islamic New Year" -#. Ashura +#. Ashura. msgid "عاشورة" msgstr "Ashura" -#. Mawlid / Prophet's Birthday +#. Mawlid / Prophet's Birthday. msgid "عيد المولد النبوي" msgstr "Prophet's Birthday" -#. As of April 30, 2023. Algeria has 3 days of Eid holidays -#. (https://www.horizons.dz/english/archives/amp/12021) Eid al-Fitr - Feast -#. Festive +#. Eid al-Fitr - Feast Festive. msgid "عيد الفطر" msgstr "Eid al-Fitr" -#. Eid al-Fitr Holiday +#. Eid al-Fitr Holiday. msgid "عطلة عيد الفطر" msgstr "Eid al-Fitr Holiday" -#. Eid al-Adha - Scarfice Festive +#. Eid al-Adha - Scarfice Festive. msgid "عيد الأضحى" msgstr "Eid al-Adha" -#. Eid al-Adha Holiday +#. Eid al-Adha Holiday. msgid "عطلة عيد الأضحى" msgstr "Eid al-Adha Holiday" diff --git a/holidays/locale/fr/LC_MESSAGES/DZ.po b/holidays/locale/fr/LC_MESSAGES/DZ.po new file mode 100644 index 000000000..7a9214eaf --- /dev/null +++ b/holidays/locale/fr/LC_MESSAGES/DZ.po @@ -0,0 +1,70 @@ +# Algeria holidays fr localization. +# Authors: Abdelkhalek Boukli Hacene , 2023. +# +msgid "" +msgstr "" +"Project-Id-Version: Python Holidays 0.33\n" +"POT-Creation-Date: 2023-06-28 00:13+0100\n" +"PO-Revision-Date: 2023-09-12 15:08+0100\n" +"Last-Translator: Abdelkhalek Boukli Hacene \n" +"Language-Team: Python Holidays localization team\n" +"Language: fr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"Generated-By: Lingua 4.15.0\n" +"X-Generator: Poedit 3.3.2\n" + +#. Estimated label. +#, c-format +msgid "(تقدير*) *%s" +msgstr "%s* (*estimé)" + +#. New Year's Day. +msgid "رأس السنة الميلادية" +msgstr "Nouvel an" + +#. Amazigh New Year / Yennayer. +msgid "رأس السنة الأمازيغية" +msgstr "Nouvel an Amazigh" + +#. Labor Day. +msgid "عيد العمال" +msgstr "Fête du Travail" + +#. Independence Day. +msgid "عيد الإستقلال" +msgstr "Fête de l'indépendance" + +#. Revolution Day. +msgid "عيد الثورة" +msgstr "Fête de la Révolution" + +#. Islamic New Year. +msgid "رأس السنة الهجرية" +msgstr "Nouvel an musulman" + +#. Ashura. +msgid "عاشورة" +msgstr "Achoura" + +#. Mawlid / Prophet's Birthday. +msgid "عيد المولد النبوي" +msgstr "Anniversaire du prophète" + +#. Eid al-Fitr - Feast Festive. +msgid "عيد الفطر" +msgstr "Fête de la rupture du jeûne" + +#. Eid al-Fitr Holiday. +msgid "عطلة عيد الفطر" +msgstr "Congé de fête de la rupture du jeûne" + +#. Eid al-Adha - Scarfice Festive. +msgid "عيد الأضحى" +msgstr "Fête du sacrifice" + +#. Eid al-Adha Holiday. +msgid "عطلة عيد الأضحى" +msgstr "Congé de fête du sacrifice" diff --git a/tests/countries/test_algeria.py b/tests/countries/test_algeria.py index fcdd3a556..1c9479097 100644 --- a/tests/countries/test_algeria.py +++ b/tests/countries/test_algeria.py @@ -150,3 +150,20 @@ def test_l10n_en_us(self): ("2022-10-08", "Prophet's Birthday* (*estimated)"), ("2022-11-01", "Revolution Day"), ) + + def test_l10n_fr(self): + self.assertLocalizedHolidays( + "fr", + ("2022-01-01", "Nouvel an"), + ("2022-01-12", "Nouvel an Amazigh"), + ("2022-05-01", "Fête du Travail"), + ("2022-05-02", "Fête de la rupture du jeûne* (*estimé)"), + ("2022-05-03", "Congé de fête de la rupture du jeûne* (*estimé)"), + ("2022-07-05", "Fête de l'indépendance"), + ("2022-07-09", "Fête du sacrifice* (*estimé)"), + ("2022-07-10", "Congé de fête du sacrifice* (*estimé)"), + ("2022-07-30", "Nouvel an musulman* (*estimé)"), + ("2022-08-08", "Achoura* (*estimé)"), + ("2022-10-08", "Anniversaire du prophète* (*estimé)"), + ("2022-11-01", "Fête de la Révolution"), + ) From 21f9b1cf343326a09b1a1cff24649239f295f90b Mon Sep 17 00:00:00 2001 From: ~Jhellico Date: Fri, 15 Sep 2023 19:08:32 +0300 Subject: [PATCH 19/21] Fix README.rst (#1472) --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 25ee8bfc8..ffddb6cdb 100644 --- a/README.rst +++ b/README.rst @@ -45,7 +45,7 @@ Install The latest stable version can always be installed or updated via pip: -.. code-block:: bash +.. code-block:: shell $ pip install --upgrade holidays @@ -705,7 +705,7 @@ Beta Version The latest development (beta) version can be installed directly from GitHub: -.. code-block:: bash +.. code-block:: shell $ pip install --upgrade https://github.com/vacanza/python-holidays/tarball/beta From 00935bfaccb2818faec8940950579440793cf87b Mon Sep 17 00:00:00 2001 From: Arkadii Yakovets Date: Fri, 15 Sep 2023 09:08:44 -0700 Subject: [PATCH 20/21] Tune pre-commit auto-update workflow (#1473) --- .github/workflows/pre-commit-autoupdate.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/pre-commit-autoupdate.yml b/.github/workflows/pre-commit-autoupdate.yml index f31f370ce..07c67ff89 100644 --- a/.github/workflows/pre-commit-autoupdate.yml +++ b/.github/workflows/pre-commit-autoupdate.yml @@ -1,4 +1,4 @@ -name: Chores +name: Update pre-commit hooks on: schedule: @@ -16,13 +16,14 @@ jobs: steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v4.7.0 + with: + python-version: "3.11" - uses: browniebroke/pre-commit-autoupdate-action@v1.0.0 - uses: peter-evans/create-pull-request@v5.0.2 with: - author: github-actions[bot] base: beta - body: Update versions of pre-commit hooks to the latest version. - branch: update/pre-commit-hooks + body: Update pre-commit hooks to their latest versions. + branch: update-pre-commit-hooks commit-message: "Chore: Update pre-commit hooks" committer: github-actions[bot] delete-branch: true From 082a8b3de41429d2338f62dea6d0157a014ecdf9 Mon Sep 17 00:00:00 2001 From: ~Jhellico Date: Mon, 18 Sep 2023 19:22:54 +0300 Subject: [PATCH 21/21] Finalize v0.33 --- CHANGES | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 2bfa44803..a900a29b4 100644 --- a/CHANGES +++ b/CHANGES @@ -1,7 +1,25 @@ Version 0.33 ============ -Released ???????? ??, ???? +Released September 18, 2023 + +- Add merge queue support (#1464 by @arkid15r) +- Introduce ObservedHolidays class (#1444 by @KJhellico, @arkid15r) +- Update Algeria holidays: add fr localization (#1467 by @abh31000) +- Fix README.rst (#1472 by @KJhellico) +- Update Belgium holidays: add bank holidays (#1457 by @KJhellico) +- Update Brazil holidays: specify optional holidays (#1452 by @KJhellico) +- Update Canada holidays (#1448 by @KJhellico) +- Update Liechtenstein holidays: specify bank holidays (#1462 by @KJhellico) +- Update PR template (#1461 by @arkid15r) +- Update Thailand holidays: add holiday categories (#1346 by @PPsyrius, @arkid15r, @KJhellico) +- Update United Kingdom holidays (#1454 by @KJhellico) +- Update documentation: add language usage example to examples.rst (#1456 by @jovana, @arkid15r, @KJhellico) +- Update l10n files: fix ar, en_US headers (#1468 by @abh31000) +- Update pre commit automatic update workflow (#1469 by @arkid15r) +- Update skipIf rules for heavy tests (#1460 by @arkid15r) +- Migrate remaining countries to ObservedHolidayBase (#1463 by @KJhellico) +- Tune pre-commit auto-update workflow (#1473 by @arkid15r) Version 0.32 ============