holidays 0.81__py3-none-any.whl → 0.83__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. holidays/calendars/__init__.py +1 -0
  2. holidays/calendars/burmese.py +319 -0
  3. holidays/calendars/hebrew.py +2 -2
  4. holidays/calendars/islamic.py +26 -6
  5. holidays/calendars/thai.py +49 -2
  6. holidays/countries/__init__.py +11 -0
  7. holidays/countries/afghanistan.py +7 -5
  8. holidays/countries/algeria.py +89 -24
  9. holidays/countries/antarctica.py +58 -0
  10. holidays/countries/azerbaijan.py +1 -1
  11. holidays/countries/bouvet_island.py +31 -0
  12. holidays/countries/brazil.py +1 -1
  13. holidays/countries/british_indian_ocean_territory.py +31 -0
  14. holidays/countries/bulgaria.py +6 -1
  15. holidays/countries/canada.py +27 -13
  16. holidays/countries/chile.py +9 -8
  17. holidays/countries/cuba.py +3 -4
  18. holidays/countries/denmark.py +18 -6
  19. holidays/countries/djibouti.py +1 -1
  20. holidays/countries/fiji.py +2 -3
  21. holidays/countries/finland.py +3 -6
  22. holidays/countries/guyana.py +24 -32
  23. holidays/countries/heard_island_and_mcdonald_islands.py +31 -0
  24. holidays/countries/hungary.py +4 -5
  25. holidays/countries/india.py +27 -78
  26. holidays/countries/iran.py +133 -269
  27. holidays/countries/ireland.py +1 -1
  28. holidays/countries/israel.py +1 -1
  29. holidays/countries/japan.py +22 -18
  30. holidays/countries/jordan.py +6 -3
  31. holidays/countries/kuwait.py +6 -3
  32. holidays/countries/macau.py +5 -8
  33. holidays/countries/malaysia.py +14 -3
  34. holidays/countries/mauritius.py +10 -9
  35. holidays/countries/montserrat.py +1 -1
  36. holidays/countries/myanmar.py +194 -0
  37. holidays/countries/nepal.py +11 -30
  38. holidays/countries/netherlands.py +28 -24
  39. holidays/countries/new_zealand.py +75 -31
  40. holidays/countries/nigeria.py +53 -75
  41. holidays/countries/north_korea.py +161 -0
  42. holidays/countries/norway.py +24 -9
  43. holidays/countries/oman.py +6 -2
  44. holidays/countries/paraguay.py +70 -23
  45. holidays/countries/portugal.py +5 -6
  46. holidays/countries/qatar.py +5 -2
  47. holidays/countries/rwanda.py +8 -3
  48. holidays/countries/saudi_arabia.py +6 -5
  49. holidays/countries/serbia.py +5 -0
  50. holidays/countries/slovakia.py +23 -4
  51. holidays/countries/south_africa.py +96 -53
  52. holidays/countries/south_korea.py +6 -1
  53. holidays/countries/spain.py +4 -4
  54. holidays/countries/sri_lanka.py +21 -58
  55. holidays/countries/sudan.py +6 -3
  56. holidays/countries/suriname.py +1 -2
  57. holidays/countries/sweden.py +1 -1
  58. holidays/countries/switzerland.py +255 -6
  59. holidays/countries/taiwan.py +23 -3
  60. holidays/countries/tanzania.py +25 -30
  61. holidays/countries/thailand.py +29 -7
  62. holidays/countries/tonga.py +6 -1
  63. holidays/countries/trinidad_and_tobago.py +7 -2
  64. holidays/countries/ukraine.py +1 -1
  65. holidays/countries/united_arab_emirates.py +9 -2
  66. holidays/countries/united_kingdom.py +1 -1
  67. holidays/countries/united_states.py +18 -2
  68. holidays/countries/uruguay.py +6 -1
  69. holidays/countries/vietnam.py +6 -1
  70. holidays/countries/western_sahara.py +92 -0
  71. holidays/countries/yemen.py +6 -3
  72. holidays/groups/__init__.py +1 -0
  73. holidays/groups/balinese_saka.py +1 -1
  74. holidays/groups/buddhist.py +3 -3
  75. holidays/groups/burmese.py +273 -0
  76. holidays/groups/chinese.py +30 -2
  77. holidays/groups/christian.py +10 -0
  78. holidays/groups/custom.py +8 -2
  79. holidays/groups/eastern.py +26 -0
  80. holidays/groups/hebrew.py +16 -14
  81. holidays/groups/hindu.py +23 -15
  82. holidays/groups/international.py +1 -1
  83. holidays/groups/islamic.py +52 -50
  84. holidays/groups/mongolian.py +6 -3
  85. holidays/groups/sinhala.py +9 -11
  86. holidays/groups/tibetan.py +8 -6
  87. holidays/holiday_base.py +26 -26
  88. holidays/ical.py +6 -4
  89. holidays/locale/ar/LC_MESSAGES/DZ.mo +0 -0
  90. holidays/locale/ar/LC_MESSAGES/EH.mo +0 -0
  91. holidays/locale/ca/LC_MESSAGES/ES.mo +0 -0
  92. holidays/locale/de/LC_MESSAGES/CH.mo +0 -0
  93. holidays/locale/en_IN/LC_MESSAGES/IN.mo +0 -0
  94. holidays/locale/en_US/LC_MESSAGES/BR.mo +0 -0
  95. holidays/locale/en_US/LC_MESSAGES/CH.mo +0 -0
  96. holidays/locale/en_US/LC_MESSAGES/DZ.mo +0 -0
  97. holidays/locale/en_US/LC_MESSAGES/EH.mo +0 -0
  98. holidays/locale/en_US/LC_MESSAGES/ES.mo +0 -0
  99. holidays/locale/en_US/LC_MESSAGES/IN.mo +0 -0
  100. holidays/locale/en_US/LC_MESSAGES/JP.mo +0 -0
  101. holidays/locale/en_US/LC_MESSAGES/KP.mo +0 -0
  102. holidays/locale/en_US/LC_MESSAGES/MM.mo +0 -0
  103. holidays/locale/en_US/LC_MESSAGES/NL.mo +0 -0
  104. holidays/locale/en_US/LC_MESSAGES/PY.mo +0 -0
  105. holidays/locale/en_US/LC_MESSAGES/US.mo +0 -0
  106. holidays/locale/es/LC_MESSAGES/EH.mo +0 -0
  107. holidays/locale/es/LC_MESSAGES/ES.mo +0 -0
  108. holidays/locale/es/LC_MESSAGES/PY.mo +0 -0
  109. holidays/locale/fr/LC_MESSAGES/CH.mo +0 -0
  110. holidays/locale/fr/LC_MESSAGES/DZ.mo +0 -0
  111. holidays/locale/fr/LC_MESSAGES/EH.mo +0 -0
  112. holidays/locale/fy/LC_MESSAGES/NL.mo +0 -0
  113. holidays/locale/hi/LC_MESSAGES/IN.mo +0 -0
  114. holidays/locale/it/LC_MESSAGES/CH.mo +0 -0
  115. holidays/locale/ja/LC_MESSAGES/JP.mo +0 -0
  116. holidays/locale/ko_KP/LC_MESSAGES/KP.mo +0 -0
  117. holidays/locale/my/LC_MESSAGES/MM.mo +0 -0
  118. holidays/locale/nl/LC_MESSAGES/NL.mo +0 -0
  119. holidays/locale/pt_BR/LC_MESSAGES/BR.mo +0 -0
  120. holidays/locale/th/LC_MESSAGES/CH.mo +0 -0
  121. holidays/locale/th/LC_MESSAGES/DK.mo +0 -0
  122. holidays/locale/th/LC_MESSAGES/JP.mo +0 -0
  123. holidays/locale/th/LC_MESSAGES/MM.mo +0 -0
  124. holidays/locale/th/LC_MESSAGES/NL.mo +0 -0
  125. holidays/locale/th/LC_MESSAGES/US.mo +0 -0
  126. holidays/locale/uk/LC_MESSAGES/BR.mo +0 -0
  127. holidays/locale/uk/LC_MESSAGES/CH.mo +0 -0
  128. holidays/locale/uk/LC_MESSAGES/ES.mo +0 -0
  129. holidays/locale/uk/LC_MESSAGES/NL.mo +0 -0
  130. holidays/locale/uk/LC_MESSAGES/PY.mo +0 -0
  131. holidays/no_holiday_base.py +21 -0
  132. holidays/observed_holiday_base.py +16 -4
  133. holidays/registry.py +8 -0
  134. holidays/utils.py +5 -5
  135. holidays/version.py +1 -1
  136. {holidays-0.81.dist-info → holidays-0.83.dist-info}/METADATA +63 -19
  137. {holidays-0.81.dist-info → holidays-0.83.dist-info}/RECORD +141 -118
  138. {holidays-0.81.dist-info → holidays-0.83.dist-info}/licenses/CONTRIBUTORS +2 -0
  139. {holidays-0.81.dist-info → holidays-0.83.dist-info}/WHEEL +0 -0
  140. {holidays-0.81.dist-info → holidays-0.83.dist-info}/licenses/LICENSE +0 -0
  141. {holidays-0.81.dist-info → holidays-0.83.dist-info}/top_level.txt +0 -0
@@ -14,6 +14,7 @@
14
14
 
15
15
  from holidays.calendars.balinese_saka import _BalineseSakaLunar
16
16
  from holidays.calendars.buddhist import _BuddhistLunisolar, _CustomBuddhistHolidays
17
+ from holidays.calendars.burmese import _BurmeseLunisolar
17
18
  from holidays.calendars.chinese import _ChineseLunisolar, _CustomChineseHolidays
18
19
  from holidays.calendars.custom import _CustomCalendar
19
20
  from holidays.calendars.gregorian import GREGORIAN_CALENDAR
@@ -0,0 +1,319 @@
1
+ # holidays
2
+ # --------
3
+ # A fast, efficient Python library for generating country, province and state
4
+ # specific sets of holidays on the fly. It aims to make determining whether a
5
+ # specific date is a holiday as fast and flexible as possible.
6
+ #
7
+ # Authors: Vacanza Team and individual contributors (see CONTRIBUTORS file)
8
+ # dr-prodigy <dr.prodigy.github@gmail.com> (c) 2017-2023
9
+ # ryanss <ryanssdev@icloud.com> (c) 2014-2017
10
+ # Website: https://github.com/vacanza/holidays
11
+ # License: MIT (see LICENSE file)
12
+
13
+ from datetime import date
14
+ from functools import cache
15
+ from typing import Optional
16
+
17
+ from holidays.calendars.gregorian import _timedelta
18
+
19
+ MO = 1954168.050623 # Beginning of 0 ME for MMT.
20
+ SY = 1577917828.0 / 4320000.0 # Solar year (365.2587565 days).
21
+
22
+
23
+ class _BurmeseLunisolar:
24
+ """Burmese Lunisolar calendar.
25
+
26
+ References:
27
+ * [Algorithm, Program and Calculation of Myanmar Calendar](https://web.archive.org/web/20250510011425/http://cool-emerald.blogspot.com/2013/06/algorithm-program-and-calculation-of.html)
28
+ """
29
+
30
+ START_DATE = date(1939, 3, 20) # 1 Late Tagu 1301 Burmese Era.
31
+ START_YEAR = 1939 # 1301 Burmese Era.
32
+ END_YEAR = 2100 # 1463 Burmese Era.
33
+
34
+ LITTLE_WATAT_YEARS_GREGORIAN = {
35
+ 1939,
36
+ 1948,
37
+ 1955,
38
+ 1958,
39
+ 1966,
40
+ 1972,
41
+ 1974,
42
+ 1982,
43
+ 1988,
44
+ 1993,
45
+ 1999,
46
+ 2004,
47
+ 2012,
48
+ 2018,
49
+ 2020,
50
+ 2029,
51
+ 2034,
52
+ 2039,
53
+ 2045,
54
+ 2050,
55
+ 2056,
56
+ 2061,
57
+ 2069,
58
+ 2075,
59
+ 2077,
60
+ 2086,
61
+ 2091,
62
+ 2096,
63
+ }
64
+
65
+ BIG_WATAT_YEARS_GREGORIAN = {
66
+ 1942,
67
+ 1945,
68
+ 1950,
69
+ 1953,
70
+ 1961,
71
+ 1964,
72
+ 1969,
73
+ 1977,
74
+ 1980,
75
+ 1985,
76
+ 1991,
77
+ 1996,
78
+ 2001,
79
+ 2007,
80
+ 2010,
81
+ 2015,
82
+ 2023,
83
+ 2026,
84
+ 2031,
85
+ 2037,
86
+ 2042,
87
+ 2048,
88
+ 2053,
89
+ 2058,
90
+ 2064,
91
+ 2067,
92
+ 2072,
93
+ 2080,
94
+ 2083,
95
+ 2088,
96
+ 2094,
97
+ 2099,
98
+ }
99
+
100
+ @staticmethod
101
+ def jdn_to_gregorian(jdn: int) -> date:
102
+ """Convert Julian Day Number to Gregorian date.
103
+
104
+ Args:
105
+ jdn: Julian Day Number.
106
+
107
+ Returns:
108
+ Gregorian date.
109
+ """
110
+ j = jdn - 1721119
111
+ y, j = divmod(4 * j - 1, 146097)
112
+ d = j // 4
113
+ j, d = divmod(4 * d + 3, 1461)
114
+ d = (d + 4) // 4
115
+ m, d = divmod(5 * d - 3, 153)
116
+ d = (d + 5) // 5
117
+ y = 100 * y + j
118
+ if m < 10:
119
+ m += 3
120
+ else:
121
+ m -= 9
122
+ y += 1
123
+
124
+ return date(y, m, d)
125
+
126
+ @cache
127
+ def _get_start_date(self, year: int) -> Optional[date]:
128
+ if year < self.START_YEAR or year > self.END_YEAR:
129
+ return None
130
+
131
+ delta_days = 354 * (year - self.START_YEAR)
132
+ for iter_year in range(self.START_YEAR, year):
133
+ if iter_year in self.LITTLE_WATAT_YEARS_GREGORIAN:
134
+ delta_days += 30
135
+ elif iter_year in self.BIG_WATAT_YEARS_GREGORIAN:
136
+ delta_days += 31
137
+
138
+ return _timedelta(self.START_DATE, delta_days)
139
+
140
+ def thingyan_dates(self, year: int) -> tuple[Optional[date], Optional[date]]:
141
+ """Calculate key dates of Thingyan (Myanmar New Year festival) - Akya day
142
+ and Atat day.
143
+
144
+ Args:
145
+ year:
146
+ Gregorian year.
147
+
148
+ Returns:
149
+ Akya day date, Atat day date.
150
+
151
+ """
152
+ if year < self.START_YEAR or year > self.END_YEAR:
153
+ return None, None
154
+
155
+ ja = SY * (year - 638) + MO
156
+ jk = (ja - 2.169918982) if year >= 1950 else (ja - 2.1675)
157
+
158
+ return self.jdn_to_gregorian(round(jk)), self.jdn_to_gregorian(round(ja))
159
+
160
+ def kason_full_moon_date(self, year: int) -> Optional[date]:
161
+ """Calculate the Gregorian date of Full Moon Day of Kason.
162
+
163
+ 15th day of 2nd month (Kason).
164
+
165
+ To calculate, we use the following time delta:
166
+
167
+ * All years:
168
+ 29 [1] + 15 - 1 = 43.
169
+
170
+ Args:
171
+ year:
172
+ Gregorian year.
173
+
174
+ Returns:
175
+ Gregorian date of Full Moon Day of Kason.
176
+ `None` if the Gregorian year is out of supported range.
177
+ """
178
+ if not (start_date := self._get_start_date(year)):
179
+ return None
180
+
181
+ return _timedelta(start_date, +43)
182
+
183
+ def waso_full_moon_date(self, year: int) -> Optional[date]:
184
+ """Calculate the Gregorian date of Full Moon Day of Waso.
185
+
186
+ 15th day of 4th month (Waso).
187
+
188
+ To calculate, we use the following time delta:
189
+
190
+ * All years:
191
+ Year_length - 266 [4-12] + 15 - 1 = Year_length - 252.
192
+
193
+ Args:
194
+ year:
195
+ Gregorian year.
196
+
197
+ Returns:
198
+ Gregorian date of Full Moon Day of Waso.
199
+ `None` if the Gregorian year is out of supported range.
200
+ """
201
+ if not (next_year_start_date := self._get_start_date(year + 1)):
202
+ return None
203
+
204
+ return _timedelta(next_year_start_date, -252)
205
+
206
+ def thadingyut_full_moon_date(self, year: int) -> Optional[date]:
207
+ """Calculate the Gregorian date of Full Moon Day of Thadingyut.
208
+
209
+ 15th day of 7th month (Thadingyut).
210
+
211
+ To calculate, we use the following time delta:
212
+
213
+ * All years:
214
+ Year_length - 177 [7-12] + 15 - 1 = Year_length - 163.
215
+
216
+ Args:
217
+ year:
218
+ Gregorian year.
219
+
220
+ Returns:
221
+ Gregorian date of Full Moon Day of Thadingyut.
222
+ `None` if the Gregorian year is out of supported range.
223
+ """
224
+ if not (next_year_start_date := self._get_start_date(year + 1)):
225
+ return None
226
+
227
+ return _timedelta(next_year_start_date, -163)
228
+
229
+ def tazaungmon_waxing_moon_date(self, year: int) -> Optional[date]:
230
+ """Calculate the Gregorian date of 1st Waxing Day of Tazaungmon.
231
+
232
+ 1st day of 8th month (Tazaungmon).
233
+
234
+ To calculate, we use the following time delta:
235
+
236
+ * All years:
237
+ Year_length - 148 [8-12] + 1 - 1 = Year_length - 148.
238
+
239
+ Args:
240
+ year:
241
+ Gregorian year.
242
+
243
+ Returns:
244
+ Gregorian date of 1st Waxing Day of Tazaungmon.
245
+ `None` if the Gregorian year is out of supported range.
246
+ """
247
+ if not (next_year_start_date := self._get_start_date(year + 1)):
248
+ return None
249
+
250
+ return _timedelta(next_year_start_date, -148)
251
+
252
+ def tazaungmon_full_moon_date(self, year: int) -> Optional[date]:
253
+ """Calculate the Gregorian date of Full Moon Day of Tazaungmon.
254
+
255
+ 15th day of 8th month (Tazaungmon).
256
+
257
+ To calculate, we use the following time delta:
258
+
259
+ * All years:
260
+ Year_length - 148 [8-12] + 15 - 1 = Year_length - 134.
261
+
262
+ Args:
263
+ year:
264
+ Gregorian year.
265
+
266
+ Returns:
267
+ Gregorian date of Full Moon Day of Tazaungmon.
268
+ `None` if the Gregorian year is out of supported range.
269
+ """
270
+ if not (next_year_start_date := self._get_start_date(year + 1)):
271
+ return None
272
+
273
+ return _timedelta(next_year_start_date, -134)
274
+
275
+ def pyatho_waxing_moon_date(self, year: int) -> Optional[date]:
276
+ """Calculate the Gregorian date of 1st Waxing Day of Pyatho.
277
+
278
+ 1st day of 10th month (Pyatho).
279
+
280
+ To calculate, we use the following time delta:
281
+
282
+ * All years:
283
+ Year_length - 89 [10-12] + 1 - 1 = Year_length - 89.
284
+
285
+ Args:
286
+ year:
287
+ Gregorian year.
288
+
289
+ Returns:
290
+ Gregorian date of 1st Waxing Day of Pyatho.
291
+ `None` if the Gregorian year is out of supported range.
292
+ """
293
+ if not (next_year_start_date := self._get_start_date(year + 1)):
294
+ return None
295
+
296
+ return _timedelta(next_year_start_date, -89)
297
+
298
+ def tabaung_full_moon_date(self, year: int) -> Optional[date]:
299
+ """Calculate the Gregorian date of Full Moon Day of Tabaung.
300
+
301
+ 15th day of 12th month (Tabaung).
302
+
303
+ To calculate, we use the following time delta:
304
+
305
+ * All years:
306
+ Year_length - 30 [12] + 15 - 1 = Year_length - 16.
307
+
308
+ Args:
309
+ year:
310
+ Gregorian year.
311
+
312
+ Returns:
313
+ Gregorian date of Full Moon Day of Tabaung.
314
+ `None` if the Gregorian year is out of supported range.
315
+ """
316
+ if not (next_year_start_date := self._get_start_date(year + 1)):
317
+ return None
318
+
319
+ return _timedelta(next_year_start_date, -16)
@@ -1602,8 +1602,8 @@ class _HebrewLunisolar:
1602
1602
  dt = getattr(self, f"{holiday}_DATES", {}).get(year, ())
1603
1603
  return date(year, *dt) if dt else None
1604
1604
 
1605
- def hanukkah_date(self, year: int) -> set[Optional[date]]:
1606
- return {self._get_holiday(HANUKKAH, y) for y in (year - 1, year)}
1605
+ def hanukkah_date(self, year: int) -> set[date]:
1606
+ return {dt for y in (year - 1, year) if (dt := self._get_holiday(HANUKKAH, y)) is not None}
1607
1607
 
1608
1608
  def israel_independence_date(self, year: int) -> Optional[date]:
1609
1609
  return self._get_holiday(INDEPENDENCE_DAY, year)
@@ -3970,24 +3970,44 @@ class _IslamicLunar:
3970
3970
  2076: (DEC, 5),
3971
3971
  }
3972
3972
 
3973
+ def __init__(self, calendar_delta_days: int = 0) -> None:
3974
+ """
3975
+ Args:
3976
+ calendar_delta_days:
3977
+ Number of days to shift all calculated holiday dates.
3978
+ Positive values move holidays forward, negative values move them backward.
3979
+ Defaults to 0 (no shift).
3980
+ """
3981
+ self.__calendar_delta_days = calendar_delta_days
3982
+
3973
3983
  def _get_holiday(self, holiday: str, year: int) -> Iterable[tuple[date, bool]]:
3974
3984
  confirmed_dates = getattr(
3975
3985
  self, f"{holiday}_DATES_{_CustomCalendar.CUSTOM_ATTR_POSTFIX}", {}
3976
3986
  )
3977
- confirmed_years = getattr(
3978
- self, f"{holiday}_DATES_CONFIRMED_YEARS_{_CustomCalendar.CUSTOM_ATTR_POSTFIX}", ()
3987
+ confirmed_years = _normalize_tuple(
3988
+ getattr(
3989
+ self, f"{holiday}_DATES_CONFIRMED_YEARS_{_CustomCalendar.CUSTOM_ATTR_POSTFIX}", ()
3990
+ )
3979
3991
  )
3980
3992
  estimated_dates = getattr(self, f"{holiday}_DATES", {})
3993
+ calendar_delta_days = self.__calendar_delta_days
3981
3994
 
3982
3995
  for check_year in (year - 1, year):
3996
+ is_confirmed_year = check_year in confirmed_dates
3983
3997
  for dt in _normalize_tuple(
3984
3998
  confirmed_dates.get(check_year, estimated_dates.get(check_year, ()))
3985
3999
  ):
3986
- is_confirmed = check_year in confirmed_dates or any(
3987
- year_from <= check_year <= year_to
3988
- for year_from, year_to in _normalize_tuple(confirmed_years)
4000
+ is_confirmed = is_confirmed_year or any(
4001
+ year_from <= check_year <= year_to for year_from, year_to in confirmed_years
4002
+ )
4003
+
4004
+ holiday_date = date(check_year, *dt)
4005
+ yield (
4006
+ _timedelta(holiday_date, calendar_delta_days)
4007
+ if calendar_delta_days and not is_confirmed_year
4008
+ else holiday_date,
4009
+ not is_confirmed,
3989
4010
  )
3990
- yield date(check_year, *dt), not is_confirmed
3991
4011
 
3992
4012
  def _is_long_ramadan(self, eid_al_fitr: date) -> bool:
3993
4013
  """Check whether the Ramadan preceding the given Eid al-Fitr date lasted 30 days.
@@ -11,7 +11,7 @@
11
11
  # License: MIT (see LICENSE file)
12
12
 
13
13
  from datetime import date
14
- from functools import lru_cache
14
+ from functools import cache
15
15
  from typing import Optional
16
16
 
17
17
  from holidays.calendars.gregorian import _timedelta
@@ -100,6 +100,8 @@ class _ThaiLunisolar:
100
100
  References:
101
101
  * <https://web.archive.org/web/20241016080156/https://www.ninenik.com/แนวทางฟังก์ชั่น_php_อย่างง่ายกับการหาวันข้างขึ้นข้างแรม-1021.html>
102
102
  * <https://web.archive.org/web/20250119171416/https://www.myhora.com/ปฏิทิน/ปฏิทิน-พ.ศ.2560.aspx>
103
+ * <https://web.archive.org/web/20250302100842/https://calendar.kunthet.com/#/>
104
+ * [2025 Khmer Lunar Calendar](https://web.archive.org/web/20250921045847/https://static1.squarespace.com/static/67722f65f2908e531b5f326d/t/678a87776503cc3f8bc538ac/1737131904673/2025Calendar-compressed.pdf)
103
105
 
104
106
  Example:
105
107
 
@@ -293,7 +295,7 @@ class _ThaiLunisolar:
293
295
  f"Unknown calendar name: {calendar}. Use `KHMER_CALENDAR` or `THAI_CALENDAR`."
294
296
  )
295
297
 
296
- @lru_cache
298
+ @cache
297
299
  def _get_start_date(self, year: int) -> Optional[date]:
298
300
  """Calculate the start date of that particular Thai Lunar Calendar Year.
299
301
 
@@ -320,6 +322,51 @@ class _ThaiLunisolar:
320
322
 
321
323
  return _timedelta(_ThaiLunisolar.START_DATE, delta_days)
322
324
 
325
+ @cache
326
+ def buddhist_sabbath_dates(self, year: int) -> set[date]:
327
+ """Return all Buddhist Sabbath (Uposatha) days in a given Gregorian year.
328
+
329
+ This function works independently of the calendar system in use,
330
+ whether `THAI_CALENDAR` or `KHMER_CALENDAR`.
331
+
332
+ Args:
333
+ year:
334
+ The Gregorian year.
335
+
336
+ Returns:
337
+ A set of `date` objects representing all Buddhist Sabbath days in the specified
338
+ Gregorian year. Returns an empty set if the year is outside the supported range.
339
+ """
340
+ start_date = self._get_start_date(year)
341
+ if not start_date:
342
+ return set()
343
+
344
+ # Initializes Thai lunar month lengths.
345
+ months = [29, 30] * 6
346
+ if year in _ThaiLunisolar.ATHIKAMAT_YEARS_GREGORIAN:
347
+ months.insert(7, 30)
348
+ elif year in _ThaiLunisolar.ATHIKAWAN_YEARS_GREGORIAN:
349
+ months[6] += 1
350
+ # Includes first two months of the next Thai lunar year to ensure all Buddhist Sabbaths
351
+ # in the Gregorian year are captured.
352
+ months.extend([29, 30])
353
+
354
+ buddhist_sabbaths: set[date] = set()
355
+ day_cursor = start_date
356
+ for month_days in months:
357
+ if day_cursor.year > year:
358
+ break
359
+ # Buddhist Sabbaths: 8 Waxing, 15 Waxing, 8 Waning, 14/15 Waning.
360
+ for offset in (7, 14, 22, month_days - 1):
361
+ buddhist_sabbath = _timedelta(day_cursor, offset)
362
+ if buddhist_sabbath.year == year:
363
+ buddhist_sabbaths.add(buddhist_sabbath)
364
+ elif buddhist_sabbath.year > year:
365
+ break
366
+ day_cursor = _timedelta(day_cursor, month_days)
367
+
368
+ return buddhist_sabbaths
369
+
323
370
  def makha_bucha_date(self, year: int, calendar=None) -> Optional[date]:
324
371
  """Calculate the estimated Gregorian date of Makha Bucha.
325
372
 
@@ -20,6 +20,7 @@ from holidays.countries.american_samoa import AmericanSamoa, AS, ASM, HolidaysAS
20
20
  from holidays.countries.andorra import Andorra, AD, AND
21
21
  from holidays.countries.angola import Angola, AO, AGO
22
22
  from holidays.countries.anguilla import Anguilla, AI, AIA
23
+ from holidays.countries.antarctica import Antarctica, AQ, ATA
23
24
  from holidays.countries.antigua_and_barbuda import AntiguaAndBarbuda, AG, ATG
24
25
  from holidays.countries.argentina import Argentina, AR, ARG
25
26
  from holidays.countries.armenia import Armenia, AM, ARM
@@ -41,7 +42,9 @@ from holidays.countries.bolivia import Bolivia, BO, BOL
41
42
  from holidays.countries.bonaire_sint_eustatius_and_saba import BonaireSintEustatiusAndSaba, BQ, BES
42
43
  from holidays.countries.bosnia_and_herzegovina import BosniaAndHerzegovina, BA, BIH
43
44
  from holidays.countries.botswana import Botswana, BW, BWA
45
+ from holidays.countries.bouvet_island import BouvetIsland, BV, BVT
44
46
  from holidays.countries.brazil import Brazil, BR, BRA
47
+ from holidays.countries.british_indian_ocean_territory import BritishIndianOceanTerritory, IO, IOT
45
48
  from holidays.countries.british_virgin_islands import BritishVirginIslands, VG, VGB
46
49
  from holidays.countries.brunei import Brunei, BN, BRN
47
50
  from holidays.countries.bulgaria import Bulgaria, BG, BLG
@@ -111,6 +114,11 @@ from holidays.countries.guinea import Guinea, GN, GIN
111
114
  from holidays.countries.guinea_bissau import GuineaBissau, GW, GNB
112
115
  from holidays.countries.guyana import Guyana, GY, GUY
113
116
  from holidays.countries.haiti import Haiti, HT, HTI
117
+ from holidays.countries.heard_island_and_mcdonald_islands import (
118
+ HeardIslandAndMcDonaldIslands,
119
+ HM,
120
+ HMD,
121
+ )
114
122
  from holidays.countries.honduras import Honduras, HN, HND
115
123
  from holidays.countries.hongkong import HongKong, HK, HKG
116
124
  from holidays.countries.hungary import Hungary, HU, HUN
@@ -163,6 +171,7 @@ from holidays.countries.montenegro import Montenegro, ME, MNE
163
171
  from holidays.countries.montserrat import Montserrat, MS, MSR
164
172
  from holidays.countries.morocco import Morocco, MA, MOR
165
173
  from holidays.countries.mozambique import Mozambique, MZ, MOZ
174
+ from holidays.countries.myanmar import Myanmar, MM, MMR
166
175
  from holidays.countries.namibia import Namibia, NA, NAM
167
176
  from holidays.countries.nauru import Nauru, NR, NRU
168
177
  from holidays.countries.nepal import Nepal, NP, NPL
@@ -174,6 +183,7 @@ from holidays.countries.niger import Niger, NE, NER
174
183
  from holidays.countries.nigeria import Nigeria, NG, NGA
175
184
  from holidays.countries.niue import Niue, NU, NIU
176
185
  from holidays.countries.norfolk_island import NorfolkIsland, NF, NFK
186
+ from holidays.countries.north_korea import NorthKorea, KP, PRK
177
187
  from holidays.countries.north_macedonia import NorthMacedonia, MK, MKD
178
188
  from holidays.countries.northern_mariana_islands import NorthernMarianaIslands, MP, MNP, HolidaysMP
179
189
  from holidays.countries.norway import Norway, NO, NOR
@@ -283,6 +293,7 @@ from holidays.countries.vatican_city import VaticanCity, VA, VAT
283
293
  from holidays.countries.venezuela import Venezuela, VE, VEN
284
294
  from holidays.countries.vietnam import Vietnam, VN, VNM
285
295
  from holidays.countries.wallis_and_futuna import WallisAndFutuna, WF, WLF, HolidaysWF
296
+ from holidays.countries.western_sahara import WesternSahara, EH, ESH
286
297
  from holidays.countries.yemen import Yemen, YE, YEM
287
298
  from holidays.countries.zambia import Zambia, ZM, ZMB
288
299
  from holidays.countries.zimbabwe import Zimbabwe, ZW, ZWE
@@ -23,7 +23,7 @@ class Afghanistan(HolidayBase, InternationalHolidays, IslamicHolidays, PersianCa
23
23
 
24
24
  References:
25
25
  * <https://en.wikipedia.org/wiki/Public_holidays_in_Afghanistan>
26
- * <https://web.archive.org/web/20250408032244/https://www.timeanddate.com/holidays/afghanistan>
26
+ * <https://web.archive.org/web/20250903162748/https://www.timeanddate.com/holidays/afghanistan>
27
27
  * <https://en.wikipedia.org/wiki/Workweek_and_weekend>
28
28
  """
29
29
 
@@ -131,16 +131,17 @@ class AfghanistanIslamicHolidays(_CustomIslamicHolidays):
131
131
  2021: (AUG, 19),
132
132
  }
133
133
 
134
- EID_AL_ADHA_DATES_CONFIRMED_YEARS = (2014, 2024)
134
+ EID_AL_ADHA_DATES_CONFIRMED_YEARS = (2014, 2025)
135
135
  EID_AL_ADHA_DATES = {
136
136
  2014: (OCT, 5),
137
137
  2016: (SEP, 13),
138
138
  2017: (SEP, 2),
139
139
  2018: (AUG, 22),
140
140
  2024: (JUN, 17),
141
+ 2025: (JUN, 7),
141
142
  }
142
143
 
143
- EID_AL_FITR_DATES_CONFIRMED_YEARS = (2014, 2024)
144
+ EID_AL_FITR_DATES_CONFIRMED_YEARS = (2014, 2025)
144
145
  EID_AL_FITR_DATES = {
145
146
  2014: (JUL, 29),
146
147
  2015: (JUL, 18),
@@ -150,7 +151,7 @@ class AfghanistanIslamicHolidays(_CustomIslamicHolidays):
150
151
  2023: (APR, 22),
151
152
  }
152
153
 
153
- MAWLID_DATES_CONFIRMED_YEARS = (2014, 2024)
154
+ MAWLID_DATES_CONFIRMED_YEARS = (2014, 2025)
154
155
  MAWLID_DATES = {
155
156
  2014: (JAN, 14),
156
157
  2015: ((JAN, 3), (DEC, 24)),
@@ -160,9 +161,10 @@ class AfghanistanIslamicHolidays(_CustomIslamicHolidays):
160
161
  2019: (NOV, 10),
161
162
  2021: (OCT, 19),
162
163
  2024: (SEP, 16),
164
+ 2025: (SEP, 5),
163
165
  }
164
166
 
165
- RAMADAN_BEGINNING_DATES_CONFIRMED_YEARS = (2014, 2024)
167
+ RAMADAN_BEGINNING_DATES_CONFIRMED_YEARS = (2014, 2025)
166
168
  RAMADAN_BEGINNING_DATES = {
167
169
  2014: (JUN, 29),
168
170
  2016: (JUN, 7),