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.
- holidays/__init__.py +1 -1
- holidays/calendars/__init__.py +2 -1
- holidays/calendars/balinese_saka.py +112 -0
- holidays/calendars/buddhist.py +1 -1
- holidays/calendars/chinese.py +1 -1
- holidays/calendars/custom.py +1 -1
- holidays/calendars/gregorian.py +1 -1
- holidays/calendars/hebrew.py +1 -1
- holidays/calendars/hindu.py +866 -2
- holidays/calendars/islamic.py +161 -1
- holidays/calendars/julian.py +1 -1
- holidays/calendars/julian_revised.py +1 -1
- holidays/calendars/persian.py +1 -1
- holidays/calendars/sinhala.py +1 -1
- holidays/calendars/thai.py +309 -257
- holidays/constants.py +3 -1
- holidays/countries/__init__.py +7 -1
- holidays/countries/afghanistan.py +17 -7
- holidays/countries/albania.py +16 -7
- holidays/countries/algeria.py +14 -10
- holidays/countries/american_samoa.py +12 -6
- holidays/countries/andorra.py +5 -4
- holidays/countries/angola.py +15 -14
- holidays/countries/antigua_and_barbuda.py +145 -0
- holidays/countries/argentina.py +787 -169
- holidays/countries/armenia.py +5 -6
- holidays/countries/aruba.py +11 -9
- holidays/countries/australia.py +21 -20
- holidays/countries/austria.py +3 -1
- holidays/countries/azerbaijan.py +30 -19
- holidays/countries/bahamas.py +13 -11
- holidays/countries/bahrain.py +14 -7
- holidays/countries/bangladesh.py +5 -4
- holidays/countries/barbados.py +11 -9
- holidays/countries/belarus.py +15 -15
- holidays/countries/belgium.py +8 -6
- holidays/countries/belize.py +7 -6
- holidays/countries/bolivia.py +12 -11
- holidays/countries/bosnia_and_herzegovina.py +21 -11
- holidays/countries/botswana.py +8 -6
- holidays/countries/brazil.py +8 -7
- holidays/countries/brunei.py +56 -62
- holidays/countries/bulgaria.py +9 -10
- holidays/countries/burkina_faso.py +14 -5
- holidays/countries/burundi.py +17 -12
- holidays/countries/cambodia.py +15 -24
- holidays/countries/cameroon.py +16 -7
- holidays/countries/canada.py +13 -12
- holidays/countries/chad.py +15 -6
- holidays/countries/chile.py +29 -28
- holidays/countries/china.py +39 -38
- holidays/countries/colombia.py +15 -20
- holidays/countries/congo.py +6 -7
- holidays/countries/costa_rica.py +11 -10
- holidays/countries/croatia.py +8 -5
- holidays/countries/cuba.py +30 -27
- holidays/countries/curacao.py +6 -4
- holidays/countries/cyprus.py +4 -5
- holidays/countries/czechia.py +7 -6
- holidays/countries/denmark.py +5 -6
- holidays/countries/djibouti.py +11 -3
- holidays/countries/dominica.py +18 -16
- holidays/countries/dominican_republic.py +6 -4
- holidays/countries/ecuador.py +5 -4
- holidays/countries/egypt.py +10 -11
- holidays/countries/el_salvador.py +6 -5
- holidays/countries/estonia.py +3 -1
- holidays/countries/eswatini.py +6 -4
- holidays/countries/ethiopia.py +26 -11
- holidays/countries/fiji.py +183 -0
- holidays/countries/finland.py +11 -10
- holidays/countries/france.py +6 -9
- holidays/countries/gabon.py +17 -8
- holidays/countries/georgia.py +7 -7
- holidays/countries/germany.py +11 -11
- holidays/countries/ghana.py +14 -6
- holidays/countries/greece.py +4 -5
- holidays/countries/greenland.py +5 -6
- holidays/countries/guam.py +12 -6
- holidays/countries/guatemala.py +7 -9
- holidays/countries/guernsey.py +37 -34
- holidays/countries/guinea.py +182 -0
- holidays/countries/haiti.py +6 -5
- holidays/countries/honduras.py +8 -4
- holidays/countries/hongkong.py +49 -60
- holidays/countries/hungary.py +26 -23
- holidays/countries/iceland.py +5 -4
- holidays/countries/india.py +494 -174
- holidays/countries/indonesia.py +68 -108
- holidays/countries/iran.py +18 -9
- holidays/countries/ireland.py +5 -4
- holidays/countries/isle_of_man.py +2 -2
- holidays/countries/israel.py +4 -5
- holidays/countries/italy.py +5 -4
- holidays/countries/ivory_coast.py +156 -0
- holidays/countries/jamaica.py +6 -4
- holidays/countries/japan.py +5 -5
- holidays/countries/jersey.py +29 -26
- holidays/countries/jordan.py +13 -6
- holidays/countries/kazakhstan.py +72 -51
- holidays/countries/kenya.py +28 -18
- holidays/countries/kuwait.py +14 -7
- holidays/countries/kyrgyzstan.py +11 -6
- holidays/countries/laos.py +21 -29
- holidays/countries/latvia.py +7 -5
- holidays/countries/lesotho.py +6 -5
- holidays/countries/liechtenstein.py +5 -5
- holidays/countries/lithuania.py +4 -5
- holidays/countries/luxembourg.py +5 -3
- holidays/countries/macau.py +480 -0
- holidays/countries/madagascar.py +5 -4
- holidays/countries/malawi.py +6 -4
- holidays/countries/malaysia.py +30 -18
- holidays/countries/maldives.py +14 -7
- holidays/countries/malta.py +10 -19
- holidays/countries/marshall_islands.py +6 -4
- holidays/countries/mauritania.py +13 -6
- holidays/countries/mexico.py +8 -7
- holidays/countries/moldova.py +6 -4
- holidays/countries/monaco.py +6 -4
- holidays/countries/montenegro.py +16 -7
- holidays/countries/morocco.py +13 -8
- holidays/countries/mozambique.py +3 -1
- holidays/countries/namibia.py +7 -5
- holidays/countries/netherlands.py +6 -6
- holidays/countries/new_zealand.py +3 -1
- holidays/countries/nicaragua.py +6 -5
- holidays/countries/nigeria.py +13 -5
- holidays/countries/north_macedonia.py +13 -5
- holidays/countries/northern_mariana_islands.py +12 -6
- holidays/countries/norway.py +15 -15
- holidays/countries/pakistan.py +48 -18
- holidays/countries/palau.py +13 -11
- holidays/countries/panama.py +9 -8
- holidays/countries/papua_new_guinea.py +25 -21
- holidays/countries/paraguay.py +10 -9
- holidays/countries/peru.py +4 -5
- holidays/countries/philippines.py +32 -18
- holidays/countries/poland.py +7 -6
- holidays/countries/portugal.py +13 -15
- holidays/countries/puerto_rico.py +12 -6
- holidays/countries/qatar.py +172 -0
- holidays/countries/romania.py +6 -4
- holidays/countries/russia.py +6 -4
- holidays/countries/saint_kitts_and_nevis.py +24 -22
- holidays/countries/saint_lucia.py +8 -7
- holidays/countries/samoa.py +7 -6
- holidays/countries/san_marino.py +4 -3
- holidays/countries/saudi_arabia.py +15 -15
- holidays/countries/serbia.py +3 -4
- holidays/countries/seychelles.py +22 -26
- holidays/countries/singapore.py +33 -38
- holidays/countries/slovakia.py +6 -5
- holidays/countries/slovenia.py +7 -6
- holidays/countries/south_africa.py +8 -6
- holidays/countries/south_korea.py +25 -32
- holidays/countries/spain.py +31 -24
- holidays/countries/sri_lanka.py +52 -42
- holidays/countries/sweden.py +20 -19
- holidays/countries/switzerland.py +6 -5
- holidays/countries/taiwan.py +230 -31
- holidays/countries/tanzania.py +34 -27
- holidays/countries/thailand.py +134 -142
- holidays/countries/timor_leste.py +38 -17
- holidays/countries/tonga.py +46 -42
- holidays/countries/tunisia.py +9 -3
- holidays/countries/turkey.py +17 -9
- holidays/countries/tuvalu.py +12 -11
- holidays/countries/ukraine.py +54 -54
- holidays/countries/united_arab_emirates.py +43 -30
- holidays/countries/united_kingdom.py +7 -6
- holidays/countries/united_states.py +130 -86
- holidays/countries/united_states_minor_outlying_islands.py +12 -6
- holidays/countries/united_states_virgin_islands.py +12 -6
- holidays/countries/uruguay.py +10 -9
- holidays/countries/uzbekistan.py +16 -7
- holidays/countries/vanuatu.py +7 -5
- holidays/countries/vatican_city.py +16 -15
- holidays/countries/venezuela.py +11 -14
- holidays/countries/vietnam.py +15 -11
- holidays/countries/zambia.py +8 -6
- holidays/countries/zimbabwe.py +6 -4
- holidays/deprecations/v1_incompatibility.py +1 -1
- holidays/financial/__init__.py +1 -1
- holidays/financial/brasil_bolsa_balcao.py +14 -13
- holidays/financial/european_central_bank.py +7 -6
- holidays/financial/ice_futures_europe.py +7 -6
- holidays/financial/ny_stock_exchange.py +13 -10
- holidays/groups/__init__.py +2 -1
- holidays/groups/balinese_saka.py +45 -0
- holidays/groups/buddhist.py +1 -1
- holidays/groups/chinese.py +69 -4
- holidays/groups/christian.py +1 -1
- holidays/groups/custom.py +1 -1
- holidays/groups/eastern.py +1 -1
- holidays/groups/hebrew.py +1 -1
- holidays/groups/hindu.py +256 -1
- holidays/groups/international.py +1 -1
- holidays/groups/islamic.py +17 -3
- holidays/groups/persian.py +1 -1
- holidays/groups/sinhala.py +1 -1
- holidays/groups/thai.py +1 -7
- holidays/helpers.py +1 -1
- holidays/holiday_base.py +410 -273
- holidays/ical.py +228 -0
- holidays/locale/ar_QA/LC_MESSAGES/QA.mo +0 -0
- holidays/locale/de/LC_MESSAGES/PL.mo +0 -0
- holidays/locale/en_CI/LC_MESSAGES/CI.mo +0 -0
- holidays/locale/en_HK/LC_MESSAGES/HK.mo +0 -0
- holidays/locale/en_IN/LC_MESSAGES/IN.mo +0 -0
- holidays/locale/en_MO/LC_MESSAGES/MO.mo +0 -0
- holidays/locale/en_PK/LC_MESSAGES/PK.mo +0 -0
- holidays/locale/en_TL/LC_MESSAGES/TL.mo +0 -0
- holidays/locale/en_US/LC_MESSAGES/AR.mo +0 -0
- holidays/locale/en_US/LC_MESSAGES/CI.mo +0 -0
- holidays/locale/en_US/LC_MESSAGES/GN.mo +0 -0
- holidays/locale/en_US/LC_MESSAGES/HK.mo +0 -0
- holidays/locale/en_US/LC_MESSAGES/IN.mo +0 -0
- holidays/locale/en_US/LC_MESSAGES/MO.mo +0 -0
- holidays/locale/en_US/LC_MESSAGES/PK.mo +0 -0
- holidays/locale/en_US/LC_MESSAGES/QA.mo +0 -0
- holidays/locale/en_US/LC_MESSAGES/TL.mo +0 -0
- holidays/locale/en_US/LC_MESSAGES/TW.mo +0 -0
- holidays/locale/es/LC_MESSAGES/AR.mo +0 -0
- holidays/locale/fr/LC_MESSAGES/CI.mo +0 -0
- holidays/locale/fr/LC_MESSAGES/GN.mo +0 -0
- holidays/locale/hi/LC_MESSAGES/IN.mo +0 -0
- holidays/locale/pt_MO/LC_MESSAGES/MO.mo +0 -0
- holidays/locale/pt_TL/LC_MESSAGES/TL.mo +0 -0
- holidays/locale/tet/LC_MESSAGES/TL.mo +0 -0
- holidays/locale/th/LC_MESSAGES/HK.mo +0 -0
- holidays/locale/th/LC_MESSAGES/MO.mo +0 -0
- holidays/locale/th/LC_MESSAGES/TL.mo +0 -0
- holidays/locale/th/LC_MESSAGES/TW.mo +0 -0
- holidays/locale/uk/LC_MESSAGES/AR.mo +0 -0
- holidays/locale/ur_PK/LC_MESSAGES/PK.mo +0 -0
- holidays/locale/zh_CN/LC_MESSAGES/HK.mo +0 -0
- holidays/locale/zh_CN/LC_MESSAGES/MO.mo +0 -0
- holidays/locale/zh_CN/LC_MESSAGES/TW.mo +0 -0
- holidays/locale/zh_HK/LC_MESSAGES/HK.mo +0 -0
- holidays/locale/zh_MO/LC_MESSAGES/MO.mo +0 -0
- holidays/locale/zh_TW/LC_MESSAGES/TW.mo +0 -0
- holidays/mixins.py +2 -2
- holidays/observed_holiday_base.py +5 -2
- holidays/registry.py +7 -1
- holidays/utils.py +151 -151
- holidays/version.py +2 -2
- holidays-0.70.dist-info/METADATA +1404 -0
- {holidays-0.68.dist-info → holidays-0.70.dist-info}/RECORD +253 -222
- {holidays-0.68.dist-info → holidays-0.70.dist-info}/WHEEL +1 -1
- holidays-0.68.dist-info/AUTHORS → holidays-0.70.dist-info/licenses/AUTHORS.md +7 -2
- {holidays-0.68.dist-info → holidays-0.70.dist-info/licenses}/LICENSE +1 -1
- holidays-0.68.dist-info/METADATA +0 -1080
- {holidays-0.68.dist-info → holidays-0.70.dist-info}/top_level.txt +0 -0
holidays/ical.py
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
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 AUTHORS.md 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
|
+
import re
|
|
14
|
+
import uuid
|
|
15
|
+
from datetime import date, datetime, timedelta, timezone
|
|
16
|
+
from typing import Union
|
|
17
|
+
|
|
18
|
+
from holidays.holiday_base import HolidayBase
|
|
19
|
+
from holidays.version import __version__
|
|
20
|
+
|
|
21
|
+
# iCal-specific constants
|
|
22
|
+
CONTENT_LINE_MAX_LENGTH = 75
|
|
23
|
+
CONTENT_LINE_DELIMITER = "\r\n"
|
|
24
|
+
CONTENT_LINE_DELIMITER_WRAP = f"{CONTENT_LINE_DELIMITER} "
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class ICalExporter:
|
|
28
|
+
def __init__(self, instance: HolidayBase, show_language: bool = False) -> None:
|
|
29
|
+
"""Initialize iCalendar exporter.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
show_language:
|
|
33
|
+
Determines whether to include the `;LANGUAGE=` attribute in the
|
|
34
|
+
`SUMMARY` field. Defaults to `False`.
|
|
35
|
+
|
|
36
|
+
If the `HolidaysBase` object has a `language` attribute, it will
|
|
37
|
+
be used. Otherwise, `default_language` will be used if available.
|
|
38
|
+
|
|
39
|
+
If neither attribute exists and `show_language=True`, an
|
|
40
|
+
exception will be raised.
|
|
41
|
+
|
|
42
|
+
instance:
|
|
43
|
+
`HolidaysBase` object containing holiday data.
|
|
44
|
+
"""
|
|
45
|
+
self.holidays = instance
|
|
46
|
+
self.show_language = show_language
|
|
47
|
+
self.ical_timestamp = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ")
|
|
48
|
+
self.holidays_version = __version__
|
|
49
|
+
language = getattr(self.holidays, "language", None) or getattr(
|
|
50
|
+
self.holidays, "default_language", None
|
|
51
|
+
)
|
|
52
|
+
self.language = (
|
|
53
|
+
self._validate_language(language)
|
|
54
|
+
if isinstance(language, str)
|
|
55
|
+
and language in getattr(self.holidays, "supported_languages", [])
|
|
56
|
+
else None
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
if self.show_language and self.language is None:
|
|
60
|
+
raise ValueError("LANGUAGE cannot be included because the language code is missing.")
|
|
61
|
+
|
|
62
|
+
def _validate_language(self, language: str) -> str:
|
|
63
|
+
"""Validate the language code to ensure it complies with RFC 5646.
|
|
64
|
+
|
|
65
|
+
In the current implementation, all languages must comply with
|
|
66
|
+
either ISO 639-1 or ISO 639-2 if specified (part of RFC 5646).
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
language:
|
|
70
|
+
The language code to validate.
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
Validated language code.
|
|
74
|
+
"""
|
|
75
|
+
# Remove whitespace (if any), transforms HolidaysBase default to RFC 5646 compliant
|
|
76
|
+
# i.e. `en_US` to `en-US`.
|
|
77
|
+
language = language.strip().replace("_", "-")
|
|
78
|
+
|
|
79
|
+
# ISO 639-1 and ISO 639-2 patterns, in compliance with RFC 5646.
|
|
80
|
+
iso639_pattern = re.compile(r"^[a-z]{2,3}(?:-[A-Z]{2})?$")
|
|
81
|
+
|
|
82
|
+
if not iso639_pattern.fullmatch(language):
|
|
83
|
+
raise ValueError(
|
|
84
|
+
f"Invalid language tag: '{language}'. Expected format follows "
|
|
85
|
+
"ISO 639-1 or ISO 639-2, e.g., 'en', 'en-US'. For more details, "
|
|
86
|
+
"refer to: https://www.loc.gov/standards/iso639-2/php/code_list.php."
|
|
87
|
+
)
|
|
88
|
+
return language
|
|
89
|
+
|
|
90
|
+
def _fold_line(self, line: str) -> str:
|
|
91
|
+
"""Fold long lines according to RFC 5545.
|
|
92
|
+
|
|
93
|
+
Content lines SHOULD NOT exceed 75 octets. If a line is too long,
|
|
94
|
+
it must be split into multiple lines, with each continuation line
|
|
95
|
+
starting with a space.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
line:
|
|
99
|
+
The content line to be folded.
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
The folded content line.
|
|
103
|
+
"""
|
|
104
|
+
if line.isascii():
|
|
105
|
+
# Simple split for ASCII: every (CONTENT_LINE_MAX_LENGTH - 1) chars,
|
|
106
|
+
# as first char of the next line is space
|
|
107
|
+
if len(line) > CONTENT_LINE_MAX_LENGTH:
|
|
108
|
+
return CONTENT_LINE_DELIMITER_WRAP.join(
|
|
109
|
+
line[i : i + CONTENT_LINE_MAX_LENGTH - 1]
|
|
110
|
+
for i in range(0, len(line), CONTENT_LINE_MAX_LENGTH - 1)
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
elif len(line.encode()) > CONTENT_LINE_MAX_LENGTH:
|
|
114
|
+
# Handle non-ASCII text while respecting byte length
|
|
115
|
+
parts = []
|
|
116
|
+
part_start = 0
|
|
117
|
+
part_len = 0
|
|
118
|
+
for i, char in enumerate(line):
|
|
119
|
+
char_byte_len = len(char.encode())
|
|
120
|
+
part_len += char_byte_len
|
|
121
|
+
|
|
122
|
+
if part_len > CONTENT_LINE_MAX_LENGTH:
|
|
123
|
+
parts.append(line[part_start:i])
|
|
124
|
+
part_start = i
|
|
125
|
+
part_len = char_byte_len + 1 # line start with space
|
|
126
|
+
|
|
127
|
+
parts.append(line[part_start:])
|
|
128
|
+
return CONTENT_LINE_DELIMITER_WRAP.join(parts)
|
|
129
|
+
|
|
130
|
+
# Return as-is if it doesn't exceed the limit
|
|
131
|
+
return line
|
|
132
|
+
|
|
133
|
+
def _generate_event(self, dt: date, holiday_name: str, holiday_length: int = 1) -> list[str]:
|
|
134
|
+
"""Generate a single holiday event.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
dt:
|
|
138
|
+
Holiday date.
|
|
139
|
+
|
|
140
|
+
holiday_name:
|
|
141
|
+
Holiday name.
|
|
142
|
+
|
|
143
|
+
holiday_length:
|
|
144
|
+
Holiday length in days, default to 1.
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
List of iCalendar format event lines.
|
|
148
|
+
"""
|
|
149
|
+
# Escape special characters per RFC 5545.
|
|
150
|
+
# SEMICOLON is used as a delimiter in HolidayBase (HOLIDAY_NAME_DELIMITER = "; "),
|
|
151
|
+
# so a name with a semicolon gets split into two separate `VEVENT`s.
|
|
152
|
+
sanitized_holiday_name = (
|
|
153
|
+
holiday_name.replace("\\", "\\\\").replace(",", "\\,").replace(":", "\\:")
|
|
154
|
+
)
|
|
155
|
+
event_uid = f"{uuid.uuid4()}@{self.holidays_version}.holidays.local"
|
|
156
|
+
language_tag = f";LANGUAGE={self.language}" if self.show_language else ""
|
|
157
|
+
|
|
158
|
+
return [
|
|
159
|
+
"BEGIN:VEVENT",
|
|
160
|
+
f"DTSTAMP:{self.ical_timestamp}",
|
|
161
|
+
f"UID:{event_uid}",
|
|
162
|
+
self._fold_line(f"SUMMARY{language_tag}:{sanitized_holiday_name}"),
|
|
163
|
+
f"DTSTART;VALUE=DATE:{dt:%Y%m%d}",
|
|
164
|
+
f"DURATION:P{holiday_length}D",
|
|
165
|
+
"END:VEVENT",
|
|
166
|
+
]
|
|
167
|
+
|
|
168
|
+
def generate(self, return_bytes: bool = False) -> Union[str, bytes]:
|
|
169
|
+
"""Generate iCalendar data.
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
return_bytes:
|
|
173
|
+
If True, return bytes instead of string.
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
The complete iCalendar data
|
|
177
|
+
(string or UTF-8 bytes depending on return_bytes).
|
|
178
|
+
"""
|
|
179
|
+
lines = [
|
|
180
|
+
"BEGIN:VCALENDAR",
|
|
181
|
+
f"PRODID:-//Vacanza//Open World Holidays Framework v{self.holidays_version}//EN",
|
|
182
|
+
"VERSION:2.0",
|
|
183
|
+
"CALSCALE:GREGORIAN",
|
|
184
|
+
]
|
|
185
|
+
|
|
186
|
+
sorted_dates = sorted(self.holidays.keys())
|
|
187
|
+
# Merged continuous holiday with the same name and use `DURATION` instead.
|
|
188
|
+
i = 0
|
|
189
|
+
while i < len(sorted_dates):
|
|
190
|
+
dt = sorted_dates[i]
|
|
191
|
+
names = self.holidays.get_list(dt)
|
|
192
|
+
|
|
193
|
+
for name in names:
|
|
194
|
+
days = 1
|
|
195
|
+
while (
|
|
196
|
+
i + days < len(sorted_dates)
|
|
197
|
+
and sorted_dates[i + days] == sorted_dates[i] + timedelta(days=days)
|
|
198
|
+
and name in self.holidays.get_list(sorted_dates[i + days])
|
|
199
|
+
):
|
|
200
|
+
days += 1
|
|
201
|
+
|
|
202
|
+
lines.extend(self._generate_event(dt, name, days))
|
|
203
|
+
|
|
204
|
+
i += days
|
|
205
|
+
|
|
206
|
+
lines.append("END:VCALENDAR")
|
|
207
|
+
lines.append("")
|
|
208
|
+
|
|
209
|
+
output = CONTENT_LINE_DELIMITER.join(lines)
|
|
210
|
+
return output.encode() if return_bytes else output
|
|
211
|
+
|
|
212
|
+
def save_ics(self, file_path: str) -> None:
|
|
213
|
+
"""Export the calendar data to a .ics file.
|
|
214
|
+
|
|
215
|
+
While RFC 5545 does not specifically forbid filenames for .ics files, but it’s advisable
|
|
216
|
+
to follow general filesystem conventions and avoid using problematic characters.
|
|
217
|
+
|
|
218
|
+
Args:
|
|
219
|
+
file_path:
|
|
220
|
+
Path to save the .ics file, including the filename (with extension).
|
|
221
|
+
"""
|
|
222
|
+
# Generate and write out content (always in bytes for .ics)
|
|
223
|
+
content = self.generate(return_bytes=True)
|
|
224
|
+
if not content:
|
|
225
|
+
raise ValueError("Generated content is empty or invalid.")
|
|
226
|
+
|
|
227
|
+
with open(file_path, "wb") as file:
|
|
228
|
+
file.write(content) # type: ignore # this is always bytes, ignoring mypy error.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
holidays/mixins.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
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
class PreferredDiscretionaryHolidays:
|
|
15
15
|
"""A mixin for setting preferred discretionary holidays.
|
|
16
16
|
|
|
17
|
-
See
|
|
17
|
+
See [HongKong][holidays.countries.hongkong.HongKong] for an example.
|
|
18
18
|
"""
|
|
19
19
|
|
|
20
20
|
default_preferred_discretionary_holidays: tuple[str, ...] = ()
|
|
@@ -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
|
|
@@ -138,11 +138,14 @@ class ObservedHolidayBase(HolidayBase):
|
|
|
138
138
|
|
|
139
139
|
def _add_observed(
|
|
140
140
|
self,
|
|
141
|
-
dt: DateArg,
|
|
141
|
+
dt: Optional[DateArg] = None,
|
|
142
142
|
name: Optional[str] = None,
|
|
143
143
|
rule: Optional[ObservedRule] = None,
|
|
144
144
|
show_observed_label: bool = True,
|
|
145
145
|
) -> tuple[bool, Optional[date]]:
|
|
146
|
+
if dt is None:
|
|
147
|
+
return False, None
|
|
148
|
+
|
|
146
149
|
dt = dt if isinstance(dt, date) else date(self._year, *dt)
|
|
147
150
|
|
|
148
151
|
if not self.observed or not self._is_observed(dt):
|
holidays/registry.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
|
|
@@ -26,6 +26,7 @@ COUNTRIES: RegistryDict = {
|
|
|
26
26
|
"american_samoa": ("AmericanSamoa", "AS", "ASM", "HolidaysAS"),
|
|
27
27
|
"andorra": ("Andorra", "AD", "AND"),
|
|
28
28
|
"angola": ("Angola", "AO", "AGO"),
|
|
29
|
+
"antigua_and_barbuda": ("AntiguaAndBarbuda", "AG", "ATG"),
|
|
29
30
|
"argentina": ("Argentina", "AR", "ARG"),
|
|
30
31
|
"armenia": ("Armenia", "AM", "ARM"),
|
|
31
32
|
"aruba": ("Aruba", "AW", "ABW"),
|
|
@@ -72,6 +73,7 @@ COUNTRIES: RegistryDict = {
|
|
|
72
73
|
"estonia": ("Estonia", "EE", "EST"),
|
|
73
74
|
"eswatini": ("Eswatini", "SZ", "SZW", "Swaziland"),
|
|
74
75
|
"ethiopia": ("Ethiopia", "ET", "ETH"),
|
|
76
|
+
"fiji": ("Fiji", "FJ", "FJI"),
|
|
75
77
|
"finland": ("Finland", "FI", "FIN"),
|
|
76
78
|
"france": ("France", "FR", "FRA"),
|
|
77
79
|
"gabon": ("Gabon", "GA", "GAB"),
|
|
@@ -83,6 +85,7 @@ COUNTRIES: RegistryDict = {
|
|
|
83
85
|
"guam": ("Guam", "GU", "GUM", "HolidaysGU"),
|
|
84
86
|
"guatemala": ("Guatemala", "GT", "GUA"),
|
|
85
87
|
"guernsey": ("Guernsey", "GG", "GGY"),
|
|
88
|
+
"guinea": ("Guinea", "GN", "GIN"),
|
|
86
89
|
"haiti": ("Haiti", "HT", "HTI"),
|
|
87
90
|
"honduras": ("Honduras", "HN", "HND"),
|
|
88
91
|
"hongkong": ("HongKong", "HK", "HKG"),
|
|
@@ -95,6 +98,7 @@ COUNTRIES: RegistryDict = {
|
|
|
95
98
|
"isle_of_man": ("IsleOfMan", "IM", "IMN"),
|
|
96
99
|
"israel": ("Israel", "IL", "ISR"),
|
|
97
100
|
"italy": ("Italy", "IT", "ITA"),
|
|
101
|
+
"ivory_coast": ("IvoryCoast", "CI", "CIV"),
|
|
98
102
|
"jamaica": ("Jamaica", "JM", "JAM"),
|
|
99
103
|
"japan": ("Japan", "JP", "JPN"),
|
|
100
104
|
"jersey": ("Jersey", "JE", "JEY"),
|
|
@@ -108,6 +112,7 @@ COUNTRIES: RegistryDict = {
|
|
|
108
112
|
"liechtenstein": ("Liechtenstein", "LI", "LIE"),
|
|
109
113
|
"lithuania": ("Lithuania", "LT", "LTU"),
|
|
110
114
|
"luxembourg": ("Luxembourg", "LU", "LUX"),
|
|
115
|
+
"macau": ("Macau", "MO", "MAC"),
|
|
111
116
|
"madagascar": ("Madagascar", "MG", "MDG"),
|
|
112
117
|
"malawi": ("Malawi", "MW", "MWI"),
|
|
113
118
|
"malaysia": ("Malaysia", "MY", "MYS"),
|
|
@@ -139,6 +144,7 @@ COUNTRIES: RegistryDict = {
|
|
|
139
144
|
"poland": ("Poland", "PL", "POL"),
|
|
140
145
|
"portugal": ("Portugal", "PT", "PRT"),
|
|
141
146
|
"puerto_rico": ("PuertoRico", "PR", "PRI", "HolidaysPR"),
|
|
147
|
+
"qatar": ("Qatar", "QA", "QAT"),
|
|
142
148
|
"romania": ("Romania", "RO", "ROU"),
|
|
143
149
|
"russia": ("Russia", "RU", "RUS"),
|
|
144
150
|
"saint_kitts_and_nevis": ("SaintKittsAndNevis", "KN", "KNA"),
|