Repository: vacanza/python-holidays

Issue:
Add Bahamas holidays
Useful links:
  - https://en.wikipedia.org/wiki/Public_holidays_in_the_Bahamas
  - https://www.timeanddate.com/holidays/bahamas/


Discussion:
Ironically enough, none of the government site got usable data except for https://www.bahamashclondon.net/consular-information/public-holidays/, but that was for 2020.

With some random resort got better formatted sources (but still only on par to Wikipedia): https://tainobeach.com/amenities/grand-bahama-island/holidays/

Edit: nvm, found it - thank god ILO source exists: https://www.ilo.org/dyn/travail/docs/1204/PublicHolidaysAct_1.pdf

Resolving patch:
diff --git a/holidays/countries/__init__.py b/holidays/countries/__init__.py
--- a/holidays/countries/__init__.py
+++ b/holidays/countries/__init__.py
@@ -22,6 +22,7 @@
 from .australia import Australia, AU, AUS
 from .austria import Austria, AT, AUT
 from .azerbaijan import Azerbaijan, AZ, AZE
+from .bahamas import Bahamas, BS, BHS
 from .bahrain import Bahrain, BH, BAH
 from .bangladesh import Bangladesh, BD, BGD
 from .barbados import Barbados, BB, BRB
diff --git a/holidays/countries/bahamas.py b/holidays/countries/bahamas.py
new file mode 100644
--- /dev/null
+++ b/holidays/countries/bahamas.py
@@ -0,0 +1,135 @@
+#  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.calendars.gregorian import SEP
+from holidays.groups import ChristianHolidays, InternationalHolidays, StaticHolidays
+from holidays.observed_holiday_base import (
+    ObservedHolidayBase,
+    TUE_TO_PREV_MON,
+    WED_THU_TO_NEXT_FRI,
+    SAT_SUN_TO_NEXT_MON,
+    SUN_TO_NEXT_MON,
+    SUN_TO_NEXT_TUE,
+)
+
+
+class Bahamas(ObservedHolidayBase, ChristianHolidays, InternationalHolidays, StaticHolidays):
+    """
+    References:
+      - https://en.wikipedia.org/wiki/Public_holidays_in_the_Bahamas
+      - https://laws.bahamas.gov.bs/cms/images/LEGISLATION/PRINCIPAL/1938/1938-0015/PublicHolidaysAct_1.pdf  # noqa: E501
+      - https://laws.bahamas.gov.bs/cms/images/LEGISLATION/PRINCIPAL/2013/2013-0040/MajorityRulePublicHolidayAct2013_1.pdf  # noqa: E501
+      - https://laws.bahamas.gov.bs/cms/images/LEGISLATION/PRINCIPAL/2013/2013-0009/RandolFawkesLabourDayAct2013_1.pdf  # noqa: E501
+      - http://www.tribune242.com/news/2013/oct/12/national-heroes-day-formally-established/
+      - https://eleutheranews.com/?p=3594
+    Checked With:
+      - https://www.bahamashclondon.net/consular-information/public-holidays/
+      - https://bisxbahamas.com/wp-content/uploads/2020/12/Trading-Calendar-2021.pdf
+      - https://publicholidays.la/the-bahamas/2022-dates/  # Official source no longer accessible
+    """
+
+    country = "BS"
+    observed_label = "%s (Observed)"
+
+    def __init__(self, *args, **kwargs):
+        ChristianHolidays.__init__(self)
+        InternationalHolidays.__init__(self)
+        StaticHolidays.__init__(self, BahamasStaticHolidays)
+        super().__init__(observed_rule=SAT_SUN_TO_NEXT_MON, *args, **kwargs)
+
+    def _populate_public_holidays(self):
+        # Gained Independence on Jul 10, 1973.
+        if self._year <= 1973:
+            return None
+
+        # New Year's Day.
+        # Pre-2012 Observance:
+        # - If TUE, New Year's Day (Observed) prev MON.
+        # - If WED or THU, New Year's Day (Observed) next FRI.
+        # 2012 and beyond Observance: If SUN, New Year's Day (Observed) next MON (not for SAT).
+        self._add_observed(
+            self._add_new_years_day("New Year's Day"),
+            rule=SUN_TO_NEXT_MON
+            if self._year >= 2012
+            else SAT_SUN_TO_NEXT_MON + TUE_TO_PREV_MON + WED_THU_TO_NEXT_FRI,
+        )
+
+        # Majority Rule Day.
+        # Officially made a holiday on Oct 11, 2013 under Majority Rule (Public Holiday) Act 2013.
+        if self._year >= 2014:
+            self._add_observed(self._add_holiday_jan_10("Majority Rule Day"))
+
+        # Good Friday.
+        self._add_good_friday("Good Friday")
+
+        # Easter Monday.
+        self._add_easter_monday("Easter Monday")
+
+        # Whit Monday.
+        self._add_whit_monday("Whit Monday")
+
+        # Randol Fawkes Labour Day.
+        # This was simply known as "Labour Day" prior to Randol Fawkes Labour Day Act 2013.
+        self._add_holiday_1st_fri_of_jun(
+            "Randol Fawkes Labour Day" if self._year >= 2013 else "Labour Day"
+        )
+
+        # Independence Day.
+        self._add_observed(self._add_holiday_jul_10("Independence Day"))
+
+        # Emancipation Day.
+        self._add_holiday_1st_mon_of_aug("Emancipation Day")
+
+        # National Heroes Day.
+        # Known as "Discovery Day" prior to 2013, with its date fixed as Oct 12 annually.
+        # Got its name changed on Oct 11, 2013 under Majority Rule (Public Holiday) Act 2013.
+        # Pre-2013 Observance:
+        # - If TUE, Discovery Day (Observed) prev MON.
+        # - If WED or THU, Discovery Day (Observed) next FRI.
+        if self._year >= 2013:
+            self._add_holiday_2nd_mon_of_oct("National Heroes Day")
+        else:
+            self._add_observed(
+                self._add_columbus_day("Discovery Day"),
+                rule=SAT_SUN_TO_NEXT_MON + TUE_TO_PREV_MON + WED_THU_TO_NEXT_FRI,
+            )
+
+        # Christmas Holidays Exception Rules.
+        # Observance Exception:
+        # FRI-SAT -> Boxing Day (Observed) next MON.
+        # SAT-SUN -> Boxing Day (Observed) next MON.
+        # SUN-MON -> Christmas Day (Observed) next TUE.
+
+        # Christmas Day.
+        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"))
+
+        # New Year's Day observance overflow.
+        # This only applies to Pre-2012 observance.
+        if self.observed and self._year in {1979, 1984, 1990, 2001, 2007}:
+            self._add_new_years_eve("New Year's Day (Observed)")
+
+
+class BS(Bahamas):
+    pass
+
+
+class BHS(Bahamas):
+    pass
+
+
+class BahamasStaticHolidays:
+    special_public_holidays = {
+        # https://www.bahamas.gov.bs/wps/portal/public/gov/government/notices/national%20holiday%2019th%20september/  # noqa: E501
+        2022: (SEP, 19, "State Funeral of Queen Elizabeth II"),
+    }
diff --git a/holidays/registry.py b/holidays/registry.py
--- a/holidays/registry.py
+++ b/holidays/registry.py
@@ -28,6 +28,7 @@
     "australia": ("Australia", "AU", "AUS"),
     "austria": ("Austria", "AT", "AUT"),
     "azerbaijan": ("Azerbaijan", "AZ", "AZE"),
+    "bahamas": ("Bahamas", "BS", "BHS"),
     "bahrain": ("Bahrain", "BH", "BAH"),
     "bangladesh": ("Bangladesh", "BD", "BGD"),
     "barbados": ("Barbados", "BB", "BRB"),


Test patch:
diff --git a/tests/countries/test_bahamas.py b/tests/countries/test_bahamas.py
new file mode 100644
--- /dev/null
+++ b/tests/countries/test_bahamas.py
@@ -0,0 +1,169 @@
+#  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.bahamas import Bahamas, BS, BHS
+from tests.common import TestCase
+
+
+class TestBahamas(TestCase):
+    @classmethod
+    def setUpClass(cls):
+        super().setUpClass(Bahamas, years=range(1974, 2050), years_non_observed=range(1974, 2050))
+
+    def test_country_aliases(self):
+        self.assertCountryAliases(Bahamas, BS, BHS)
+
+    def test_no_holidays(self):
+        self.assertNoHolidays(Bahamas(years=1973))
+
+    def test_special_public_holidays(self):
+        self.assertHoliday("2022-09-19")
+
+    def test_special_observance_2012_and_prior(self):
+        self.assertNoNonObservedHoliday(
+            # Cases of New Year's Day observance overflow into prev year
+            "1979-12-31",
+            "1984-12-31",
+            "1990-12-31",
+            "2001-12-31",
+            "2007-12-31",
+            # New Year's Day
+            "2003-01-03",
+            "2004-01-02",
+            "2009-01-02",
+            "2011-01-03",
+            # Discovery Day
+            "2000-10-13",
+            "2004-10-11",
+            "2005-10-14",
+            "2006-10-13",
+            "2010-10-11",
+            "2011-10-14",
+        )
+
+    def test_2012(self):
+        self.assertHolidays(
+            Bahamas(years=2012),
+            ("2012-01-01", "New Year's Day"),
+            ("2012-01-02", "New Year's Day (Observed)"),
+            ("2012-04-06", "Good Friday"),
+            ("2012-04-09", "Easter Monday"),
+            ("2012-05-28", "Whit Monday"),
+            ("2012-06-01", "Labour Day"),
+            ("2012-07-10", "Independence Day"),
+            ("2012-08-06", "Emancipation Day"),
+            ("2012-10-12", "Discovery Day"),
+            ("2012-12-25", "Christmas Day"),
+            ("2012-12-26", "Boxing Day"),
+        )
+
+    def test_2013(self):
+        self.assertHolidays(
+            Bahamas(years=2013),
+            ("2013-01-01", "New Year's Day"),
+            ("2013-03-29", "Good Friday"),
+            ("2013-04-01", "Easter Monday"),
+            ("2013-05-20", "Whit Monday"),
+            ("2013-06-07", "Randol Fawkes Labour Day"),
+            ("2013-07-10", "Independence Day"),
+            ("2013-08-05", "Emancipation Day"),
+            ("2013-10-14", "National Heroes Day"),
+            ("2013-12-25", "Christmas Day"),
+            ("2013-12-26", "Boxing Day"),
+        )
+
+    def test_2014(self):
+        self.assertHolidays(
+            Bahamas(years=2014),
+            ("2014-01-01", "New Year's Day"),
+            ("2014-01-10", "Majority Rule Day"),
+            ("2014-04-18", "Good Friday"),
+            ("2014-04-21", "Easter Monday"),
+            ("2014-06-06", "Randol Fawkes Labour Day"),
+            ("2014-06-09", "Whit Monday"),
+            ("2014-07-10", "Independence Day"),
+            ("2014-08-04", "Emancipation Day"),
+            ("2014-10-13", "National Heroes Day"),
+            ("2014-12-25", "Christmas Day"),
+            ("2014-12-26", "Boxing Day"),
+        )
+
+    def test_2020(self):
+        self.assertHolidays(
+            Bahamas(years=2020),
+            ("2020-01-01", "New Year's Day"),
+            ("2020-01-10", "Majority Rule Day"),
+            ("2020-04-10", "Good Friday"),
+            ("2020-04-13", "Easter Monday"),
+            ("2020-06-01", "Whit Monday"),
+            ("2020-06-05", "Randol Fawkes Labour Day"),
+            ("2020-07-10", "Independence Day"),
+            ("2020-08-03", "Emancipation Day"),
+            ("2020-10-12", "National Heroes Day"),
+            ("2020-12-25", "Christmas Day"),
+            ("2020-12-26", "Boxing Day"),
+            ("2020-12-28", "Boxing Day (Observed)"),
+        )
+
+    def test_2021(self):
+        self.assertHolidays(
+            Bahamas(years=2021),
+            ("2021-01-01", "New Year's Day"),
+            ("2021-01-10", "Majority Rule Day"),
+            ("2021-01-11", "Majority Rule Day (Observed)"),
+            ("2021-04-02", "Good Friday"),
+            ("2021-04-05", "Easter Monday"),
+            ("2021-05-24", "Whit Monday"),
+            ("2021-06-04", "Randol Fawkes Labour Day"),
+            ("2021-07-10", "Independence Day"),
+            ("2021-07-12", "Independence Day (Observed)"),
+            ("2021-08-02", "Emancipation Day"),
+            ("2021-10-11", "National Heroes Day"),
+            ("2021-12-25", "Christmas Day"),
+            ("2021-12-26", "Boxing Day"),
+            ("2021-12-27", "Boxing Day (Observed)"),
+        )
+
+    def test_2022(self):
+        self.assertHolidays(
+            Bahamas(years=2022),
+            ("2022-01-01", "New Year's Day"),
+            ("2022-01-10", "Majority Rule Day"),
+            ("2022-04-15", "Good Friday"),
+            ("2022-04-18", "Easter Monday"),
+            ("2022-06-03", "Randol Fawkes Labour Day"),
+            ("2022-06-06", "Whit Monday"),
+            ("2022-07-10", "Independence Day"),
+            ("2022-07-11", "Independence Day (Observed)"),
+            ("2022-08-01", "Emancipation Day"),
+            ("2022-09-19", "State Funeral of Queen Elizabeth II"),
+            ("2022-10-10", "National Heroes Day"),
+            ("2022-12-25", "Christmas Day"),
+            ("2022-12-26", "Boxing Day"),
+            ("2022-12-27", "Christmas Day (Observed)"),
+        )
+
+    def test_2023(self):
+        self.assertHolidays(
+            Bahamas(years=2023),
+            ("2023-01-01", "New Year's Day"),
+            ("2023-01-02", "New Year's Day (Observed)"),
+            ("2023-01-10", "Majority Rule Day"),
+            ("2023-04-07", "Good Friday"),
+            ("2023-04-10", "Easter Monday"),
+            ("2023-05-29", "Whit Monday"),
+            ("2023-06-02", "Randol Fawkes Labour Day"),
+            ("2023-07-10", "Independence Day"),
+            ("2023-08-07", "Emancipation Day"),
+            ("2023-10-09", "National Heroes Day"),
+            ("2023-12-25", "Christmas Day"),
+            ("2023-12-26", "Boxing Day"),
+        )