Repository: vacanza/python-holidays

Issue:
Add Belize holidays
Useful links:
  - https://en.wikipedia.org/wiki/Public_holidays_in_Belize
  - https://www.pressoffice.gov.bz/public-and-bank-holidays-2022-updated/
  - https://www.timeanddate.com/holidays/belize/


Discussion:

Resolving patch:
diff --git a/holidays/countries/__init__.py b/holidays/countries/__init__.py
--- a/holidays/countries/__init__.py
+++ b/holidays/countries/__init__.py
@@ -24,6 +24,7 @@
 from .bangladesh import Bangladesh, BD, BGD
 from .belarus import Belarus, BY, BLR
 from .belgium import Belgium, BE, BEL
+from .belize import Belize, BZ, BLZ
 from .bolivia import Bolivia, BO, BOL
 from .bosnia_and_herzegovina import BosniaAndHerzegovina, BA, BIH
 from .botswana import Botswana, BW, BWA
diff --git a/holidays/countries/belize.py b/holidays/countries/belize.py
new file mode 100644
--- /dev/null
+++ b/holidays/countries/belize.py
@@ -0,0 +1,140 @@
+#  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 <dr.prodigy.github@gmail.com> (c) 2017-2023
+#           ryanss <ryanssdev@icloud.com> (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 Optional
+
+from holidays.calendars import _get_nth_weekday_from
+from holidays.constants import JAN, MAR, MAY, AUG, SEP, OCT, NOV, MON
+from holidays.holiday_base import HolidayBase
+from holidays.holiday_groups import ChristianHolidays, InternationalHolidays
+
+
+class Belize(HolidayBase, ChristianHolidays, InternationalHolidays):
+    """
+    References:
+      - https://en.wikipedia.org/wiki/Public_holidays_in_Belize
+      - http://www.belizelaw.org/web/lawadmin/PDF%20files/cap289.pdf
+      - https://www.pressoffice.gov.bz/public-and-bank-holidays-2022-updated/
+      - https://www.pressoffice.gov.bz/government-of-belize-establishes-new-public-and-bank-holidays/  # noqa: E501
+    """
+
+    country = "BZ"
+
+    def __init__(self, *args, **kwargs) -> None:
+        ChristianHolidays.__init__(self)
+        InternationalHolidays.__init__(self)
+        super().__init__(*args, **kwargs)
+
+    def _add_movable_holiday(self, *args) -> 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
+        name, dt = self._parse_holiday(*args)
+        if self.observed:
+            if self._is_friday(dt) or self._is_sunday(dt):
+                dt = _get_nth_weekday_from(1, MON, dt)
+                name = "%s (Observed)" % name
+            elif (
+                self._is_tuesday(dt)
+                or self._is_wednesday(dt)
+                or self._is_thursday(dt)
+            ):
+                dt = _get_nth_weekday_from(-1, MON, dt)
+                name = "%s (Observed)" % name
+        self._add_holiday(name, dt)
+
+    def _add_holiday(self, *args) -> Optional[date]:
+        # Chapter 289 of the laws of Belize states that if the holiday falls
+        # on a Sunday, the following Monday is observed as public holiday
+        name, dt = self._parse_holiday(*args)
+        if (
+            self.observed
+            and self._is_sunday(dt)
+            and dt + td(days=+1) not in self
+        ):
+            name = "%s (Observed)" % name
+            dt += td(days=+1)
+        return super()._add_holiday(name, dt)
+
+    def _populate(self, year: int) -> None:
+        # Belize was granted independence on 21.09.1981.
+        if year <= 1981:
+            return None
+        super()._populate(year)
+
+        # New Year's Day.
+        self._add_new_years_day("New Year's Day")
+
+        if year >= 2021:
+            # George Price Day.
+            self._add_holiday("George Price Day", JAN, 15)
+
+        # National Heroes and Benefactors Day.
+        self._add_movable_holiday(
+            "National Heroes and Benefactors Day", MAR, 9
+        )
+
+        # Good Friday.
+        self._add_good_friday("Good Friday")
+
+        # Holy Saturday.
+        self._add_holy_saturday("Holy Saturday")
+
+        # Easter Monday.
+        self._add_easter_monday("Easter Monday")
+
+        # Labour Day.
+        self._add_labor_day("Labour Day")
+
+        if year <= 2021:
+            # Commonwealth Day.
+            self._add_movable_holiday("Commonwealth Day", MAY, 24)
+
+        if year >= 2021:
+            # Emancipation Day.
+            self._add_movable_holiday("Emancipation Day", AUG, 1)
+
+        # Saint George's Caye Day.
+        self._add_holiday("Saint George's Caye Day", SEP, 10)
+
+        # Independence Day.
+        self._add_holiday("Independence Day", SEP, 21)
+
+        # Indigenous Peoples' Resistance Day / Pan American Day.
+        self._add_movable_holiday(
+            "Indigenous Peoples' Resistance Day"
+            if year >= 2021
+            else "Pan American Day",
+            date(year, OCT, 12),
+        )
+
+        # Garifuna Settlement Day.
+        self._add_holiday("Garifuna Settlement Day", NOV, 19)
+
+        # Populated before Christmas for right obvserved Christmas calculation
+        # (if Christmas falls on Sunday, there should be no observed Christmas
+        # on Monday)
+        # Boxing Day.
+        self._add_christmas_day_two("Boxing Day")
+
+        # Christmas Day.
+        self._add_christmas_day("Christmas Day")
+
+
+class BZ(Belize):
+    pass
+
+
+class BLZ(Belize):
+    pass
diff --git a/holidays/registry.py b/holidays/registry.py
--- a/holidays/registry.py
+++ b/holidays/registry.py
@@ -32,6 +32,7 @@
     "bangladesh": ("Bangladesh", "BD", "BGD"),
     "belarus": ("Belarus", "BY", "BLR"),
     "belgium": ("Belgium", "BE", "BEL"),
+    "belize": ("Belize", "BZ", "BLZ"),
     "bolivia": ("Bolivia", "BO", "BOL"),
     "bosnia_and_herzegovina": ("BosniaAndHerzegovina", "BA", "BIH"),
     "botswana": ("Botswana", "BW", "BWA"),


Test patch:
diff --git a/tests/countries/test_belize.py b/tests/countries/test_belize.py
new file mode 100644
--- /dev/null
+++ b/tests/countries/test_belize.py
@@ -0,0 +1,346 @@
+#  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 <dr.prodigy.github@gmail.com> (c) 2017-2023
+#           ryanss <ryanssdev@icloud.com> (c) 2014-2017
+#  Website: https://github.com/dr-prodigy/python-holidays
+#  License: MIT (see LICENSE file)
+
+from holidays.countries.belize import Belize, BZ, BLZ
+from tests.common import TestCase
+
+
+class TestBelize(TestCase):
+    @classmethod
+    def setUpClass(cls):
+        super().setUpClass(
+            Belize,
+            years=range(1982, 2050),
+            years_non_observed=range(1982, 2050),
+        )
+
+    def test_country_aliases(self):
+        self.assertCountryAliases(Belize, BZ, BLZ)
+
+    def test_no_holidays(self):
+        self.assertNoHolidays(Belize(years=1981))
+
+    def test_new_years_day(self):
+        name = "New Year's Day"
+        years_observed = {
+            1984,
+            1989,
+            1995,
+            2006,
+            2012,
+            2017,
+            2023,
+            2034,
+            2040,
+            2045,
+        }
+        self.assertHolidaysName(
+            name,
+            (
+                f"{year}-01-01"
+                for year in set(range(1982, 2050)).difference(years_observed)
+            ),
+        )
+        self.assertHolidaysName(
+            f"{name} (Observed)", (f"{year}-01-02" for year in years_observed)
+        )
+
+    def test_george_price_day(self):
+        name = "George Price Day"
+        years_observed = {2023, 2034, 2040, 2045}
+        self.assertHolidaysName(
+            name,
+            (
+                f"{year}-01-15"
+                for year in set(range(2021, 2050)).difference(years_observed)
+            ),
+        )
+        self.assertHolidaysName(
+            f"{name} (Observed)", (f"{year}-01-16" for year in years_observed)
+        )
+        self.assertNoHoliday(f"{year}-01-15" for year in range(1982, 2021))
+        self.assertNoHolidayNameInYears(name, range(1982, 2021))
+
+    def test_national_heroes_and_benefactors_day(self):
+        name = "National Heroes and Benefactors Day"
+        self.assertHolidaysName(
+            f"{name} (Observed)",
+            "1982-03-08",
+            "1983-03-07",
+            "1984-03-12",
+            "1993-03-08",
+            "2000-03-06",
+            "2010-03-08",
+            "2018-03-12",
+            "2021-03-08",
+            "2022-03-07",
+            "2023-03-06",
+        )
+        self.assertNonObservedHolidaysName(
+            name, (f"{year}-03-09" for year in range(1982, 2050))
+        )
+
+    def test_easter_holidays(self):
+        self.assertHolidaysName(
+            "Good Friday",
+            "2019-04-19",
+            "2020-04-10",
+            "2021-04-02",
+            "2022-04-15",
+            "2023-04-07",
+        )
+
+        self.assertHolidaysName(
+            "Holy Saturday",
+            "2019-04-20",
+            "2020-04-11",
+            "2021-04-03",
+            "2022-04-16",
+            "2023-04-08",
+        )
+
+        self.assertHolidayName(
+            "Easter Monday",
+            "2019-04-22",
+            "2020-04-13",
+            "2021-04-05",
+            "2022-04-18",
+            "2023-04-10",
+        )
+
+    def test_labour_day(self):
+        name = "Labour Day"
+        years_observed = {
+            1983,
+            1988,
+            1994,
+            2005,
+            2011,
+            2016,
+            2022,
+            2033,
+            2039,
+            2044,
+        }
+        self.assertHolidaysName(
+            name,
+            (
+                f"{year}-05-01"
+                for year in set(range(1982, 2050)).difference(years_observed)
+            ),
+        )
+        self.assertHolidaysName(
+            f"{name} (Observed)", (f"{year}-05-02" for year in years_observed)
+        )
+
+    def test_commonwealth_day(self):
+        name = "Commonwealth Day"
+        self.assertHolidaysName(
+            f"{name} (Observed)",
+            "1983-05-23",
+            "1984-05-21",
+            "1992-05-25",
+            "2000-05-22",
+            "2011-05-23",
+            "2018-05-21",
+            "2019-05-27",
+            "2020-05-25",
+        )
+        self.assertNonObservedHolidaysName(
+            name, (f"{year}-05-24" for year in range(1982, 2022))
+        )
+        self.assertNoHolidayNameInYears(name, range(2022, 2050))
+
+    def test_emancipation_day(self):
+        name = "Emancipation Day"
+        self.assertHolidaysName(
+            f"{name} (Observed)",
+            "2021-08-02",
+            "2023-07-31",
+            "2024-07-29",
+            "2025-08-04",
+        )
+        self.assertNonObservedHolidaysName(
+            name, (f"{year}-08-01" for year in range(2021, 2050))
+        )
+        self.assertNoHolidayNameInYears(name, range(1982, 2021))
+
+    def test_saint_georges_caye_day(self):
+        name = "Saint George's Caye Day"
+        years_observed = {
+            1989,
+            1995,
+            2000,
+            2006,
+            2017,
+            2023,
+            2028,
+            2034,
+            2045,
+        }
+        self.assertHolidaysName(
+            name,
+            (
+                f"{year}-09-10"
+                for year in set(range(1982, 2050)).difference(years_observed)
+            ),
+        )
+        self.assertHolidaysName(
+            f"{name} (Observed)", (f"{year}-09-11" for year in years_observed)
+        )
+
+    def test_independence_day(self):
+        name = "Independence Day"
+        years_observed = {
+            1986,
+            1997,
+            2003,
+            2008,
+            2014,
+            2025,
+            2031,
+            2036,
+            2042,
+        }
+        self.assertHolidaysName(
+            name,
+            (
+                f"{year}-09-21"
+                for year in set(range(1982, 2050)).difference(years_observed)
+            ),
+        )
+        self.assertHolidaysName(
+            f"{name} (Observed)", (f"{year}-09-22" for year in years_observed)
+        )
+
+    def test_indigenous_peoples_resistance_day(self):
+        name1 = "Pan American Day"
+        name2 = "Indigenous Peoples' Resistance Day"
+        self.assertHolidaysName(
+            f"{name1} (Observed)",
+            "1982-10-11",
+            "1983-10-10",
+            "1993-10-11",
+            "2000-10-09",
+            "2011-10-10",
+            "2018-10-15",
+        )
+        self.assertHolidaysName(
+            f"{name2} (Observed)",
+            "2021-10-11",
+            "2022-10-10",
+            "2023-10-09",
+            "2025-10-13",
+            "2027-10-11",
+        )
+        self.assertNonObservedHolidaysName(
+            name1, (f"{year}-10-12" for year in range(1982, 2021))
+        )
+        self.assertNonObservedHolidaysName(
+            name2, (f"{year}-10-12" for year in range(2021, 2050))
+        )
+        self.assertNoHolidayNameInYears(name1, range(2021, 2050))
+        self.assertNoHolidayNameInYears(name2, range(1982, 2021))
+
+    def test_garifuna_settlement_day(self):
+        name = "Garifuna Settlement Day"
+        years_observed = {
+            1989,
+            1995,
+            2000,
+            2006,
+            2017,
+            2023,
+            2028,
+            2034,
+            2045,
+        }
+        self.assertHolidaysName(
+            name,
+            (
+                f"{year}-11-19"
+                for year in set(range(1982, 2050)).difference(years_observed)
+            ),
+        )
+        self.assertHolidaysName(
+            f"{name} (Observed)", (f"{year}-11-20" for year in years_observed)
+        )
+
+    def test_christmas_day(self):
+        self.assertHolidaysName(
+            "Christmas Day", (f"{year}-12-25" for year in range(1982, 2050))
+        )
+
+    def test_boxing_day(self):
+        name = "Boxing Day"
+        years_observed = {
+            1982,
+            1993,
+            1999,
+            2004,
+            2010,
+            2021,
+            2027,
+            2032,
+            2038,
+            2049,
+        }
+        self.assertHolidaysName(
+            name,
+            (
+                f"{year}-12-26"
+                for year in set(range(1982, 2050)).difference(years_observed)
+            ),
+        )
+        self.assertHolidaysName(
+            f"{name} (Observed)", (f"{year}-12-27" for year in years_observed)
+        )
+
+    def test_2021(self):
+        # https://www.pressoffice.gov.bz/public-and-bank-holidays-2021-3/
+        self.assertHolidays(
+            Belize(years=2021),
+            ("2021-01-01", "New Year's Day"),
+            ("2021-01-15", "George Price Day"),
+            ("2021-03-08", "National Heroes and Benefactors Day (Observed)"),
+            ("2021-04-02", "Good Friday"),
+            ("2021-04-03", "Holy Saturday"),
+            ("2021-04-05", "Easter Monday"),
+            ("2021-05-01", "Labour Day"),
+            ("2021-05-24", "Commonwealth Day"),
+            ("2021-08-02", "Emancipation Day (Observed)"),
+            ("2021-09-10", "Saint George's Caye Day"),
+            ("2021-09-21", "Independence Day"),
+            ("2021-10-11", "Indigenous Peoples' Resistance Day (Observed)"),
+            ("2021-11-19", "Garifuna Settlement Day"),
+            ("2021-12-25", "Christmas Day"),
+            ("2021-12-27", "Boxing Day (Observed)"),
+        )
+
+    def test_2022(self):
+        # https://www.pressoffice.gov.bz/public-and-bank-holidays-2022-updated/
+        self.assertHolidays(
+            Belize(years=2022),
+            ("2022-01-01", "New Year's Day"),
+            ("2022-01-15", "George Price Day"),
+            ("2022-03-07", "National Heroes and Benefactors Day (Observed)"),
+            ("2022-04-15", "Good Friday"),
+            ("2022-04-16", "Holy Saturday"),
+            ("2022-04-18", "Easter Monday"),
+            ("2022-05-02", "Labour Day (Observed)"),
+            ("2022-08-01", "Emancipation Day"),
+            ("2022-09-10", "Saint George's Caye Day"),
+            ("2022-09-21", "Independence Day"),
+            ("2022-10-10", "Indigenous Peoples' Resistance Day (Observed)"),
+            ("2022-11-19", "Garifuna Settlement Day"),
+            ("2022-12-25", "Christmas Day"),
+            ("2022-12-26", "Boxing Day"),
+        )