holidays 0.68__py3-none-any.whl → 0.70__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 (254) hide show
  1. holidays/__init__.py +1 -1
  2. holidays/calendars/__init__.py +2 -1
  3. holidays/calendars/balinese_saka.py +112 -0
  4. holidays/calendars/buddhist.py +1 -1
  5. holidays/calendars/chinese.py +1 -1
  6. holidays/calendars/custom.py +1 -1
  7. holidays/calendars/gregorian.py +1 -1
  8. holidays/calendars/hebrew.py +1 -1
  9. holidays/calendars/hindu.py +866 -2
  10. holidays/calendars/islamic.py +161 -1
  11. holidays/calendars/julian.py +1 -1
  12. holidays/calendars/julian_revised.py +1 -1
  13. holidays/calendars/persian.py +1 -1
  14. holidays/calendars/sinhala.py +1 -1
  15. holidays/calendars/thai.py +309 -257
  16. holidays/constants.py +3 -1
  17. holidays/countries/__init__.py +7 -1
  18. holidays/countries/afghanistan.py +17 -7
  19. holidays/countries/albania.py +16 -7
  20. holidays/countries/algeria.py +14 -10
  21. holidays/countries/american_samoa.py +12 -6
  22. holidays/countries/andorra.py +5 -4
  23. holidays/countries/angola.py +15 -14
  24. holidays/countries/antigua_and_barbuda.py +145 -0
  25. holidays/countries/argentina.py +787 -169
  26. holidays/countries/armenia.py +5 -6
  27. holidays/countries/aruba.py +11 -9
  28. holidays/countries/australia.py +21 -20
  29. holidays/countries/austria.py +3 -1
  30. holidays/countries/azerbaijan.py +30 -19
  31. holidays/countries/bahamas.py +13 -11
  32. holidays/countries/bahrain.py +14 -7
  33. holidays/countries/bangladesh.py +5 -4
  34. holidays/countries/barbados.py +11 -9
  35. holidays/countries/belarus.py +15 -15
  36. holidays/countries/belgium.py +8 -6
  37. holidays/countries/belize.py +7 -6
  38. holidays/countries/bolivia.py +12 -11
  39. holidays/countries/bosnia_and_herzegovina.py +21 -11
  40. holidays/countries/botswana.py +8 -6
  41. holidays/countries/brazil.py +8 -7
  42. holidays/countries/brunei.py +56 -62
  43. holidays/countries/bulgaria.py +9 -10
  44. holidays/countries/burkina_faso.py +14 -5
  45. holidays/countries/burundi.py +17 -12
  46. holidays/countries/cambodia.py +15 -24
  47. holidays/countries/cameroon.py +16 -7
  48. holidays/countries/canada.py +13 -12
  49. holidays/countries/chad.py +15 -6
  50. holidays/countries/chile.py +29 -28
  51. holidays/countries/china.py +39 -38
  52. holidays/countries/colombia.py +15 -20
  53. holidays/countries/congo.py +6 -7
  54. holidays/countries/costa_rica.py +11 -10
  55. holidays/countries/croatia.py +8 -5
  56. holidays/countries/cuba.py +30 -27
  57. holidays/countries/curacao.py +6 -4
  58. holidays/countries/cyprus.py +4 -5
  59. holidays/countries/czechia.py +7 -6
  60. holidays/countries/denmark.py +5 -6
  61. holidays/countries/djibouti.py +11 -3
  62. holidays/countries/dominica.py +18 -16
  63. holidays/countries/dominican_republic.py +6 -4
  64. holidays/countries/ecuador.py +5 -4
  65. holidays/countries/egypt.py +10 -11
  66. holidays/countries/el_salvador.py +6 -5
  67. holidays/countries/estonia.py +3 -1
  68. holidays/countries/eswatini.py +6 -4
  69. holidays/countries/ethiopia.py +26 -11
  70. holidays/countries/fiji.py +183 -0
  71. holidays/countries/finland.py +11 -10
  72. holidays/countries/france.py +6 -9
  73. holidays/countries/gabon.py +17 -8
  74. holidays/countries/georgia.py +7 -7
  75. holidays/countries/germany.py +11 -11
  76. holidays/countries/ghana.py +14 -6
  77. holidays/countries/greece.py +4 -5
  78. holidays/countries/greenland.py +5 -6
  79. holidays/countries/guam.py +12 -6
  80. holidays/countries/guatemala.py +7 -9
  81. holidays/countries/guernsey.py +37 -34
  82. holidays/countries/guinea.py +182 -0
  83. holidays/countries/haiti.py +6 -5
  84. holidays/countries/honduras.py +8 -4
  85. holidays/countries/hongkong.py +49 -60
  86. holidays/countries/hungary.py +26 -23
  87. holidays/countries/iceland.py +5 -4
  88. holidays/countries/india.py +494 -174
  89. holidays/countries/indonesia.py +68 -108
  90. holidays/countries/iran.py +18 -9
  91. holidays/countries/ireland.py +5 -4
  92. holidays/countries/isle_of_man.py +2 -2
  93. holidays/countries/israel.py +4 -5
  94. holidays/countries/italy.py +5 -4
  95. holidays/countries/ivory_coast.py +156 -0
  96. holidays/countries/jamaica.py +6 -4
  97. holidays/countries/japan.py +5 -5
  98. holidays/countries/jersey.py +29 -26
  99. holidays/countries/jordan.py +13 -6
  100. holidays/countries/kazakhstan.py +72 -51
  101. holidays/countries/kenya.py +28 -18
  102. holidays/countries/kuwait.py +14 -7
  103. holidays/countries/kyrgyzstan.py +11 -6
  104. holidays/countries/laos.py +21 -29
  105. holidays/countries/latvia.py +7 -5
  106. holidays/countries/lesotho.py +6 -5
  107. holidays/countries/liechtenstein.py +5 -5
  108. holidays/countries/lithuania.py +4 -5
  109. holidays/countries/luxembourg.py +5 -3
  110. holidays/countries/macau.py +480 -0
  111. holidays/countries/madagascar.py +5 -4
  112. holidays/countries/malawi.py +6 -4
  113. holidays/countries/malaysia.py +30 -18
  114. holidays/countries/maldives.py +14 -7
  115. holidays/countries/malta.py +10 -19
  116. holidays/countries/marshall_islands.py +6 -4
  117. holidays/countries/mauritania.py +13 -6
  118. holidays/countries/mexico.py +8 -7
  119. holidays/countries/moldova.py +6 -4
  120. holidays/countries/monaco.py +6 -4
  121. holidays/countries/montenegro.py +16 -7
  122. holidays/countries/morocco.py +13 -8
  123. holidays/countries/mozambique.py +3 -1
  124. holidays/countries/namibia.py +7 -5
  125. holidays/countries/netherlands.py +6 -6
  126. holidays/countries/new_zealand.py +3 -1
  127. holidays/countries/nicaragua.py +6 -5
  128. holidays/countries/nigeria.py +13 -5
  129. holidays/countries/north_macedonia.py +13 -5
  130. holidays/countries/northern_mariana_islands.py +12 -6
  131. holidays/countries/norway.py +15 -15
  132. holidays/countries/pakistan.py +48 -18
  133. holidays/countries/palau.py +13 -11
  134. holidays/countries/panama.py +9 -8
  135. holidays/countries/papua_new_guinea.py +25 -21
  136. holidays/countries/paraguay.py +10 -9
  137. holidays/countries/peru.py +4 -5
  138. holidays/countries/philippines.py +32 -18
  139. holidays/countries/poland.py +7 -6
  140. holidays/countries/portugal.py +13 -15
  141. holidays/countries/puerto_rico.py +12 -6
  142. holidays/countries/qatar.py +172 -0
  143. holidays/countries/romania.py +6 -4
  144. holidays/countries/russia.py +6 -4
  145. holidays/countries/saint_kitts_and_nevis.py +24 -22
  146. holidays/countries/saint_lucia.py +8 -7
  147. holidays/countries/samoa.py +7 -6
  148. holidays/countries/san_marino.py +4 -3
  149. holidays/countries/saudi_arabia.py +15 -15
  150. holidays/countries/serbia.py +3 -4
  151. holidays/countries/seychelles.py +22 -26
  152. holidays/countries/singapore.py +33 -38
  153. holidays/countries/slovakia.py +6 -5
  154. holidays/countries/slovenia.py +7 -6
  155. holidays/countries/south_africa.py +8 -6
  156. holidays/countries/south_korea.py +25 -32
  157. holidays/countries/spain.py +31 -24
  158. holidays/countries/sri_lanka.py +52 -42
  159. holidays/countries/sweden.py +20 -19
  160. holidays/countries/switzerland.py +6 -5
  161. holidays/countries/taiwan.py +230 -31
  162. holidays/countries/tanzania.py +34 -27
  163. holidays/countries/thailand.py +134 -142
  164. holidays/countries/timor_leste.py +38 -17
  165. holidays/countries/tonga.py +46 -42
  166. holidays/countries/tunisia.py +9 -3
  167. holidays/countries/turkey.py +17 -9
  168. holidays/countries/tuvalu.py +12 -11
  169. holidays/countries/ukraine.py +54 -54
  170. holidays/countries/united_arab_emirates.py +43 -30
  171. holidays/countries/united_kingdom.py +7 -6
  172. holidays/countries/united_states.py +130 -86
  173. holidays/countries/united_states_minor_outlying_islands.py +12 -6
  174. holidays/countries/united_states_virgin_islands.py +12 -6
  175. holidays/countries/uruguay.py +10 -9
  176. holidays/countries/uzbekistan.py +16 -7
  177. holidays/countries/vanuatu.py +7 -5
  178. holidays/countries/vatican_city.py +16 -15
  179. holidays/countries/venezuela.py +11 -14
  180. holidays/countries/vietnam.py +15 -11
  181. holidays/countries/zambia.py +8 -6
  182. holidays/countries/zimbabwe.py +6 -4
  183. holidays/deprecations/v1_incompatibility.py +1 -1
  184. holidays/financial/__init__.py +1 -1
  185. holidays/financial/brasil_bolsa_balcao.py +14 -13
  186. holidays/financial/european_central_bank.py +7 -6
  187. holidays/financial/ice_futures_europe.py +7 -6
  188. holidays/financial/ny_stock_exchange.py +13 -10
  189. holidays/groups/__init__.py +2 -1
  190. holidays/groups/balinese_saka.py +45 -0
  191. holidays/groups/buddhist.py +1 -1
  192. holidays/groups/chinese.py +69 -4
  193. holidays/groups/christian.py +1 -1
  194. holidays/groups/custom.py +1 -1
  195. holidays/groups/eastern.py +1 -1
  196. holidays/groups/hebrew.py +1 -1
  197. holidays/groups/hindu.py +256 -1
  198. holidays/groups/international.py +1 -1
  199. holidays/groups/islamic.py +17 -3
  200. holidays/groups/persian.py +1 -1
  201. holidays/groups/sinhala.py +1 -1
  202. holidays/groups/thai.py +1 -7
  203. holidays/helpers.py +1 -1
  204. holidays/holiday_base.py +410 -273
  205. holidays/ical.py +228 -0
  206. holidays/locale/ar_QA/LC_MESSAGES/QA.mo +0 -0
  207. holidays/locale/de/LC_MESSAGES/PL.mo +0 -0
  208. holidays/locale/en_CI/LC_MESSAGES/CI.mo +0 -0
  209. holidays/locale/en_HK/LC_MESSAGES/HK.mo +0 -0
  210. holidays/locale/en_IN/LC_MESSAGES/IN.mo +0 -0
  211. holidays/locale/en_MO/LC_MESSAGES/MO.mo +0 -0
  212. holidays/locale/en_PK/LC_MESSAGES/PK.mo +0 -0
  213. holidays/locale/en_TL/LC_MESSAGES/TL.mo +0 -0
  214. holidays/locale/en_US/LC_MESSAGES/AR.mo +0 -0
  215. holidays/locale/en_US/LC_MESSAGES/CI.mo +0 -0
  216. holidays/locale/en_US/LC_MESSAGES/GN.mo +0 -0
  217. holidays/locale/en_US/LC_MESSAGES/HK.mo +0 -0
  218. holidays/locale/en_US/LC_MESSAGES/IN.mo +0 -0
  219. holidays/locale/en_US/LC_MESSAGES/MO.mo +0 -0
  220. holidays/locale/en_US/LC_MESSAGES/PK.mo +0 -0
  221. holidays/locale/en_US/LC_MESSAGES/QA.mo +0 -0
  222. holidays/locale/en_US/LC_MESSAGES/TL.mo +0 -0
  223. holidays/locale/en_US/LC_MESSAGES/TW.mo +0 -0
  224. holidays/locale/es/LC_MESSAGES/AR.mo +0 -0
  225. holidays/locale/fr/LC_MESSAGES/CI.mo +0 -0
  226. holidays/locale/fr/LC_MESSAGES/GN.mo +0 -0
  227. holidays/locale/hi/LC_MESSAGES/IN.mo +0 -0
  228. holidays/locale/pt_MO/LC_MESSAGES/MO.mo +0 -0
  229. holidays/locale/pt_TL/LC_MESSAGES/TL.mo +0 -0
  230. holidays/locale/tet/LC_MESSAGES/TL.mo +0 -0
  231. holidays/locale/th/LC_MESSAGES/HK.mo +0 -0
  232. holidays/locale/th/LC_MESSAGES/MO.mo +0 -0
  233. holidays/locale/th/LC_MESSAGES/TL.mo +0 -0
  234. holidays/locale/th/LC_MESSAGES/TW.mo +0 -0
  235. holidays/locale/uk/LC_MESSAGES/AR.mo +0 -0
  236. holidays/locale/ur_PK/LC_MESSAGES/PK.mo +0 -0
  237. holidays/locale/zh_CN/LC_MESSAGES/HK.mo +0 -0
  238. holidays/locale/zh_CN/LC_MESSAGES/MO.mo +0 -0
  239. holidays/locale/zh_CN/LC_MESSAGES/TW.mo +0 -0
  240. holidays/locale/zh_HK/LC_MESSAGES/HK.mo +0 -0
  241. holidays/locale/zh_MO/LC_MESSAGES/MO.mo +0 -0
  242. holidays/locale/zh_TW/LC_MESSAGES/TW.mo +0 -0
  243. holidays/mixins.py +2 -2
  244. holidays/observed_holiday_base.py +5 -2
  245. holidays/registry.py +7 -1
  246. holidays/utils.py +151 -151
  247. holidays/version.py +2 -2
  248. holidays-0.70.dist-info/METADATA +1404 -0
  249. {holidays-0.68.dist-info → holidays-0.70.dist-info}/RECORD +253 -222
  250. {holidays-0.68.dist-info → holidays-0.70.dist-info}/WHEEL +1 -1
  251. holidays-0.68.dist-info/AUTHORS → holidays-0.70.dist-info/licenses/AUTHORS.md +7 -2
  252. {holidays-0.68.dist-info → holidays-0.70.dist-info/licenses}/LICENSE +1 -1
  253. holidays-0.68.dist-info/METADATA +0 -1080
  254. {holidays-0.68.dist-info → holidays-0.70.dist-info}/top_level.txt +0 -0
holidays/holiday_base.py CHANGED
@@ -4,7 +4,7 @@
4
4
  # specific sets of holidays on the fly. It aims to make determining whether a
5
5
  # specific date is a holiday as fast and flexible as possible.
6
6
  #
7
- # Authors: Vacanza Team and individual contributors (see AUTHORS file)
7
+ # Authors: Vacanza Team and individual contributors (see AUTHORS.md file)
8
8
  # dr-prodigy <dr.prodigy.github@gmail.com> (c) 2017-2023
9
9
  # ryanss <ryanssdev@icloud.com> (c) 2014-2017
10
10
  # Website: https://github.com/vacanza/holidays
@@ -56,19 +56,19 @@ YearArg = Union[int, Iterable[int]]
56
56
 
57
57
  class HolidayBase(dict[date, str]):
58
58
  """
59
- A dict-like object containing the holidays for a specific country (and
60
- province or state if so initiated); inherits the dict class (so behaves
61
- similarly to a dict). Dates without a key in the Holiday object are not
59
+ A `dict`-like object containing the holidays for a specific country (and
60
+ province or state if so initiated); inherits the `dict` class (so behaves
61
+ similarly to a `dict`). Dates without a key in the Holiday object are not
62
62
  holidays.
63
63
 
64
64
  The key of the object is the date of the holiday and the value is the name
65
65
  of the holiday itself. When passing the date as a key, the date can be
66
66
  expressed as one of the following formats:
67
67
 
68
- * datetime.datetime type;
69
- * datetime.date types;
70
- * a float representing a Unix timestamp;
71
- * or a string of any format (recognized by datetime.parse).
68
+ * `datetime.datetime` type;
69
+ * `datetime.date` types;
70
+ * a `float` representing a Unix timestamp;
71
+ * or a string of any format (recognized by `dateutil.parser.parse()`).
72
72
 
73
73
  The key is always returned as a `datetime.date` object.
74
74
 
@@ -78,11 +78,12 @@ class HolidayBase(dict[date, str]):
78
78
  holidays. To pre-populate holidays, instantiate the class with the years
79
79
  argument:
80
80
 
81
- us_holidays = holidays.US(years=2020)
81
+ us_holidays = holidays.US(years=2020)
82
82
 
83
- It is generally instantiated using the :func:`country_holidays` function.
83
+ It is generally instantiated using the
84
+ [country_holidays()][holidays.utils.country_holidays] function.
84
85
 
85
- The key of the :class:`dict`-like :class:`HolidayBase` object is the
86
+ The key of the `dict`-like `HolidayBase` object is the
86
87
  `date` of the holiday, and the value is the name of the holiday itself.
87
88
  Dates where a key is not present are not public holidays (or, if
88
89
  **observed** is False, days when a public holiday is observed).
@@ -90,12 +91,12 @@ class HolidayBase(dict[date, str]):
90
91
  When passing the `date` as a key, the `date` can be expressed in one of the
91
92
  following types:
92
93
 
93
- * :class:`datetime.date`,
94
- * :class:`datetime.datetime`,
95
- * a :class:`str` of any format recognized by :func:`dateutil.parser.parse`,
96
- * or a :class:`float` or :class:`int` representing a POSIX timestamp.
94
+ * `datetime.date`,
95
+ * `datetime.datetime`,
96
+ * a `str` of any format recognized by `dateutil.parser.parse()`,
97
+ * or a `float` or `int` representing a POSIX timestamp.
97
98
 
98
- The key is always returned as a :class:`datetime.date` object.
99
+ The key is always returned as a `datetime.date` object.
99
100
 
100
101
  To maximize speed, the list of public holidays is built on the fly as
101
102
  needed, one calendar year at a time. When the object is instantiated
@@ -108,70 +109,70 @@ class HolidayBase(dict[date, str]):
108
109
 
109
110
  Example usage:
110
111
 
111
- >>> from holidays import country_holidays
112
- >>> us_holidays = country_holidays('US')
113
- # For a specific subdivisions (e.g. state or province):
114
- >>> california_holidays = country_holidays('US', subdiv='CA')
112
+ >>> from holidays import country_holidays
113
+ >>> us_holidays = country_holidays('US')
114
+ # For a specific subdivisions (e.g. state or province):
115
+ >>> california_holidays = country_holidays('US', subdiv='CA')
115
116
 
116
117
  The below will cause 2015 holidays to be calculated on the fly:
117
118
 
118
- >>> from datetime import date
119
- >>> assert date(2015, 1, 1) in us_holidays
119
+ >>> from datetime import date
120
+ >>> assert date(2015, 1, 1) in us_holidays
120
121
 
121
122
  This will be faster because 2015 holidays are already calculated:
122
123
 
123
- >>> assert date(2015, 1, 2) not in us_holidays
124
+ >>> assert date(2015, 1, 2) not in us_holidays
124
125
 
125
- The :class:`HolidayBase` class also recognizes strings of many formats
126
+ The `HolidayBase` class also recognizes strings of many formats
126
127
  and numbers representing a POSIX timestamp:
127
128
 
128
- >>> assert '2014-01-01' in us_holidays
129
- >>> assert '1/1/2014' in us_holidays
130
- >>> assert 1388597445 in us_holidays
129
+ >>> assert '2014-01-01' in us_holidays
130
+ >>> assert '1/1/2014' in us_holidays
131
+ >>> assert 1388597445 in us_holidays
131
132
 
132
133
  Show the holiday's name:
133
134
 
134
- >>> us_holidays.get('2014-01-01')
135
- "New Year's Day"
135
+ >>> us_holidays.get('2014-01-01')
136
+ "New Year's Day"
136
137
 
137
138
  Check a range:
138
139
 
139
- >>> us_holidays['2014-01-01': '2014-01-03']
140
- [datetime.date(2014, 1, 1)]
140
+ >>> us_holidays['2014-01-01': '2014-01-03']
141
+ [datetime.date(2014, 1, 1)]
141
142
 
142
143
  List all 2020 holidays:
143
144
 
144
- >>> us_holidays = country_holidays('US', years=2020)
145
- >>> for day in us_holidays.items():
146
- ... print(day)
147
- (datetime.date(2020, 1, 1), "New Year's Day")
148
- (datetime.date(2020, 1, 20), 'Martin Luther King Jr. Day')
149
- (datetime.date(2020, 2, 17), "Washington's Birthday")
150
- (datetime.date(2020, 5, 25), 'Memorial Day')
151
- (datetime.date(2020, 7, 4), 'Independence Day')
152
- (datetime.date(2020, 7, 3), 'Independence Day (observed)')
153
- (datetime.date(2020, 9, 7), 'Labor Day')
154
- (datetime.date(2020, 10, 12), 'Columbus Day')
155
- (datetime.date(2020, 11, 11), 'Veterans Day')
156
- (datetime.date(2020, 11, 26), 'Thanksgiving')
157
- (datetime.date(2020, 12, 25), 'Christmas Day')
145
+ >>> us_holidays = country_holidays('US', years=2020)
146
+ >>> for day in sorted(us_holidays.items()):
147
+ ... print(day)
148
+ (datetime.date(2020, 1, 1), "New Year's Day")
149
+ (datetime.date(2020, 1, 20), 'Martin Luther King Jr. Day')
150
+ (datetime.date(2020, 2, 17), "Washington's Birthday")
151
+ (datetime.date(2020, 5, 25), 'Memorial Day')
152
+ (datetime.date(2020, 7, 3), 'Independence Day (observed)')
153
+ (datetime.date(2020, 7, 4), 'Independence Day')
154
+ (datetime.date(2020, 9, 7), 'Labor Day')
155
+ (datetime.date(2020, 10, 12), 'Columbus Day')
156
+ (datetime.date(2020, 11, 11), 'Veterans Day')
157
+ (datetime.date(2020, 11, 26), 'Thanksgiving Day')
158
+ (datetime.date(2020, 12, 25), 'Christmas Day')
158
159
 
159
160
  Some holidays are only present in parts of a country:
160
161
 
161
- >>> us_pr_holidays = country_holidays('US', subdiv='PR')
162
- >>> assert '2018-01-06' not in us_holidays
163
- >>> assert '2018-01-06' in us_pr_holidays
162
+ >>> us_pr_holidays = country_holidays('US', subdiv='PR')
163
+ >>> assert '2018-01-06' not in us_holidays
164
+ >>> assert '2018-01-06' in us_pr_holidays
164
165
 
165
166
  Append custom holiday dates by passing one of:
166
167
 
167
- * a :class:`dict` with date/name key/value pairs (e.g.
168
- ``{'2010-07-10': 'My birthday!'}``),
169
- * a list of dates (as a :class:`datetime.date`, :class:`datetime.datetime`,
170
- :class:`str`, :class:`int`, or :class:`float`); ``'Holiday'`` will be
171
- used as a description,
172
- * or a single date item (of one of the types above); ``'Holiday'`` will be
168
+ * a `dict` with date/name key/value pairs (e.g.
169
+ `{'2010-07-10': 'My birthday!'}`),
170
+ * a list of dates (as a `datetime.date`, `datetime.datetime`,
171
+ `str`, `int`, or `float`); `'Holiday'` will be used as a description,
172
+ * or a single date item (of one of the types above); `'Holiday'` will be
173
173
  used as a description:
174
174
 
175
+ ```python
175
176
  >>> custom_holidays = country_holidays('US', years=2015)
176
177
  >>> custom_holidays.update({'2015-01-01': "New Year's Day"})
177
178
  >>> custom_holidays.update(['2015-07-01', '07/04/2015'])
@@ -179,11 +180,10 @@ class HolidayBase(dict[date, str]):
179
180
  >>> assert date(2015, 1, 1) in custom_holidays
180
181
  >>> assert date(2015, 1, 2) not in custom_holidays
181
182
  >>> assert '12/25/2015' in custom_holidays
183
+ ```
182
184
 
183
185
  For special (one-off) country-wide holidays handling use
184
- :attr:`special_public_holidays`:
185
-
186
- .. code-block:: python
186
+ `special_public_holidays`:
187
187
 
188
188
  special_public_holidays = {
189
189
  1977: ((JUN, 7, "Silver Jubilee of Elizabeth II"),),
@@ -204,7 +204,8 @@ class HolidayBase(dict[date, str]):
204
204
  ...
205
205
 
206
206
  For more complex logic, like 4th Monday of January, you can inherit the
207
- :class:`HolidayBase` class and define your own :meth:`_populate` method.
207
+ [HolidayBase][holidays.holiday_base.HolidayBase] class and define your own `_populate()`
208
+ method.
208
209
  See documentation for examples.
209
210
  """
210
211
 
@@ -265,39 +266,40 @@ class HolidayBase(dict[date, str]):
265
266
  categories: Optional[CategoryArg] = None,
266
267
  ) -> None:
267
268
  """
268
- :param years:
269
- The year(s) to pre-calculate public holidays for at instantiation.
269
+ Args:
270
+ years:
271
+ The year(s) to pre-calculate public holidays for at instantiation.
270
272
 
271
- :param expand:
272
- Whether the entire year is calculated when one date from that year
273
- is requested.
273
+ expand:
274
+ Whether the entire year is calculated when one date from that year
275
+ is requested.
274
276
 
275
- :param observed:
276
- Whether to include the dates when public holiday are observed
277
- (e.g. a holiday falling on a Sunday being observed the
278
- following Monday). This doesn't work for all countries.
277
+ observed:
278
+ Whether to include the dates when public holiday are observed
279
+ (e.g. a holiday falling on a Sunday being observed the
280
+ following Monday). This doesn't work for all countries.
279
281
 
280
- :param subdiv:
281
- The subdivision (e.g. state or province) as a ISO 3166-2 code
282
- or its alias; not implemented for all countries (see documentation).
282
+ subdiv:
283
+ The subdivision (e.g. state or province) as a ISO 3166-2 code
284
+ or its alias; not implemented for all countries (see documentation).
283
285
 
284
- :param prov:
285
- *deprecated* use subdiv instead.
286
+ prov:
287
+ *deprecated* use `subdiv` instead.
286
288
 
287
- :param state:
288
- *deprecated* use subdiv instead.
289
+ state:
290
+ *deprecated* use `subdiv` instead.
289
291
 
290
- :param language:
291
- The language which the returned holiday names will be translated
292
- into. It must be an ISO 639-1 (2-letter) language code. If the
293
- language translation is not supported the original holiday names
294
- will be used.
292
+ language:
293
+ The language which the returned holiday names will be translated
294
+ into. It must be an ISO 639-1 (2-letter) language code. If the
295
+ language translation is not supported the original holiday names
296
+ will be used.
295
297
 
296
- :param categories:
297
- Requested holiday categories.
298
+ categories:
299
+ Requested holiday categories.
298
300
 
299
- :return:
300
- A :class:`HolidayBase` object matching the **country**.
301
+ Returns:
302
+ A `HolidayBase` object matching the **country** or **market**.
301
303
  """
302
304
  super().__init__()
303
305
 
@@ -361,54 +363,29 @@ class HolidayBase(dict[date, str]):
361
363
  self.expand = expand
362
364
  self.has_special_holidays = getattr(self, "has_special_holidays", False)
363
365
  self.has_substituted_holidays = has_substituted_holidays
364
- self.language = language.lower() if language else None
366
+ self.language = language
365
367
  self.observed = observed
366
368
  self.subdiv = subdiv
367
369
  self.weekend_workdays = getattr(self, "weekend_workdays", set())
368
-
369
- supported_languages = set(self.supported_languages)
370
- if self._entity_code is not None:
371
- fallback = language not in supported_languages
372
- languages = [language] if language in supported_languages else None
373
- locale_directory = str(Path(__file__).with_name("locale"))
374
-
375
- # Add entity native content translations.
376
- entity_translation = translation(
377
- self._entity_code,
378
- fallback=fallback,
379
- languages=languages,
380
- localedir=locale_directory,
381
- )
382
- # Add a fallback if entity has parent translations.
383
- if parent_entity := self.parent_entity:
384
- entity_translation.add_fallback(
385
- translation(
386
- parent_entity.country or parent_entity.market,
387
- fallback=fallback,
388
- languages=languages,
389
- localedir=locale_directory,
390
- )
391
- )
392
- self.tr = entity_translation.gettext
393
- else:
394
- self.tr = gettext
395
-
396
370
  self.years = _normalize_arguments(int, years)
397
371
 
372
+ # Configure l10n related attributes.
373
+ self._init_translation()
374
+
398
375
  # Populate holidays.
399
376
  for year in self.years:
400
377
  self._populate(year)
401
378
 
402
379
  def __add__(self, other: Union[int, "HolidayBase", "HolidaySum"]) -> "HolidayBase":
403
380
  """Add another dictionary of public holidays creating a
404
- :class:`HolidaySum` object.
381
+ [HolidaySum][holidays.holiday_base.HolidaySum] object.
405
382
 
406
- :param other:
407
- The dictionary of public holiday to be added.
383
+ Args:
384
+ other:
385
+ The dictionary of public holiday to be added.
408
386
 
409
- :return:
410
- A :class:`HolidaySum` object unless the other object cannot be
411
- added, then :class:`self`.
387
+ Returns:
388
+ A `HolidayBase` object unless the other object cannot be added, then `self`.
412
389
  """
413
390
  if isinstance(other, int) and other == 0:
414
391
  # Required to sum() list of holidays
@@ -424,14 +401,21 @@ class HolidayBase(dict[date, str]):
424
401
  return len(self) > 0
425
402
 
426
403
  def __contains__(self, key: object) -> bool:
427
- """Return true if date is in self, false otherwise. Accepts a date in
428
- the following types:
429
-
430
- * :class:`datetime.date`,
431
- * :class:`datetime.datetime`,
432
- * a :class:`str` of any format recognized by
433
- :func:`dateutil.parser.parse`,
434
- * or a :class:`float` or :class:`int` representing a POSIX timestamp.
404
+ """Check if a given date is a holiday.
405
+
406
+ The method supports the following input types:
407
+
408
+ * `datetime.date`,
409
+ * `datetime.datetime`,
410
+ * a `str` of any format recognized by `dateutil.parser.parse()`,
411
+ * or a `float` or `int` representing a POSIX timestamp.
412
+
413
+ Args:
414
+ key:
415
+ The date to check.
416
+
417
+ Returns:
418
+ `True` if the date is a holiday, `False` otherwise.
435
419
  """
436
420
 
437
421
  if not isinstance(key, (date, datetime, float, int, str)):
@@ -592,16 +576,28 @@ class HolidayBase(dict[date, str]):
592
576
 
593
577
  return dict.__getitem__(self, self.__keytransform__(key))
594
578
 
579
+ def __getstate__(self) -> dict[str, Any]:
580
+ """Return the object's state for serialization."""
581
+ state = self.__dict__.copy()
582
+ state.pop("tr", None)
583
+ return state
584
+
595
585
  def __keytransform__(self, key: DateLike) -> date:
596
- """Transforms the date from one of the following types:
586
+ """Convert various date-like formats to `datetime.date`.
597
587
 
598
- * :class:`datetime.date`,
599
- * :class:`datetime.datetime`,
600
- * a :class:`str` of any format recognized by
601
- :func:`dateutil.parser.parse`,
602
- * or a :class:`float` or :class:`int` representing a POSIX timestamp
588
+ The method supports the following input types:
589
+ * `datetime.date`,
590
+ * `datetime.datetime`,
591
+ * a `str` of any format recognized by `dateutil.parser.parse()`,
592
+ * or a `float` or `int` representing a POSIX timestamp
603
593
 
604
- to :class:`datetime.date`, which is how it's stored by the class."""
594
+ Args:
595
+ key:
596
+ The date-like object to convert.
597
+
598
+ Returns:
599
+ The corresponding `datetime.date` representation.
600
+ """
605
601
 
606
602
  dt: Optional[date] = None
607
603
  # Try to catch `date` and `str` type keys first.
@@ -699,6 +695,11 @@ class HolidayBase(dict[date, str]):
699
695
 
700
696
  dict.__setitem__(self, self.__keytransform__(key), value)
701
697
 
698
+ def __setstate__(self, state: dict[str, Any]) -> None:
699
+ """Restore the object's state after deserialization."""
700
+ self.__dict__.update(state)
701
+ self._init_translation()
702
+
702
703
  def __str__(self) -> str:
703
704
  if self:
704
705
  return super().__str__()
@@ -743,13 +744,46 @@ class HolidayBase(dict[date, str]):
743
744
 
744
745
  @classmethod
745
746
  def get_subdivision_aliases(cls) -> dict[str, list]:
746
- """Get subdivision aliases."""
747
+ """Get subdivision aliases.
748
+
749
+ Returns:
750
+ A dictionary mapping subdivision aliases to their official ISO 3166-2 codes.
751
+ """
747
752
  subdivision_aliases: dict[str, list[str]] = {s: [] for s in cls.subdivisions}
748
753
  for alias, subdivision in cls.subdivisions_aliases.items():
749
754
  subdivision_aliases[subdivision].append(alias)
750
755
 
751
756
  return subdivision_aliases
752
757
 
758
+ def _init_translation(self) -> None:
759
+ """Initialize translation function based on language settings."""
760
+ supported_languages = set(self.supported_languages)
761
+ if self._entity_code is not None:
762
+ fallback = self.language not in supported_languages
763
+ languages = [self.language] if self.language in supported_languages else None
764
+ locale_directory = str(Path(__file__).with_name("locale"))
765
+
766
+ # Add entity native content translations.
767
+ entity_translation = translation(
768
+ self._entity_code,
769
+ fallback=fallback,
770
+ languages=languages,
771
+ localedir=locale_directory,
772
+ )
773
+ # Add a fallback if entity has parent translations.
774
+ if parent_entity := self.parent_entity:
775
+ entity_translation.add_fallback(
776
+ translation(
777
+ parent_entity.country or parent_entity.market,
778
+ fallback=fallback,
779
+ languages=languages,
780
+ localedir=locale_directory,
781
+ )
782
+ )
783
+ self.tr = entity_translation.gettext
784
+ else:
785
+ self.tr = gettext
786
+
753
787
  def _is_leap_year(self) -> bool:
754
788
  """
755
789
  Returns True if the year is leap. Returns False otherwise.
@@ -841,13 +875,13 @@ class HolidayBase(dict[date, str]):
841
875
  directly from outside.
842
876
  To add holidays to an object, use the update() method.
843
877
 
844
- :param year:
845
- The year to populate with holidays.
878
+ Args:
879
+ year: The year to populate with holidays.
846
880
 
847
- >>> from holidays import country_holidays
848
- >>> us_holidays = country_holidays('US', years=2020)
849
- # to add new holidays to the object:
850
- >>> us_holidays.update(country_holidays('US', years=2021))
881
+ >>> from holidays import country_holidays
882
+ >>> us_holidays = country_holidays('US', years=2020)
883
+ # to add new holidays to the object:
884
+ >>> us_holidays.update(country_holidays('US', years=2021))
851
885
  """
852
886
 
853
887
  if year < self.start_year or year > self.end_year:
@@ -888,7 +922,16 @@ class HolidayBase(dict[date, str]):
888
922
  )
889
923
 
890
924
  def append(self, *args: Union[dict[DateLike, str], list[DateLike], DateLike]) -> None:
891
- """Alias for :meth:`update` to mimic list type."""
925
+ """Alias for [update()][holidays.holiday_base.HolidayBase.update] to mimic list type.
926
+
927
+ Args:
928
+ args:
929
+ Holiday data to add. Can be:
930
+
931
+ * A dictionary mapping dates to holiday names.
932
+ * A list of dates (without names).
933
+ * A single date.
934
+ """
892
935
  return self.update(*args)
893
936
 
894
937
  def copy(self):
@@ -896,64 +939,73 @@ class HolidayBase(dict[date, str]):
896
939
  return copy.copy(self)
897
940
 
898
941
  def get(self, key: DateLike, default: Union[str, Any] = None) -> Union[str, Any]:
899
- """Return the holiday name for a date if date is a holiday, else
900
- default. If default is not given, it defaults to None, so that this
901
- method never raises a KeyError. If more than one holiday is present,
902
- they are separated by a comma.
903
-
904
- :param key:
905
- The date expressed in one of the following types:
906
-
907
- * :class:`datetime.date`,
908
- * :class:`datetime.datetime`,
909
- * a :class:`str` of any format recognized by
910
- :func:`dateutil.parser.parse`,
911
- * or a :class:`float` or :class:`int` representing a POSIX
912
- timestamp.
913
-
914
- :param default:
915
- The default value to return if no value is found.
942
+ """Retrieve the holiday name(s) for a given date.
943
+
944
+ If the date is a holiday, returns the holiday name as a string.
945
+ If multiple holidays fall on the same date, their names are joined by a semicolon (`;`).
946
+ If the date is not a holiday, returns the provided `default` value (defaults to `None`).
947
+
948
+ Args:
949
+ key:
950
+ The date expressed in one of the following types:
951
+
952
+ * `datetime.date`,
953
+ * `datetime.datetime`,
954
+ * a `str` of any format recognized by `dateutil.parser.parse()`,
955
+ * or a `float` or `int` representing a POSIX timestamp.
956
+
957
+ default:
958
+ The default value to return if no value is found.
959
+
960
+ Returns:
961
+ The holiday name(s) as a string if the date is a holiday,
962
+ or the `default` value otherwise.
916
963
  """
917
964
  return dict.get(self, self.__keytransform__(key), default)
918
965
 
919
966
  def get_list(self, key: DateLike) -> list[str]:
920
- """Return a list of all holiday names for a date if date is a holiday,
921
- else empty string.
922
-
923
- :param key:
924
- The date expressed in one of the following types:
925
-
926
- * :class:`datetime.date`,
927
- * :class:`datetime.datetime`,
928
- * a :class:`str` of any format recognized by
929
- :func:`dateutil.parser.parse`,
930
- * or a :class:`float` or :class:`int` representing a POSIX
931
- timestamp.
967
+ """Retrieve all holiday names for a given date.
968
+
969
+ Args:
970
+ key:
971
+ The date expressed in one of the following types:
972
+
973
+ * `datetime.date`,
974
+ * `datetime.datetime`,
975
+ * a `str` of any format recognized by `dateutil.parser.parse()`,
976
+ * or a `float` or `int` representing a POSIX timestamp.
977
+
978
+ Returns:
979
+ A list of holiday names if the date is a holiday, otherwise an empty list.
932
980
  """
933
981
  return [name for name in self.get(key, "").split(HOLIDAY_NAME_DELIMITER) if name]
934
982
 
935
983
  def get_named(
936
- self, holiday_name: str, lookup="icontains", split_multiple_names=True
984
+ self, holiday_name: str, lookup: str = "icontains", split_multiple_names: bool = True
937
985
  ) -> list[date]:
938
- """Return a list of all holiday dates matching the provided holiday
939
- name. The match will be made case insensitively and partial matches
940
- will be included by default.
941
-
942
- :param holiday_name:
943
- The holiday's name to try to match.
944
- :param lookup:
945
- The holiday name lookup type:
946
- contains - case sensitive contains match;
947
- exact - case sensitive exact match;
948
- startswith - case sensitive starts with match;
949
- icontains - case insensitive contains match;
950
- iexact - case insensitive exact match;
951
- istartswith - case insensitive starts with match;
952
- :param split_multiple_names:
953
- Either use the exact name for each date or split it by holiday
954
- name delimiter.
955
-
956
- :return:
986
+ """Find all holiday dates matching a given name.
987
+
988
+ The search by default is case-insensitive and includes partial matches.
989
+
990
+ Args:
991
+ holiday_name:
992
+ The holiday's name to try to match.
993
+
994
+ lookup:
995
+ The holiday name lookup type:
996
+
997
+ * contains - case sensitive contains match;
998
+ * exact - case sensitive exact match;
999
+ * startswith - case sensitive starts with match;
1000
+ * icontains - case insensitive contains match;
1001
+ * iexact - case insensitive exact match;
1002
+ * istartswith - case insensitive starts with match;
1003
+
1004
+ split_multiple_names:
1005
+ Either use the exact name for each date or split it by holiday
1006
+ name delimiter.
1007
+
1008
+ Returns:
957
1009
  A list of all holiday dates matching the provided holiday name.
958
1010
  """
959
1011
  holiday_name_dates = (
@@ -991,10 +1043,23 @@ class HolidayBase(dict[date, str]):
991
1043
  target_date: DateLike = None,
992
1044
  direction: Literal["forward", "backward"] = "forward",
993
1045
  ) -> Optional[tuple[date, str]]:
994
- """Return the date and name of the next holiday for a target_date
995
- if direction is "forward" or the previous holiday if direction is "backward".
996
- If target_date is not provided the current date will be used by default."""
1046
+ """Find the closest holiday relative to a given date.
997
1047
 
1048
+ If `direction` is "forward", returns the next holiday after `target_date`.
1049
+ If `direction` is "backward", returns the previous holiday before `target_date`.
1050
+ If `target_date` is not provided, the current date is used.
1051
+
1052
+ Args:
1053
+ target_date:
1054
+ The reference date. If None, defaults to today.
1055
+
1056
+ direction:
1057
+ Search direction, either "forward" (next holiday) or
1058
+ "backward" (previous holiday).
1059
+
1060
+ Returns:
1061
+ A tuple containing the holiday date and its name, or None if no holiday is found.
1062
+ """
998
1063
  if direction not in {"backward", "forward"}:
999
1064
  raise AttributeError(f"Unknown direction: {direction}")
1000
1065
 
@@ -1017,8 +1082,20 @@ class HolidayBase(dict[date, str]):
1017
1082
  return None
1018
1083
 
1019
1084
  def get_nth_working_day(self, key: DateLike, n: int) -> date:
1020
- """Return n-th working day from provided date (if n is positive)
1021
- or n-th working day before provided date (if n is negative).
1085
+ """Find the n-th working day from a given date.
1086
+
1087
+ Moves forward if n is positive, or backward if n is negative.
1088
+
1089
+ Args:
1090
+ key:
1091
+ The starting date.
1092
+
1093
+ n:
1094
+ The number of working days to move. Positive values move forward,
1095
+ negative values move backward.
1096
+
1097
+ Returns:
1098
+ The calculated working day after shifting by n working days.
1022
1099
  """
1023
1100
  direction = +1 if n > 0 else -1
1024
1101
  dt = self.__keytransform__(key)
@@ -1029,16 +1106,20 @@ class HolidayBase(dict[date, str]):
1029
1106
  return dt
1030
1107
 
1031
1108
  def get_working_days_count(self, start: DateLike, end: DateLike) -> int:
1032
- """Return the number of working days between two dates.
1109
+ """Calculate the number of working days between two dates.
1033
1110
 
1034
1111
  The date range works in a closed interval fashion [start, end] so both
1035
1112
  endpoints are included.
1036
1113
 
1037
- :param start:
1038
- The range start date.
1114
+ Args:
1115
+ start:
1116
+ The range start date.
1117
+
1118
+ end:
1119
+ The range end date.
1039
1120
 
1040
- :param end:
1041
- The range end date.
1121
+ Returns:
1122
+ The total count of working days between the given dates.
1042
1123
  """
1043
1124
  dt1 = self.__keytransform__(start)
1044
1125
  dt2 = self.__keytransform__(end)
@@ -1048,55 +1129,83 @@ class HolidayBase(dict[date, str]):
1048
1129
  return sum(self.is_working_day(_timedelta(dt1, n)) for n in range(days))
1049
1130
 
1050
1131
  def is_working_day(self, key: DateLike) -> bool:
1051
- """Return True if date is a working day (not a holiday or a weekend)."""
1132
+ """Check if the given date is considered a working day.
1133
+
1134
+ Args:
1135
+ key:
1136
+ The date to check.
1137
+
1138
+ Returns:
1139
+ True if the date is a working day, False if it is a holiday or weekend.
1140
+ """
1052
1141
  dt = self.__keytransform__(key)
1053
1142
  return dt in self.weekend_workdays if self._is_weekend(dt) else dt not in self
1054
1143
 
1055
1144
  def pop(self, key: DateLike, default: Union[str, Any] = None) -> Union[str, Any]:
1056
- """If date is a holiday, remove it and return its date, else return
1057
- default.
1145
+ """Remove a holiday for a given date and return its name.
1146
+
1147
+ If the specified date is a holiday, it will be removed, and its name will
1148
+ be returned. If the date is not a holiday, the provided `default` value
1149
+ will be returned instead.
1058
1150
 
1059
- :param key:
1060
- The date expressed in one of the following types:
1151
+ Args:
1152
+ key:
1153
+ The date expressed in one of the following types:
1061
1154
 
1062
- * :class:`datetime.date`,
1063
- * :class:`datetime.datetime`,
1064
- * a :class:`str` of any format recognized by
1065
- :func:`dateutil.parser.parse`,
1066
- * or a :class:`float` or :class:`int` representing a POSIX
1067
- timestamp.
1155
+ * `datetime.date`,
1156
+ * `datetime.datetime`,
1157
+ * a `str` of any format recognized by `dateutil.parser.parse()`,
1158
+ * or a `float` or `int` representing a POSIX timestamp.
1068
1159
 
1069
- :param default:
1070
- The default value to return if no match is found.
1160
+ default:
1161
+ The default value to return if no match is found.
1071
1162
 
1072
- :return:
1073
- The date removed.
1163
+ Returns:
1164
+ The name of the removed holiday if the date was a holiday, otherwise
1165
+ the provided `default` value.
1074
1166
 
1075
- :raise:
1076
- KeyError if date is not a holiday and default is not given.
1167
+ Raises:
1168
+ KeyError: if date is not a holiday and default is not given.
1077
1169
  """
1078
1170
  if default is None:
1079
1171
  return dict.pop(self, self.__keytransform__(key))
1080
1172
 
1081
1173
  return dict.pop(self, self.__keytransform__(key), default)
1082
1174
 
1083
- def pop_named(self, name: str) -> list[date]:
1084
- """Remove (no longer treat at as holiday) all dates matching the
1085
- provided holiday name. The match will be made case insensitively and
1086
- partial matches will be removed.
1175
+ def pop_named(self, holiday_name: str, lookup: str = "icontains") -> list[date]:
1176
+ """Remove all holidays matching the given name.
1177
+
1178
+ This method removes all dates associated with a holiday name, so they are
1179
+ no longer considered holidays. The search by default is case-insensitive and
1180
+ includes partial matches.
1087
1181
 
1088
- :param name:
1089
- The holiday's name to try to match.
1182
+ Args:
1183
+ holiday_name:
1184
+ The holiday's name to try to match.
1090
1185
 
1091
- :return:
1186
+ lookup:
1187
+ The holiday name lookup type:
1188
+
1189
+ * contains - case sensitive contains match;
1190
+ * exact - case sensitive exact match;
1191
+ * startswith - case sensitive starts with match;
1192
+ * icontains - case insensitive contains match;
1193
+ * iexact - case insensitive exact match;
1194
+ * istartswith - case insensitive starts with match;
1195
+
1196
+ Returns:
1092
1197
  A list of dates removed.
1093
1198
 
1094
- :raise:
1095
- KeyError if date is not a holiday and default is not given.
1199
+ Raises:
1200
+ KeyError: if date is not a holiday.
1096
1201
  """
1097
- use_exact_name = HOLIDAY_NAME_DELIMITER in name
1098
- if not (dts := self.get_named(name, split_multiple_names=not use_exact_name)):
1099
- raise KeyError(name)
1202
+ use_exact_name = HOLIDAY_NAME_DELIMITER in holiday_name
1203
+ if not (
1204
+ dts := self.get_named(
1205
+ holiday_name, lookup=lookup, split_multiple_names=not use_exact_name
1206
+ )
1207
+ ):
1208
+ raise KeyError(holiday_name)
1100
1209
 
1101
1210
  popped = []
1102
1211
  for dt in dts:
@@ -1105,38 +1214,55 @@ class HolidayBase(dict[date, str]):
1105
1214
  popped.append(dt)
1106
1215
 
1107
1216
  # Keep the rest of holidays falling on the same date.
1108
- if not use_exact_name:
1109
- name_lower = name.lower()
1217
+ if use_exact_name:
1218
+ continue
1219
+ if lookup == "icontains":
1220
+ holiday_name_lower = holiday_name.lower()
1110
1221
  holiday_names = [
1111
- holiday_name
1112
- for holiday_name in holiday_names
1113
- if name_lower not in holiday_name.lower()
1222
+ name for name in holiday_names if holiday_name_lower not in name.lower()
1114
1223
  ]
1115
-
1116
- if holiday_names:
1117
- self[dt] = HOLIDAY_NAME_DELIMITER.join(holiday_names)
1224
+ elif lookup == "iexact":
1225
+ holiday_name_lower = holiday_name.lower()
1226
+ holiday_names = [
1227
+ name for name in holiday_names if holiday_name_lower != name.lower()
1228
+ ]
1229
+ elif lookup == "istartswith":
1230
+ holiday_name_lower = holiday_name.lower()
1231
+ holiday_names = [
1232
+ name
1233
+ for name in holiday_names
1234
+ if holiday_name_lower != name[: len(holiday_name)].lower()
1235
+ ]
1236
+ elif lookup == "contains":
1237
+ holiday_names = [name for name in holiday_names if holiday_name not in name]
1238
+ elif lookup == "exact":
1239
+ holiday_names = [name for name in holiday_names if holiday_name != name]
1240
+ else: # startswith
1241
+ holiday_names = [
1242
+ name for name in holiday_names if holiday_name != name[: len(holiday_name)]
1243
+ ]
1244
+ if holiday_names:
1245
+ self[dt] = HOLIDAY_NAME_DELIMITER.join(holiday_names)
1118
1246
 
1119
1247
  return popped
1120
1248
 
1121
1249
  def update( # type: ignore[override]
1122
1250
  self, *args: Union[dict[DateLike, str], list[DateLike], DateLike]
1123
1251
  ) -> None:
1124
- # TODO: fix arguments; should not be *args (cannot properly Type hint)
1125
1252
  """Update the object, overwriting existing dates.
1126
1253
 
1127
- :param:
1128
- Either another dictionary object where keys are dates and values
1129
- are holiday names, or a single date (or a list of dates) for which
1130
- the value will be set to "Holiday".
1254
+ Args:
1255
+ args:
1256
+ Either another dictionary object where keys are dates and values
1257
+ are holiday names, or a single date (or a list of dates) for which
1258
+ the value will be set to "Holiday".
1131
1259
 
1132
- Dates can be expressed in one or more of the following types:
1260
+ Dates can be expressed in one or more of the following types:
1133
1261
 
1134
- * :class:`datetime.date`,
1135
- * :class:`datetime.datetime`,
1136
- * a :class:`str` of any format recognized by
1137
- :func:`dateutil.parser.parse`,
1138
- * or a :class:`float` or :class:`int` representing a POSIX
1139
- timestamp.
1262
+ * `datetime.date`,
1263
+ * `datetime.datetime`,
1264
+ * a `str` of any format recognized by `dateutil.parser.parse()`,
1265
+ * or a `float` or `int` representing a POSIX timestamp.
1140
1266
  """
1141
1267
  for arg in args:
1142
1268
  if isinstance(arg, dict):
@@ -1151,11 +1277,11 @@ class HolidayBase(dict[date, str]):
1151
1277
 
1152
1278
  class HolidaySum(HolidayBase):
1153
1279
  """
1154
- Returns a :class:`dict`-like object resulting from the addition of two or
1280
+ Returns a `dict`-like object resulting from the addition of two or
1155
1281
  more individual dictionaries of public holidays. The original dictionaries
1156
- are available as a :class:`list` in the attribute :attr:`holidays,` and
1157
- :attr:`country` and :attr:`subdiv` attributes are added
1158
- together and could become :class:`list` s. Holiday names, when different,
1282
+ are available as a `list` in the attribute `holidays,` and
1283
+ `country` and `subdiv` attributes are added
1284
+ together and could become `list` s. Holiday names, when different,
1159
1285
  are merged. All years are calculated (expanded) for all operands.
1160
1286
  """
1161
1287
 
@@ -1174,30 +1300,31 @@ class HolidaySum(HolidayBase):
1174
1300
  self, h1: Union[HolidayBase, "HolidaySum"], h2: Union[HolidayBase, "HolidaySum"]
1175
1301
  ) -> None:
1176
1302
  """
1177
- :param h1:
1178
- The first HolidayBase object to add.
1303
+ Args:
1304
+ h1:
1305
+ The first HolidayBase object to add.
1179
1306
 
1180
- :param h2:
1181
- The other HolidayBase object to add.
1307
+ h2:
1308
+ The other HolidayBase object to add.
1182
1309
 
1183
1310
  Example:
1184
1311
 
1185
- >>> from holidays import country_holidays
1186
- >>> nafta_holidays = country_holidays('US', years=2020) + \
1187
- country_holidays('CA') + country_holidays('MX')
1188
- >>> dates = sorted(nafta_holidays.items(), key=lambda x: x[0])
1189
- >>> from pprint import pprint
1190
- >>> pprint(dates[:10], width=72)
1191
- [(datetime.date(2020, 1, 1), "Año Nuevo"),
1192
- (datetime.date(2020, 1, 20), 'Martin Luther King Jr. Day'),
1193
- (datetime.date(2020, 2, 3),
1194
- 'Día de la Constitución'),
1195
- (datetime.date(2020, 2, 17), "Washington's Birthday, Family Day"),
1196
- (datetime.date(2020, 3, 16),
1197
- "Natalicio de Benito Juárez"),
1198
- (datetime.date(2020, 4, 10), 'Good Friday'),
1199
- (datetime.date(2020, 5, 1), 'Día del Trabajo'),
1200
- (datetime.date(2020, 5, 18), 'Victoria Day')]
1312
+ >>> from holidays import country_holidays
1313
+ >>> nafta_holidays = country_holidays('US', years=2020) + \
1314
+ country_holidays('CA') + country_holidays('MX')
1315
+ >>> dates = sorted(nafta_holidays.items(), key=lambda x: x[0])
1316
+ >>> from pprint import pprint
1317
+ >>> pprint(dates[:10], width=72)
1318
+ [(datetime.date(2020, 1, 1), "Año Nuevo; New Year's Day"),
1319
+ (datetime.date(2020, 1, 20), 'Martin Luther King Jr. Day'),
1320
+ (datetime.date(2020, 2, 3), 'Día de la Constitución'),
1321
+ (datetime.date(2020, 2, 17), "Washington's Birthday"),
1322
+ (datetime.date(2020, 3, 16), 'Natalicio de Benito Juárez'),
1323
+ (datetime.date(2020, 4, 10), 'Good Friday'),
1324
+ (datetime.date(2020, 5, 1), 'Día del Trabajo'),
1325
+ (datetime.date(2020, 5, 25), 'Memorial Day'),
1326
+ (datetime.date(2020, 7, 1), 'Canada Day'),
1327
+ (datetime.date(2020, 7, 3), 'Independence Day (observed)')]
1201
1328
  """
1202
1329
  # Store originals in the holidays attribute.
1203
1330
  self.holidays = []
@@ -1245,8 +1372,18 @@ country_holidays('CA') + country_holidays('MX')
1245
1372
  else:
1246
1373
  setattr(self, attr, value)
1247
1374
 
1375
+ # Retain language if they match and are strings.
1376
+ # If language wasn't assigned, default_language acts as fallback.
1377
+ h1_language = h1.language or h1.default_language
1378
+ h2_language = h2.language or h2.default_language
1379
+ if isinstance(h1_language, str) and h1_language == h2_language:
1380
+ kwargs["language"] = h1_language
1381
+
1248
1382
  HolidayBase.__init__(self, **kwargs)
1249
1383
 
1384
+ # supported_languages is used for iCalExporter language check as well.
1385
+ self.supported_languages = (h1_language,) if h1_language else ()
1386
+
1250
1387
  def _populate(self, year):
1251
1388
  for operand in self.holidays:
1252
1389
  operand._populate(year)