Repository: dr-prodigy/python-holidays

Issue:
US Holidays Error: dictionary changed during iteration when passing years to holidays.US constructor
Attempting to use code to get US holidays for specific years.  This code works for some countries but not US (nor Canada)

```
import holidays
import numpy as np
print(holidays.__version__)
years = np.array([2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021])
x = holidays.US(years=years)
```

This code works when I try different countries: FR, GB, RU, DE, ES, CZ, AU, JP, CN, IN

This syntax **fails** to return results for the countries: US, CA

This syntax for passing numpy arrays I found in a ticket with a CZ example.  (https://github.com/dr-prodigy/python-holidays/pull/528).


I am running on Databricks runtime 10.0.  The holidays version is 0.11.3.1 as you see below.

The output I got was for US (or CA) is:
```
0.11.3.1
Out[41]: 
RuntimeError: dictionary changed size during iteration
```


Example output when I use another country, such as AU is:
```
0.11.3.1
Out[42]: {datetime.date(2016, 1, 1): "New Year's Day",
 datetime.date(2016, 1, 26): 'Australia Day',
 datetime.date(2016, 3, 25): 'Good Friday',
 datetime.date(2016, 3, 28): 'Easter Monday',
 datetime.date(2016, 4, 25): 'Anzac Day',
 datetime.date(2016, 12, 25): 'Christmas Day',
 datetime.date(2016, 12, 27): 'Christmas Day (Observed)',
 datetime.date(2016, 12, 26): 'Boxing Day',
 datetime.date(2017, 1, 1): "New Year's Day",
 datetime.date(2017, 1, 2): "New Year's Day (Observed)",
...
```

Discussion:

Patch:
diff --git a/holidays/countries/angola.py b/holidays/countries/angola.py
index c911b8ec..7bbf7b9e 100644
--- a/holidays/countries/angola.py
+++ b/holidays/countries/angola.py
@@ -17,8 +17,8 @@ from dateutil.easter import easter
 from dateutil.relativedelta import relativedelta as rd
 
 
-from holidays.constants import TUE, THU, SUN
-from holidays.constants import FEB, MAR, APR, MAY, SEP, NOV, DEC
+from holidays.constants import MON, TUE, THU, SUN
+from holidays.constants import JAN, FEB, MAR, APR, MAY, SEP, NOV, DEC
 from holidays.holiday_base import HolidayBase
 
 
@@ -30,7 +30,7 @@ class Angola(HolidayBase):
         # https://www.timeanddate.com/holidays/angola/
         HolidayBase.__init__(self, **kwargs)
 
-    def _populate(self, year):
+    def _populate(self, year: int) -> None:
         # Observed since 1975
         # TODO do more research on history of Angolan holidays
 
@@ -41,7 +41,12 @@ class Angola(HolidayBase):
             self[date(year, SEP, 17)] = "Dia do Herói Nacional"
 
         if year > 1974:
-            self[date(year, 1, 1)] = "Ano novo"
+            self[date(year, JAN, 1)] = "Ano novo"
+            # Since 2018, if the following year's New Year's Day falls on a
+            # Tuesday, the 31st of the current year is also a holiday.
+            if year >= 2018:
+                if self.observed and date(year, DEC, 31).weekday() == MON:
+                    self[date(year, DEC, 31)] = "Ano novo (Day off)"
 
             e = easter(year)
             good_friday = e - rd(days=2)
@@ -74,7 +79,7 @@ class Angola(HolidayBase):
                 if k.weekday() == SUN:
                     pass
             if self.observed and year > 2017:
-                if k.weekday() == TUE:
+                if k.weekday() == TUE and k != date(year, JAN, 1):
                     self[k - rd(days=1)] = v + " (Day off)"
                 elif k.weekday() == THU:
                     self[k + rd(days=1)] = v + " (Day off)"
diff --git a/holidays/countries/azerbaijan.py b/holidays/countries/azerbaijan.py
index b3526873..a4f792fe 100644
--- a/holidays/countries/azerbaijan.py
+++ b/holidays/countries/azerbaijan.py
@@ -13,7 +13,8 @@
 
 from datetime import date
 from dateutil.relativedelta import relativedelta as rd
-from holidays.constants import JAN, MAR, MAY, JUN, NOV, DEC, SAT, SUN
+from holidays.constants import SAT, SUN
+from holidays.constants import JAN, MAR, MAY, JUN, OCT, NOV, DEC
 from holidays.holiday_base import HolidayBase
 from holidays.utils import _islamic_to_gre
 
@@ -32,10 +33,12 @@ class Azerbaijan(HolidayBase):
     def _add_observed(self, holiday: date) -> None:
         if self.observed and holiday.weekday() in (SAT, SUN):
             next_monday = holiday + rd(days=7 - holiday.weekday())
-            if not self.get(next_monday, None):
+            if next_monday.year == holiday.year and not self.get(
+                next_monday, None
+            ):
                 self[next_monday] = self[holiday] + OBSERVED_SUFFIX
 
-    def _populate(self, year):
+    def _populate(self, year: int) -> None:
 
         # 1st of Jan
         self[date(year, JAN, 1)] = "New Year's Day"
@@ -85,17 +88,30 @@ class Azerbaijan(HolidayBase):
             date(year, DEC, 31)
         ] = "International Solidarity Day of Azerbaijanis"
         self._add_observed(date(year, DEC, 31))
+        # If the prior year's International Solidarity Day of Azerbaijanis
+        # falls on a Saturday or Monday, the 1st Monday of the current year is
+        # also a holiday.
+        if self.observed and date(year - 1, DEC, 31).weekday() == SUN:
+            self[date(year, JAN, 1)] = (
+                "International Solidarity Day of Azerbaijanis"
+                + OBSERVED_SUFFIX
+            )
+        elif self.observed and date(year - 1, DEC, 31).weekday() == SAT:
+            self[date(year, JAN, 2)] = (
+                "International Solidarity Day of Azerbaijanis"
+                + OBSERVED_SUFFIX
+            )
 
         # Ramadan
         # Date of observance is announced yearly, This is an estimate.
-        hol_date = _islamic_to_gre(year, 10, 1)[0]
+        hol_date = _islamic_to_gre(year, OCT, 1)[0]
         self[hol_date] = "Ramadan"
         self[hol_date + rd(days=1)] = "Ramadan"
         self._add_observed(hol_date + rd(days=1))
 
         # Festival of the Sacrifice
         # Date of observance is announced yearly, This is an estimate.
-        hol_date = _islamic_to_gre(year, 12, 10)[0]
+        hol_date = _islamic_to_gre(year, DEC, 10)[0]
         self[hol_date] = "Festival of the Sacrifice"
         self[hol_date + rd(days=1)] = "Festival of the Sacrifice"
         self._add_observed(hol_date + rd(days=1))
diff --git a/holidays/countries/botswana.py b/holidays/countries/botswana.py
index 7c3dc93a..8edeeb81 100644
--- a/holidays/countries/botswana.py
+++ b/holidays/countries/botswana.py
@@ -103,8 +103,9 @@ class Botswana(HolidayBase):
                     if " (Observed)" not in i:
                         self[k + rd(days=1)] = i.lstrip() + " (Observed)"
 
-        # Once off ad-hoc holidays
-        self[date(2019, JUL, 2)] = "Public Holiday"
+        # Once off ad-hoc holiday.
+        if year == 2019:
+            self[date(year, JUL, 2)] = "Public Holiday"
 
 
 class BW(Botswana):
diff --git a/holidays/countries/canada.py b/holidays/countries/canada.py
index 0b01c365..66360849 100644
--- a/holidays/countries/canada.py
+++ b/holidays/countries/canada.py
@@ -63,15 +63,8 @@ class Canada(HolidayBase):
             self[date(year, JAN, 1)] = name
             if self.observed and date(year, JAN, 1).weekday() == SUN:
                 self[date(year, JAN, 1) + rd(days=+1)] = name + " (Observed)"
-            elif self.observed and date(year, JAN, 1).weekday() == SAT:
-                # Add Dec 31st from the previous year without triggering
-                # the entire year to be added
-                expand = self.expand
-                self.expand = False
-                self[date(year, JAN, 1) + rd(days=-1)] = name + " (Observed)"
-                self.expand = expand
-            # The next year's observed New Year's Day can be in this year
-            # when it falls on a Friday (Jan 1st is a Saturday)
+            # 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 date(year, DEC, 31).weekday() == FRI:
                 self[date(year, DEC, 31)] = name + " (Observed)"
 
diff --git a/holidays/countries/hungary.py b/holidays/countries/hungary.py
index 91e72170..fa9f03ac 100644
--- a/holidays/countries/hungary.py
+++ b/holidays/countries/hungary.py
@@ -37,9 +37,14 @@ class Hungary(HolidayBase):
     def __init__(self, **kwargs):
         HolidayBase.__init__(self, **kwargs)
 
-    def _populate(self, year):
+    def _populate(self, year: int) -> None:
         # New years
         self._add_with_observed_day_off(date(year, JAN, 1), "Újév", since=2014)
+        # Since 2014, the last day of the year is an observed day off if New
+        # Year's Day falls on a Tuesday.
+        if year >= 2104:
+            if self.observed and date(year, DEC, 31).weekday() == MON:
+                self[date(year, DEC, 31)] = "Újév előtti pihenőnap"
 
         # National Day
         if 1945 <= year <= 1950 or 1989 <= year:
@@ -140,14 +145,23 @@ class Hungary(HolidayBase):
             self[date(year, DEC, 31)] = "Szilveszter"
 
     def _add_with_observed_day_off(
-        self, day, desc, since=2010, before=True, after=True
-    ):
+        self,
+        day: date,
+        desc: str,
+        since: int = 2010,
+        before: bool = True,
+        after: bool = True,
+    ) -> None:
         # Swapped days off were in place earlier but
         # I haven't found official record yet.
         self[day] = desc
         # TODO: should it be a separate flag?
         if self.observed and since <= day.year:
-            if day.weekday() == TUE and before:
+            if (
+                day.weekday() == TUE
+                and before
+                and not (day.month == JAN and day.day == 1)
+            ):
                 self[day - rd(days=1)] = desc + " előtti pihenőnap"
             elif day.weekday() == THU and after:
                 self[day + rd(days=1)] = desc + " utáni pihenőnap"
diff --git a/holidays/countries/israel.py b/holidays/countries/israel.py
index a793c91c..aa5d3371 100644
--- a/holidays/countries/israel.py
+++ b/holidays/countries/israel.py
@@ -39,8 +39,7 @@ class Israel(HolidayBase):
     def _populate(self, year):
         # Passover
         name = "Passover I"
-        year, month, day = passover(year, eve=True)
-        passover_start_dt = date(year, month, day)
+        passover_start_dt = date(*passover(year, eve=True))
         self[passover_start_dt] = name + " - Eve"
         self[passover_start_dt + rd(days=1)] = name
 
@@ -54,61 +53,62 @@ class Israel(HolidayBase):
 
         # Memorial Day
         name = "Memorial Day"
-        year, month, day = gregorian.from_jd(
-            hebrew.to_jd_gregorianyear(year, hebrew.IYYAR, 3)
+        memorial_day_dt = date(
+            *gregorian.from_jd(
+                hebrew.to_jd_gregorianyear(year, hebrew.IYYAR, 3)
+            )
         )
-        self[date(year, month, day) + rd(days=1)] = name
+        self[memorial_day_dt + rd(days=1)] = name
 
         observed_delta = 0
         if self.observed:
-            day_in_week = date(year, month, day).weekday()
+            day_in_week = memorial_day_dt.weekday()
             if day_in_week in (2, 3):
                 observed_delta = -(day_in_week - 1)
             elif 2004 <= year and day_in_week == 5:
                 observed_delta = 1
 
             if observed_delta != 0:
-                self[date(year, month, day) + rd(days=observed_delta + 1)] = (
+                self[memorial_day_dt + rd(days=observed_delta + 1)] = (
                     name + " (Observed)"
                 )
 
         # Independence Day
         name = "Independence Day"
-        self[date(year, month, day) + rd(days=2)] = name
+        self[memorial_day_dt + rd(days=2)] = name
 
         if self.observed and observed_delta != 0:
-            self[date(year, month, day) + rd(days=observed_delta + 2)] = (
+            self[memorial_day_dt + rd(days=observed_delta + 2)] = (
                 name + " (Observed)"
             )
 
         # Lag Baomer
         name = "Lag B'Omer"
-        year, month, day = lag_baomer(year, eve=False)
-        self[date(year, month, day)] = name
+        lag_baomer_dt = date(*lag_baomer(year, eve=False))
+        self[lag_baomer_dt] = name
 
         # Shavuot
         name = "Shavuot"
-        year, month, day = shavuot(year, eve=True)
-        self[date(year, month, day)] = name + " - Eve"
-        self[date(year, month, day) + rd(days=1)] = name
+        shavuot_dt = date(*shavuot(year, eve=True))
+        self[shavuot_dt] = name + " - Eve"
+        self[shavuot_dt + rd(days=1)] = name
 
         # Rosh Hashana
         name = "Rosh Hashanah"
-        year, month, day = rosh_hashanah(year, eve=True)
-        self[date(year, month, day)] = name + " - Eve"
-        self[date(year, month, day) + rd(days=1)] = name
-        self[date(year, month, day) + rd(days=2)] = name
+        rosh_hashanah_dt = date(*rosh_hashanah(year, eve=True))
+        self[rosh_hashanah_dt] = name + " - Eve"
+        self[rosh_hashanah_dt + rd(days=1)] = name
+        self[rosh_hashanah_dt + rd(days=2)] = name
 
         # Yom Kippur
         name = "Yom Kippur"
-        year, month, day = yom_kippur(year, eve=True)
-        self[date(year, month, day)] = name + " - Eve"
-        self[date(year, month, day) + rd(days=1)] = name
+        yom_kippur_dt = date(*yom_kippur(year, eve=True))
+        self[yom_kippur_dt] = name + " - Eve"
+        self[yom_kippur_dt + rd(days=1)] = name
 
         # Sukkot
         name = "Sukkot I"
-        year, month, day = sukkot(year, eve=True)
-        sukkot_start_dt = date(year, month, day)
+        sukkot_start_dt = date(*sukkot(year, eve=True))
         self[sukkot_start_dt] = name + " - Eve"
         self[sukkot_start_dt + rd(days=1)] = name
 
@@ -122,16 +122,24 @@ class Israel(HolidayBase):
 
         # Hanukkah
         name = "Hanukkah"
-        year, month, day = hanukkah(year, eve=False)
+        hk_start_date = date(*hanukkah(year, eve=False))
+        for offset in range(8):
+            hk_date = hk_start_date + rd(days=offset)
+            if hk_date.year == year:
+                self[hk_date] = name
+        # Some o prior's year Hannukah may fall in current year.
+        hk_start_date = date(*hanukkah(year - 1, eve=False))
         for offset in range(8):
-            self[date(year, month, day) + rd(days=offset)] = name
+            hk_date = hk_start_date + rd(days=offset)
+            if hk_date.year == year:
+                self[hk_date] = name
 
         # Purim
         name = "Purim"
-        year, month, day = purim(year, eve=True)
-        self[date(year, month, day)] = name + " - Eve"
-        self[date(year, month, day) + rd(days=1)] = name
-        self[date(year, month, day) + rd(days=2)] = "Shushan Purim"
+        purim_date = date(*purim(year, eve=True))
+        self[purim_date] = name + " - Eve"
+        self[purim_date + rd(days=1)] = name
+        self[purim_date + rd(days=2)] = "Shushan Purim"
 
 
 class IL(Israel):
diff --git a/holidays/countries/taiwan.py b/holidays/countries/taiwan.py
index 3816324c..d7eeb5f6 100644
--- a/holidays/countries/taiwan.py
+++ b/holidays/countries/taiwan.py
@@ -28,8 +28,8 @@ class Taiwan(HolidayBase):
     country = "TW"
 
     def __init__(self, **kwargs):
-        HolidayBase.__init__(self, **kwargs)
         self.cnls = _ChineseLuniSolar()
+        HolidayBase.__init__(self, **kwargs)
 
     def _populate(self, year):
         # New Year's Day
diff --git a/holidays/countries/united_states.py b/holidays/countries/united_states.py
index 9eacdbc6..fb8aefc7 100644
--- a/holidays/countries/united_states.py
+++ b/holidays/countries/united_states.py
@@ -110,15 +110,8 @@ class UnitedStates(HolidayBase):
             self[date(year, JAN, 1)] = name
             if self.observed and date(year, JAN, 1).weekday() == SUN:
                 self[date(year, JAN, 1) + rd(days=+1)] = name + " (Observed)"
-            elif self.observed and date(year, JAN, 1).weekday() == SAT:
-                # Add Dec 31st from the previous year without triggering
-                # the entire year to be added
-                expand = self.expand
-                self.expand = False
-                self[date(year, JAN, 1) + rd(days=-1)] = name + " (Observed)"
-                self.expand = expand
-            # The next year's observed New Year's Day can be in this year
-            # when it falls on a Friday (Jan 1st is a Saturday)
+            # 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 date(year, DEC, 31).weekday() == FRI:
                 self[date(year, DEC, 31)] = name + " (Observed)"
 


Test patch:
diff --git a/test/test_holiday_base.py b/test/test_holiday_base.py
index 509ae262..8a87d2de 100644
--- a/test/test_holiday_base.py
+++ b/test/test_holiday_base.py
@@ -409,7 +409,7 @@ class TestArgs(unittest.TestCase):
         self.assertIn(2013, self.holidays.years)
         self.assertNotIn(2014, self.holidays.years)
         self.assertIn(2015, self.holidays.years)
-        self.assertIn(date(2021, 12, 31), holidays.US(years=[2022]).keys())
+        self.assertIn(date(2021, 12, 31), holidays.US(years=[2021]).keys())
         self.holidays = holidays.US(years=2015)
         self.assertNotIn(2014, self.holidays.years)
         self.assertIn(2015, self.holidays.years)
@@ -541,3 +541,56 @@ class TestCountryHoliday(unittest.TestCase):
 
     def test_exceptions(self):
         self.assertRaises((KeyError), lambda: holidays.CountryHoliday("XXXX"))
+
+
+class TestAllInSameYear(unittest.TestCase):
+    """Test that only holidays in the year(s) requested are returned."""
+
+    country: str
+    hol: datetime
+    year: int
+
+    def setUp(self):
+        self.countries = holidays.list_supported_countries()
+
+    def tearDown(self):
+        """https://stackoverflow.com/questions/4414234/"""
+
+        def list2reason(exc_list):
+            if exc_list and exc_list[-1][0] is self:
+                return exc_list[-1][1]
+
+        result = self.defaultTestResult()
+        self._feedErrorsToResult(result, self._outcome.errors)
+        error = list2reason(result.errors)
+        failure = list2reason(result.failures)
+        text = error if error else failure
+        if text:
+            print(
+                f"{text.splitlines()[-1]} in country {self.country}: "
+                f"holiday {self.hol} returned for year {self.year}"
+            )
+            print(
+                holidays.country_holidays(
+                    self.country, prov=None, state=None, years=[self.year]
+                ).get_list(self.hol)
+            )
+
+    def test_all_countries(self):
+        """
+        Only holidays in the year(s) requested should be returned. This
+        ensures that we avoid triggering a "RuntimeError: dictionary changed
+        size during iteration" error.
+
+        Here we test all countries for the 12-year period starting ten years
+        ago and ending 2 years from now.
+        """
+        for self.country in self.countries:
+            for self.year in range(
+                date.today().year - 10, date.today().year + 3
+            ):
+                hols = holidays.country_holidays(
+                    self.country, prov=None, state=None, years=[self.year]
+                )
+                for self.hol in hols:
+                    assert self.hol.year == self.year