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)