Repository: dr-prodigy/python-holidays

Issue:
ADD Holidays for Bosnia and Hertzegovina
Please can you add holidays for BiH Bosnia and Hertzegovina ?

Discussion:

Patch:
diff --git a/README.rst b/README.rst
index ebdca91d..3888be90 100644
--- a/README.rst
+++ b/README.rst
@@ -148,6 +148,10 @@ following countries and their subdivisions are available:
    * - Bolivia
      - BO
      - Departments: B, C, H, L, N, O, P, S, T
+   * - Bosnia and Herzegovina
+     - BA
+     - BIH
+     - Departments: FBiH, RS, BD
    * - Botswana
      - BW
      - None
diff --git a/holidays/countries/__init__.py b/holidays/countries/__init__.py
index 672995d5..9e689115 100644
--- a/holidays/countries/__init__.py
+++ b/holidays/countries/__init__.py
@@ -19,6 +19,7 @@ from .bangladesh import BD, BGD, Bangladesh
 from .belarus import BLR, BY, Belarus
 from .belgium import BE, BEL, Belgium
 from .bolivia import BO, BOL, Bolivia
+from .bosnia_and_herzegovina import BosniaAndHerzegovina, BA, BIH
 from .botswana import BW, BWA, Botswana
 from .brazil import BR, BRA, Brazil
 from .bulgaria import BG, BLG, Bulgaria
diff --git a/holidays/countries/bosnia_and_herzegovina.py b/holidays/countries/bosnia_and_herzegovina.py
new file mode 100644
index 00000000..39581bcf
--- /dev/null
+++ b/holidays/countries/bosnia_and_herzegovina.py
@@ -0,0 +1,149 @@
+#  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 <maurizio.montel@gmail.com> (c) 2017-2022
+#           ryanss <ryanssdev@icloud.com> (c) 2014-2017
+#  Website: https://github.com/dr-prodigy/python-holidays
+#  License: MIT (see LICENSE file)
+#  Copyright: Kateryna Golovanova <kate@kgthreads.com>, 2022
+
+from datetime import date
+
+from dateutil.easter import easter
+from dateutil.relativedelta import FR, MO
+from dateutil.relativedelta import relativedelta as rd
+from holidays.constants import DEC, JAN, JUN, MAR, MAY, NOV, SUN
+from holidays.holiday_base import HolidayBase
+from holidays.utils import _islamic_to_gre
+
+
+class BosniaAndHerzegovina(HolidayBase):
+    """
+    Bosnia and Herzegovina holidays.
+    See https://en.wikipedia.org/wiki/Public_holidays_in_Bosnia_and_Herzegovina
+    for details.
+    """
+
+    country = "BA"
+    subdivisions = [
+        "BD",
+        "FBiH",
+        "RS",
+    ]
+
+    def _populate(self, year):
+
+        # New Year's Day.
+        self[date(year, JAN, 1)] = "Nova Godina"
+        self[date(year, JAN, 2)] = "Drugi dan Nove Godine"
+
+        if self.observed and date(year, JAN, 1).weekday() == SUN:
+            self[date(year, JAN, 1) + rd(days=+2)] = "Treći dan Nove Godine"
+
+        # Labor Day.
+        may_1 = date(year, MAY, 1)
+        self[may_1] = "Dan rada"
+        self[may_1 + rd(days=+1)] = "Drugi dan Dana rada"
+
+        if self.observed and may_1.weekday() == SUN:
+            self[may_1 + rd(days=+2)] = "Treći dan Dana rada"
+
+        if self.subdiv == "FBiH":
+            # Independence Day.
+            self[date(year, MAR, 1)] = "Dan nezavisnosti"
+
+            # Catholic Good Friday.
+            self[
+                easter(year) + rd(weekday=FR(-1))
+            ] = "Veliki Petak (Katolički)"
+
+            # Catholic Easter.
+            self[easter(year)] = "Uskrs (Katolički)"
+            self[
+                easter(year) + rd(weekday=MO(+1))
+            ] = "Uskrsni ponedjeljak (Katolički)"
+
+            # Corpus Cristi.
+            self[
+                easter(year) + rd(days=+60)
+            ] = "Tijelovo (Tijelo i Krv Kristova)"
+
+            # Eid al-Fitr.
+            # Date of observance is announced yearly, this is an estimate.
+            for dt in _islamic_to_gre(year, 10, 1):
+                self[dt] = "Ramazanski Bajram"
+                self[dt + rd(days=+1)] = "Drugi Dan Ramazanski Bajram"
+
+            # Eid ul-Adha.
+            # Date of observance is announced yearly, this is an estimate.
+            name = "Kurban Bajram"
+            for dt in _islamic_to_gre(year, 12, 10):
+                self[dt] = name
+                for d in range(1, 4):
+                    self[dt + rd(days=+d)] = name
+
+            # Islamic New Year.
+            for dt in _islamic_to_gre(year, 1, 1):
+                self[dt] = "Muslimanska Nova Godina"
+
+            # All Saints Day.
+            self[date(year, 11, 1)] = "Svi Sveti"
+
+            # All Souls Day.
+            self[date(year, 11, 2)] = "Dušni dan"
+
+            # Statehood Day.
+            self[date(year, NOV, 25)] = "Dan državnosti"
+
+            # Catholic Christmas.
+            self[date(year, DEC, 25)] = "Božić (Katolički)"
+
+            # St. Stephen's Day.
+            self[date(year, DEC, 26)] = "Stipandan (Stjepandan)"
+
+        elif self.subdiv == "RS":
+            # Orthodox Christmas Eve.
+            self[date(year, JAN, 6)] = "Pravoslavno Badnje veče"
+
+            # Orthodox Christmas.
+            self[date(year, JAN, 7)] = "Božić (Божић)"
+
+            # Republic day.
+            self[date(year, JAN, 9)] = "Dan Republike"
+
+            # Orthodox New Year.
+            self[date(year, JAN, 14)] = "Pravoslavna Nova Godina"
+
+            # Orthodox Good Friday.
+            self[
+                easter(year, method=2) + rd(weekday=FR(-1))
+            ] = "Veliki Petak (Pravoslavni)"
+
+            # Orthodox Easter.
+            self[easter(year, method=2)] = "Vaskrs (Pravoslavni)"
+            self[
+                easter(year, method=2) + rd(weekday=MO(+1))
+            ] = "Uskrsni ponedjeljak (Pravoslavni)"
+
+            # Victory Day.
+            self[date(year, MAY, 9)] = "Dan pobjede"
+
+            # St. Vitus Day.
+            self[date(year, JUN, 28)] = "Vidovdan"
+
+            # Dayton Agreement Day.
+            self[date(year, NOV, 21)] = (
+                "Dan uspostave Opšteg okvirnog sporazuma za mir u "
+                "Bosni i Hercegovini"
+            )
+
+
+class BA(BosniaAndHerzegovina):
+    pass
+
+
+class BIH(BosniaAndHerzegovina):
+    pass


Test patch:
diff --git a/test/countries/test_bosnia_and_herzegovina.py b/test/countries/test_bosnia_and_herzegovina.py
new file mode 100644
index 00000000..c3b42f67
--- /dev/null
+++ b/test/countries/test_bosnia_and_herzegovina.py
@@ -0,0 +1,295 @@
+#  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 <maurizio.montel@gmail.com> (c) 2017-2022
+#           ryanss <ryanssdev@icloud.com> (c) 2014-2017
+#  Website: https://github.com/dr-prodigy/python-holidays
+#  License: MIT (see LICENSE file)
+#  Copyright: Kateryna Golovanova <kate@kgthreads.com>, 2022
+
+import unittest
+from datetime import date
+
+import holidays
+
+
+class TestBA(unittest.TestCase):
+    def setUp(self):
+        self.holidays = holidays.BA(observed=False)
+        self.holidays_observed = holidays.BA(observed=True)
+
+    def test_new_years(self):
+
+        for dt in (date(2010, 12, 31), date(2017, 1, 3)):
+            self.assertNotIn(dt, self.holidays)
+
+        for dt in (date(2020, 1, 1), date(2019, 1, 1)):
+            self.assertIn(dt, self.holidays)
+            self.assertEqual(self.holidays[dt], "Nova Godina")
+
+        for dt in (date(2020, 1, 2), date(2021, 1, 2)):
+            self.assertIn(dt, self.holidays_observed)
+            self.assertEqual(self.holidays[dt], "Drugi dan Nove Godine")
+
+        for dt in (date(2017, 1, 3), date(2023, 1, 3)):
+            self.assertIn(dt, self.holidays_observed)
+            self.assertEqual(
+                self.holidays_observed[dt], "Treći dan Nove Godine"
+            )
+
+    def test_orthodox_christmas_eve(self):
+        rs_holidays = holidays.BA(subdiv="RS")
+
+        for dt in (date(2022, 1, 5), date(2021, 1, 5)):
+            self.assertNotIn(dt, rs_holidays)
+
+        for dt in (date(2022, 1, 6), date(2021, 1, 6)):
+            self.assertIn(dt, rs_holidays)
+            self.assertEqual(rs_holidays[dt], "Pravoslavno Badnje veče")
+
+    def test_orthodox_christmas(self):
+        rs_holidays = holidays.BA(subdiv="RS")
+
+        for dt in (date(2022, 1, 5), date(2021, 1, 8)):
+            self.assertNotIn(dt, rs_holidays)
+
+        for dt in (date(2022, 1, 7), date(2021, 1, 7)):
+            self.assertIn(dt, rs_holidays)
+            self.assertEqual(rs_holidays[dt], "Božić (Божић)")
+
+    def test_republic_day(self):
+        rs_holidays = holidays.BA(subdiv="RS")
+
+        for dt in (date(2022, 1, 10), date(2021, 1, 8)):
+            self.assertNotIn(dt, rs_holidays)
+
+        for dt in (date(2022, 1, 9), date(2021, 1, 9)):
+            self.assertIn(dt, rs_holidays)
+            self.assertEqual(rs_holidays[dt], "Dan Republike")
+
+    def test_orthodox_new_year(self):
+        rs_holidays = holidays.BA(subdiv="RS")
+
+        for dt in (date(2022, 1, 13), date(2021, 1, 15)):
+            self.assertNotIn(dt, rs_holidays)
+
+        for dt in (date(2022, 1, 14), date(2021, 1, 14)):
+            self.assertIn(dt, rs_holidays)
+            self.assertEqual(rs_holidays[dt], "Pravoslavna Nova Godina")
+
+    def test_independence_day(self):
+        fbih_holidays = holidays.BA(subdiv="FBiH")
+
+        for dt in (date(2022, 3, 2), date(2021, 2, 28)):
+            self.assertNotIn(dt, fbih_holidays)
+
+        for dt in (date(2022, 3, 1), date(2021, 3, 1)):
+            self.assertIn(dt, fbih_holidays)
+            self.assertEqual(fbih_holidays[dt], "Dan nezavisnosti")
+
+    def test_catholic_good_friday(self):
+        fbih_holidays = holidays.BA(subdiv="FBiH")
+
+        for dt in (date(2022, 4, 16), date(2021, 4, 14)):
+            self.assertNotIn(dt, fbih_holidays)
+
+        for dt in (date(2022, 4, 15), date(2021, 4, 2)):
+            self.assertIn(dt, fbih_holidays)
+            self.assertEqual(fbih_holidays[dt], "Veliki Petak (Katolički)")
+
+    def test_catholic_easter(self):
+        fbih_holidays = holidays.BA(subdiv="FBiH")
+
+        for dt in (date(2022, 4, 16), date(2021, 4, 3)):
+            self.assertNotIn(dt, fbih_holidays)
+
+        for dt in (date(2022, 4, 17), date(2021, 4, 4)):
+            self.assertIn(dt, fbih_holidays)
+            self.assertEqual(fbih_holidays[dt], "Uskrs (Katolički)")
+
+        for dt in (date(2022, 4, 18), date(2021, 4, 5)):
+            self.assertIn(dt, fbih_holidays)
+            self.assertEqual(
+                fbih_holidays[dt], "Uskrsni ponedjeljak (Katolički)"
+            )
+
+    def test_orthodox_good_friday(self):
+        rs_holidays = holidays.BA(subdiv="RS")
+
+        for dt in (date(2022, 4, 21), date(2021, 4, 29)):
+            self.assertNotIn(dt, rs_holidays)
+
+        for dt in (date(2022, 4, 22), date(2021, 4, 30)):
+            self.assertIn(dt, rs_holidays)
+            self.assertEqual(rs_holidays[dt], "Veliki Petak (Pravoslavni)")
+
+    def test_orthodox_easter(self):
+        rs_holidays = holidays.BA(subdiv="RS")
+
+        for dt in (date(2022, 4, 23), date(2020, 4, 18)):
+            self.assertNotIn(dt, rs_holidays)
+
+        for dt in (date(2022, 4, 24), date(2020, 4, 19)):
+            self.assertIn(dt, rs_holidays)
+            self.assertEqual(rs_holidays[dt], "Vaskrs (Pravoslavni)")
+
+        for dt in (date(2022, 4, 25), date(2020, 4, 20)):
+            self.assertIn(dt, rs_holidays)
+            self.assertEqual(
+                rs_holidays[dt], "Uskrsni ponedjeljak (Pravoslavni)"
+            )
+
+    def test_labor_day(self):
+        for dt in (date(2022, 5, 4), date(2021, 5, 4)):
+            self.assertNotIn(dt, self.holidays)
+
+        for dt in (date(2020, 5, 1), date(2021, 5, 1)):
+            self.assertIn(dt, self.holidays)
+            self.assertEqual(self.holidays[dt], "Dan rada")
+
+        for dt in (date(2022, 5, 2), date(2021, 5, 2)):
+            self.assertIn(dt, self.holidays)
+            self.assertEqual(self.holidays[dt], "Drugi dan Dana rada")
+
+        for dt in (date(2022, 5, 3), date(2016, 5, 3)):
+            self.assertIn(dt, self.holidays_observed)
+            self.assertEqual(self.holidays_observed[dt], "Treći dan Dana rada")
+
+    def test_corpus_cristi(self):
+        fbih_holidays = holidays.BA(subdiv="FBiH")
+
+        for dt in (date(2022, 6, 15), date(2021, 6, 2)):
+            self.assertNotIn(dt, fbih_holidays)
+
+        for dt in (date(2022, 6, 16), date(2021, 6, 3)):
+            self.assertIn(dt, fbih_holidays)
+            self.assertEqual(
+                fbih_holidays[dt], "Tijelovo (Tijelo i Krv Kristova)"
+            )
+
+    def test_victory_day(self):
+        rs_holidays = holidays.BA(subdiv="RS")
+
+        for dt in (date(2022, 5, 8), date(2020, 5, 10)):
+            self.assertNotIn(dt, rs_holidays)
+
+        for dt in (date(2022, 5, 9), date(2020, 5, 9)):
+            self.assertIn(dt, rs_holidays)
+            self.assertEqual(rs_holidays[dt], "Dan pobjede")
+
+    def test_eid_al_fitr(self):
+        fbih_holidays = holidays.BA(subdiv="FBiH")
+
+        for dt in (date(2021, 5, 12), date(2020, 5, 23)):
+            self.assertNotIn(dt, fbih_holidays)
+
+        for dt in (date(2021, 5, 13), date(2020, 5, 24)):
+            self.assertIn(dt, fbih_holidays)
+            self.assertEqual(fbih_holidays[dt], "Ramazanski Bajram")
+
+        for dt in (date(2021, 5, 14), date(2020, 5, 25)):
+            self.assertIn(dt, fbih_holidays)
+            self.assertEqual(fbih_holidays[dt], "Drugi Dan Ramazanski Bajram")
+
+    def test_eid_ul_adha(self):
+        fbih_holidays = holidays.BA(subdiv="FBiH")
+
+        for dt in (date(2021, 7, 8), date(2021, 7, 19)):
+            self.assertNotIn(dt, fbih_holidays)
+
+        for dt in (
+            date(2022, 7, 9),
+            date(2021, 7, 20),
+            date(2022, 7, 10),
+            date(2022, 7, 11),
+            date(2022, 7, 12),
+        ):
+            self.assertIn(dt, fbih_holidays)
+            self.assertEqual(fbih_holidays[dt], "Kurban Bajram")
+
+    def test_st_vitus_day(self):
+        rs_holidays = holidays.BA(subdiv="RS")
+
+        for dt in (date(2022, 6, 27), date(2020, 6, 29)):
+            self.assertNotIn(dt, rs_holidays)
+
+        for dt in (date(2022, 6, 28), date(2020, 6, 28)):
+            self.assertIn(dt, rs_holidays)
+            self.assertEqual(rs_holidays[dt], "Vidovdan")
+
+    def test_islamic_new_year(self):
+        fbih_holidays = holidays.BA(subdiv="FBiH")
+
+        for dt in (date(2022, 7, 28), date(2021, 8, 8)):
+            self.assertNotIn(dt, fbih_holidays)
+
+        for dt in (date(2022, 7, 30), date(2021, 8, 9)):
+            self.assertIn(dt, fbih_holidays)
+            self.assertEqual(fbih_holidays[dt], "Muslimanska Nova Godina")
+
+    def test_all_saints_day(self):
+        fbih_holidays = holidays.BA(subdiv="FBiH")
+
+        for dt in (date(2021, 11, 3), date(2020, 10, 31)):
+            self.assertNotIn(dt, fbih_holidays)
+
+        for dt in (date(2021, 11, 1), date(2020, 11, 1)):
+            self.assertIn(dt, fbih_holidays)
+            self.assertEqual(fbih_holidays[dt], "Svi Sveti")
+
+    def test_all_souls_day(self):
+        fbih_holidays = holidays.BA(subdiv="FBiH")
+
+        for dt in (date(2021, 11, 3), date(2020, 10, 31)):
+            self.assertNotIn(dt, fbih_holidays)
+
+        for dt in (date(2021, 11, 2), date(2020, 11, 2)):
+            self.assertIn(dt, fbih_holidays)
+            self.assertEqual(fbih_holidays[dt], "Dušni dan")
+
+    def test_dayton_agreement_day(self):
+        rs_holidays = holidays.BA(subdiv="RS")
+
+        for dt in (date(2022, 11, 20), date(2020, 11, 22)):
+            self.assertNotIn(dt, rs_holidays)
+
+        for dt in (date(2022, 11, 21), date(2020, 11, 21)):
+            self.assertIn(dt, rs_holidays)
+            self.assertEqual(
+                rs_holidays[dt],
+                "Dan uspostave Opšteg okvirnog sporazuma za mir u "
+                "Bosni i Hercegovini",
+            )
+
+    def test_statehood_day(self):
+        fbih_holidays = holidays.BA(subdiv="FBiH")
+
+        for dt in (date(2021, 11, 24), date(2020, 10, 26)):
+            self.assertNotIn(dt, fbih_holidays)
+
+        for dt in (date(2021, 11, 25), date(2020, 11, 25)):
+            self.assertIn(dt, fbih_holidays)
+            self.assertEqual(fbih_holidays[dt], "Dan državnosti")
+
+    def test_catholic_christmas(self):
+        fbih_holidays = holidays.BA(subdiv="FBiH")
+
+        for dt in (date(2021, 12, 23), date(2020, 12, 23)):
+            self.assertNotIn(dt, fbih_holidays)
+
+        for dt in (date(2021, 12, 25), date(2020, 12, 25)):
+            self.assertIn(dt, fbih_holidays)
+            self.assertEqual(fbih_holidays[dt], "Božić (Katolički)")
+
+    def test_st_stephens_day(self):
+        fbih_holidays = holidays.BA(subdiv="FBiH")
+
+        for dt in (date(2021, 12, 27), date(2020, 12, 27)):
+            self.assertNotIn(dt, fbih_holidays)
+
+        for dt in (date(2021, 12, 26), date(2020, 12, 26)):
+            self.assertIn(dt, fbih_holidays)
+            self.assertEqual(fbih_holidays[dt], "Stipandan (Stjepandan)")