-
-
Notifications
You must be signed in to change notification settings - Fork 704
Add SCHOOL category and subdivision hooks for Belgium #3195
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from 7 commits
45a0668
14295e6
fa0bb2a
7aef11b
52e466d
057cc71
09a7fd2
ca3232a
fb49901
209d55e
a72e716
8196320
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,28 +10,65 @@ | |
| # Website: https://github.com/vacanza/holidays | ||
| # License: MIT (see LICENSE file) | ||
|
|
||
| from datetime import date | ||
| from gettext import gettext as tr | ||
|
|
||
| from holidays.constants import BANK, PUBLIC | ||
| from holidays.calendars.gregorian import ( | ||
| APR, | ||
| AUG, | ||
| JUL, | ||
| JUN, | ||
| SEP, | ||
| OCT, | ||
| NOV, | ||
| DEC, | ||
| MON, | ||
| SAT, | ||
| _timedelta, | ||
| _get_nth_weekday_from, | ||
| _get_nth_weekday_of_month, | ||
| ) | ||
| from holidays.constants import BANK, PUBLIC, SCHOOL | ||
| from holidays.groups import ChristianHolidays, InternationalHolidays | ||
| from holidays.holiday_base import HolidayBase | ||
|
|
||
|
|
||
| class Belgium(HolidayBase, ChristianHolidays, InternationalHolidays): | ||
| """Belgium holidays. | ||
| """ | ||
| Belgium holidays. | ||
|
|
||
| References: | ||
| * <https://en.wikipedia.org/wiki/Public_holidays_in_Belgium> | ||
| * <https://web.archive.org/web/20250331001402/https://www.belgium.be/nl/over_belgie/land/belgie_in_een_notendop/feestdagen> | ||
| * <https://nl.wikipedia.org/wiki/Feestdagen_in_België> | ||
| * <https://web.archive.org/web/20240816004739/https://www.nbb.be/en/about-national-bank/national-bank-belgium/public-holidays> | ||
| * Official education calendars: | ||
| - Flemish Community (Vlaanderen) | ||
| <https://www.vlaanderen.be/onderwijs-en-vorming/wat-mag-en-moet-op-school/schoolvakanties-vrije-dagen-en-afwezigheden/schoolvakanties> | ||
| - French Community (Fédération Wallonie-Bruxelles) | ||
| <http://www.enseignement.be/index.php?page=23953> | ||
| - German-speaking Community (Deutschsprachige Gemeinschaft) | ||
| <https://ostbelgienbildung.be/desktopdefault.aspx/tabid-2212/4397_read-31727/> | ||
|
|
||
| Notes: | ||
| * Belgium has three school systems with different vacation rules: | ||
| - VLG: Flemish Community | ||
| - WBR: French Community (Wallonia + Brussels French schools) | ||
| - GER: German-speaking Community (Ostbelgien) | ||
| * School holiday rules are based on official education calendars. | ||
| """ | ||
|
|
||
| country = "BE" | ||
| default_language = "nl" | ||
| supported_categories = (BANK, PUBLIC) | ||
| supported_categories = (BANK, PUBLIC, SCHOOL) | ||
| supported_languages = ("de", "en_US", "fr", "nl", "uk") | ||
|
|
||
| subdivisions = ( | ||
| "VLG", # Flemish Community. | ||
| "WBR", # French Community (Wallonia + Brussels French schools) | ||
| "GER", # German-speaking Community. | ||
| ) | ||
|
|
||
| def __init__(self, *args, **kwargs): | ||
| ChristianHolidays.__init__(self) | ||
| InternationalHolidays.__init__(self) | ||
|
|
@@ -48,7 +85,7 @@ | |
| self._add_easter_monday(tr("Paasmaandag")) | ||
|
|
||
| # Labor Day. | ||
| self._add_labor_day(tr("Dag van de Arbeid")) | ||
|
Check failure on line 88 in holidays/countries/belgium.py
|
||
|
|
||
| # Ascension Day. | ||
| self._add_ascension_thursday(tr("O. L. H. Hemelvaart")) | ||
|
|
@@ -84,6 +121,225 @@ | |
| # Bank Holiday. | ||
| self._add_christmas_day_two(tr("Banksluitingsdag")) | ||
|
|
||
| def _add_multiday_holiday( | ||
| self, start_date: date, duration_days: int, *, name: str | None = None | ||
| ) -> set[date]: | ||
| """Override to start adding holidays directly from start_date.""" | ||
| return super()._add_multiday_holiday( | ||
| _timedelta(start_date, -1), | ||
| duration_days=duration_days, | ||
| name=name, | ||
| ) | ||
|
KJhellico marked this conversation as resolved.
|
||
|
|
||
| def _populate_subdiv_vlg_school_holidays(self): | ||
| """ | ||
| School holidays for the Flemish Community (Vlaamse Gemeenschap). | ||
|
|
||
| Most vacation periods are rule-based and can be calculated | ||
| algorithmically, therefore they are implemented here. | ||
| """ | ||
|
Comment on lines
+130
to
+135
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win Consider deduplicating the shared Christmas-break logic across VLG/WBR/GER. The Christmas-break block (the December portion + January spillover with Also applies to: 218-221, 307-313 🤖 Prompt for AI Agents |
||
| year = self._year | ||
| easter = self._easter_sunday | ||
|
|
||
|
KJhellico marked this conversation as resolved.
|
||
| # Christmas holidays (week of 25 December). | ||
| christmas = date(year, DEC, 25) | ||
| start = _get_nth_weekday_from(-1 if christmas.weekday() <= 4 else 1, 0, christmas) | ||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||
| self._add_multiday_holiday(start, 13, name=tr("Kerstvakantie")) | ||
|
KJhellico marked this conversation as resolved.
Outdated
KJhellico marked this conversation as resolved.
Outdated
|
||
|
|
||
| # Previous year's Christmas (for January spillover). | ||
| prev_christmas = date(year - 1, DEC, 25) | ||
| prev_start = _get_nth_weekday_from( | ||
| -1 if prev_christmas.weekday() <= 4 else 1, | ||
| 0, | ||
| prev_christmas, | ||
| ) | ||
| self._add_multiday_holiday(prev_start, 14, name=tr("Kerstvakantie")) | ||
|
|
||
| # Carnival holidays. | ||
| krokus_raw = _timedelta(easter, days=-47) | ||
| krokus_start = _get_nth_weekday_from(-1, MON, krokus_raw) | ||
| self._add_multiday_holiday(krokus_start, 7, name=tr("Krokusvakantie")) | ||
|
KJhellico marked this conversation as resolved.
Outdated
|
||
|
|
||
| # Easter holidays. | ||
| # - March Easter → week before Easter. | ||
| # - Easter after 15 April → second Monday before Easter. | ||
| # - Otherwise → first Monday of April. | ||
| easter_start = _get_nth_weekday_of_month(1, MON, APR, self._year) | ||
| easter_duration = 14 | ||
|
|
||
| if easter.month == 3: | ||
| easter_start = _timedelta(easter, -6) | ||
|
cubic-dev-ai[bot] marked this conversation as resolved.
Outdated
|
||
| elif easter.day >= 16: | ||
| easter_start = _timedelta(easter, -13) | ||
| easter_duration = 15 | ||
| self._add_multiday_holiday(easter_start, easter_duration, name=tr("Paasvakantie")) | ||
|
KJhellico marked this conversation as resolved.
|
||
|
|
||
| # Labour Day. | ||
|
KJhellico marked this conversation as resolved.
Outdated
|
||
| self._add_labor_day(tr("Dag van de Arbeid")) | ||
|
|
||
| # Ascension Day. | ||
| self._add_ascension_thursday(tr("Hemelvaart")) | ||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||
|
|
||
| # Friday after Ascension. | ||
|
KJhellico marked this conversation as resolved.
Outdated
|
||
| ascension_friday = _timedelta(easter, days=40) | ||
| self._add_holiday(tr("Vrijdag na O. L. H. Hemelvaart"), ascension_friday) | ||
|
KJhellico marked this conversation as resolved.
Outdated
|
||
|
|
||
| # Whit Monday / Pentecost Monday. | ||
|
KJhellico marked this conversation as resolved.
Outdated
|
||
| self._add_whit_monday(tr("Pinkstermaandag")) | ||
|
Comment on lines
+185
to
+195
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Public holidays are being re-emitted inside SCHOOL population methods. Labor Day, Ascension Day, Friday after Ascension, Whit Monday, Armistice Day, Easter Monday, etc. are public holidays already populated via
If the intent is "school-closed days that aren't already public holidays", drop the public-holiday calls and only register break periods + community-specific days (e.g., Sep 27 for WBR, Nov 15 for GER, Dec 24 for GER if school-specific). Otherwise the SCHOOL category will look like a mismatched superset of PUBLIC. Also applies to: 274-290, 370-389 🤖 Prompt for AI Agents |
||
|
|
||
| # Summer holidays. | ||
|
KJhellico marked this conversation as resolved.
Outdated
|
||
| start = date(year, JUL, 1) | ||
| end = date(year, SEP, 1) | ||
| self._add_multiday_holiday(start, (end - start).days, name=tr("Zomervakantie")) | ||
|
KJhellico marked this conversation as resolved.
Outdated
|
||
|
|
||
| # Autumn holidays (week of 1 November). | ||
| nov_1 = date(year, NOV, 1) | ||
| start = _get_nth_weekday_from(-1 if nov_1.weekday() <= 5 else 1, 0, nov_1) | ||
| self._add_multiday_holiday(start, 7, name=tr("Herfstvakantie")) | ||
|
KJhellico marked this conversation as resolved.
Outdated
|
||
|
|
||
| # Armistice Day (End of World War I). | ||
|
KJhellico marked this conversation as resolved.
Outdated
|
||
| self._add_remembrance_day(tr("Wapenstilstand")) | ||
|
|
||
| def _populate_subdiv_wbr_school_holidays(self): | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In 2022, the school year schedule in the French-speaking community has changed, which must be considered. |
||
| """ | ||
| School holidays for the French Community (Wallonie-Bruxelles). | ||
| Based on the official compulsory education calendar. | ||
| """ | ||
| year = self._year | ||
| easter = self._easter_sunday | ||
|
|
||
| # Autumn holidays. | ||
| herfst_start = _get_nth_weekday_of_month(3, MON, OCT, year) | ||
| self._add_multiday_holiday(herfst_start, 14, name=tr("Herfstvakantie")) | ||
|
|
||
| # current year's Christmas. | ||
| christmas = date(year, DEC, 25) | ||
| kerst_start = _get_nth_weekday_from(-1 if christmas.weekday() <= 4 else 1, 0, christmas) | ||
| self._add_multiday_holiday(kerst_start, 14, name=tr("Kerstvakantie")) | ||
|
|
||
| # Previous year's Christmas (for January spillover). | ||
| prev_christmas = date(year - 1, DEC, 25) | ||
| prev_start = _get_nth_weekday_from( | ||
| -1 if prev_christmas.weekday() <= 4 else 1, | ||
| 0, | ||
| prev_christmas, | ||
| ) | ||
| self._add_multiday_holiday(prev_start, 14, name=tr("Kerstvakantie")) | ||
|
|
||
| # Carnival holidays. | ||
| krokus_raw = _timedelta(easter, days=-49) | ||
| krokus_start = _get_nth_weekday_from(1, MON, krokus_raw) | ||
| self._add_multiday_holiday(krokus_start, 14, name=tr("Krokusvakantie")) | ||
|
|
||
| # Spring Holidays. | ||
| paas_start = _get_nth_weekday_of_month(4, MON, APR, year) | ||
| self._add_multiday_holiday(paas_start, 14, name=tr("Paasvakantie")) | ||
|
|
||
| # Summer holidays. | ||
| zomer_start = _get_nth_weekday_of_month(1, SAT, JUL, year) | ||
|
|
||
| # School restart. | ||
| school_restart = _get_nth_weekday_of_month(4, MON, AUG, year) | ||
|
|
||
| # Summer ends day before school restart. | ||
| zomer_end = _timedelta(school_restart, -1) | ||
|
|
||
| duration = (zomer_end - zomer_start).days + 1 | ||
|
|
||
| self._add_multiday_holiday( | ||
| zomer_start, | ||
| duration, | ||
| name=tr("Zomervakantie"), | ||
| ) | ||
| # Armistice Day. | ||
| self._add_remembrance_day(tr("Wapenstilstand")) | ||
|
|
||
| # Labour Day. | ||
| self._add_labor_day(tr("Dag van de Arbeid")) | ||
|
|
||
| # French Community Day. | ||
| self._add_holiday(tr("Feestdag van de Franse Gemeenschap"), date(year, SEP, 27)) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win Use the existing
♻️ Suggested fix- self._add_holiday(tr("Feestdag van de Franse Gemeenschap"), date(year, SEP, 27))
+ self._add_holiday_sep_27(tr("Feestdag van de Franse Gemeenschap"))- self._add_holiday(tr("Feestdag van de Duitstalige Gemeenschap"), date(year, NOV, 15))
+ self._add_holiday_nov_15(tr("Feestdag van de Duitstalige Gemeenschap"))- self._add_holiday(tr("Heiligavond"), date(year, DEC, 24))
+ self._add_holiday_dec_24(tr("Heiligavond"))Also applies to: 377-377, 389-389 🤖 Prompt for AI Agents |
||
|
|
||
| # Easter Monday. | ||
| self._add_easter_monday(tr("Paasmaandag")) | ||
|
|
||
| # Ascension Day. | ||
| self._add_ascension_thursday(tr("Hemelvaart")) | ||
|
|
||
| # Whit Monday. | ||
| self._add_whit_monday(tr("Pinkstermaandag")) | ||
|
|
||
| def _populate_subdiv_ger_school_holidays(self): | ||
| """ | ||
| School holidays for the German-speaking Community (Deutschsprachige Gemeinschaft). | ||
|
|
||
| Based on official education calendar structure. | ||
| Most vacation periods follow the same framework as other Belgian communities, | ||
| with Easter holidays starting on the Monday after Easter. | ||
| """ | ||
| year = self._year | ||
| easter = self._easter_sunday | ||
|
|
||
| # Christmas holidays (week of 25 December). | ||
| christmas = date(year, DEC, 25) | ||
| # if Christmas is on Monday, holiday will start that day. | ||
| # if Christmas is on Tue-Fri, start the previous Monday. | ||
| # if Christmas is a Sat/Sun, start the following Monday. | ||
| start = _get_nth_weekday_from(-1 if christmas.weekday() <= 4 else 1, 0, christmas) | ||
| self._add_multiday_holiday(start, 13, name=tr("Kerstvakantie")) | ||
|
|
||
| # Previous year's Christmas (for January spillover). | ||
| prev_christmas = date(year - 1, DEC, 25) | ||
| prev_start = _get_nth_weekday_from( | ||
| -1 if prev_christmas.weekday() <= 4 else 1, | ||
| 0, | ||
| prev_christmas, | ||
| ) | ||
| self._add_multiday_holiday(prev_start, 13, name=tr("Kerstvakantie")) | ||
|
|
||
| # Carnival holidays. | ||
| krokus_raw = _timedelta(easter, days=-47) | ||
| krokus_start = _get_nth_weekday_from(-1, MON, krokus_raw) | ||
| self._add_multiday_holiday(krokus_start, 6, name=tr("Krokusvakantie")) | ||
|
|
||
| # Easter holidays. | ||
| # - March Easter → week before Easter. | ||
| # - Easter after 15 April → second Monday before Easter. | ||
| # - Otherwise → first Monday of April. | ||
| paas_start = _timedelta(easter, 1) # Monday after Easter | ||
| self._add_multiday_holiday(paas_start, 13, name=tr("Paasvakantie")) | ||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||
|
|
||
| # Summer holidays. | ||
| start = date(year, JUN, 30) | ||
| end = date(year, SEP, 1) | ||
| self._add_multiday_holiday(start, (end - start).days, name=tr("Zomervakantie")) | ||
|
|
||
| # Autumn holidays (week of 1 November). | ||
| nov_1 = date(year, NOV, 1) | ||
| start = _get_nth_weekday_from(-1 if nov_1.weekday() <= 5 else 1, 0, nov_1) | ||
| self._add_multiday_holiday(start, 6, name=tr("Herfstvakantie")) | ||
|
|
||
| # Armistice Day. | ||
| self._add_remembrance_day(tr("Wapenstilstand")) | ||
|
|
||
| # Labour Day. | ||
| self._add_labor_day(tr("Dag van de Arbeid")) | ||
|
|
||
| # German Community Day. | ||
| self._add_holiday(tr("Feestdag van de Duitstalige Gemeenschap"), date(year, NOV, 15)) | ||
|
|
||
| # Easter Monday. | ||
| self._add_easter_monday(tr("Paasmaandag")) | ||
|
|
||
| # Ascension Day. | ||
| self._add_ascension_thursday(tr("Hemelvaart")) | ||
|
|
||
| # Whit Monday. | ||
| self._add_whit_monday(tr("Pinkstermaandag")) | ||
|
|
||
| # Christmas Eve (observed in German-speaking schools). | ||
| self._add_holiday(tr("Heiligavond"), date(year, DEC, 24)) | ||
|
Comment on lines
+388
to
+389
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Christmas Eve is already inside the GER Christmas break. Lines 327–337 register the Christmas break which spans the week of Dec 25 (and thus includes Dec 24 in most years). Re-adding 🤖 Prompt for AI Agents |
||
|
|
||
|
|
||
| class BE(Belgium): | ||
| pass | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Example label and subdivision are inconsistent.
Line 319 says Flemish, but Line 320 uses
subdiv="WBR"(French Community). Make both refer to the same community.Suggested doc fix
📝 Committable suggestion
🤖 Prompt for AI Agents