Skip to content

Add SCHOOL category and subdivision hooks for Belgium#3195

Draft
Upanshi-Mittal wants to merge 12 commits into
vacanza:devfrom
Upanshi-Mittal:belgium-school-holidays
Draft

Add SCHOOL category and subdivision hooks for Belgium#3195
Upanshi-Mittal wants to merge 12 commits into
vacanza:devfrom
Upanshi-Mittal:belgium-school-holidays

Conversation

@Upanshi-Mittal

@Upanshi-Mittal Upanshi-Mittal commented Jan 6, 2026

Copy link
Copy Markdown

Proposed change

This PR introduces initial infrastructure for Belgian school holidays.

It adds support for the SCHOOL holiday category in Belgium and defines subdivision-specific hooks for the three Belgian school communities:
French, Flemish, and German-speaking.

No placeholder or speculative dates are included; holidays with unclear or unstable definitions are intentionally left unimplemented.

This PR follows the guidance discussed in #2506 and prepares the codebase for future, data-backed implementations.

Type of change

  • New feature (new holidays functionality in general)

Checklist

  • I've read and followed the contributing guidelines.
  • I've run relevant local checks (sanity check for SCHOOL category initialization).

@coderabbitai

coderabbitai Bot commented Jan 6, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features

    • Belgium school holidays added under a new SCHOOL category with region-specific support for Flanders (VLG), Wallonia (WBR) and the German-speaking community (GER).
  • Documentation

    • Examples demonstrating retrieval of Belgium school holidays added; README updated to show new regions and SCHOOL category.
  • Localization

    • Added Belgian school-holiday translations for German, English, French, Ukrainian and Dutch.
  • Tests

    • Added tests validating subdivision-specific Belgium school holiday dates.

Walkthrough

Adds SCHOOL to Belgium's supported categories, implements a multiday helper and subdivision-specific school-holiday population methods for VLG, WBR, and GER, adds BE/BEL aliases, updates README and docs with a Belgium school-holidays example, adds BE locale translations, and adds tests for subdivision summer holidays.

Changes

Belgium school-holidays update

Layer / File(s) Summary
Data Shape
holidays/countries/belgium.py
Added SCHOOL to Belgium.supported_categories and declared subdivisions VLG, WBR, GER.
Helpers / Core Logic
holidays/countries/belgium.py
Imported calendar helpers/constants and added `_add_multiday_holiday(self, start_date, duration_days, *, name: str
Subdivision Population
holidays/countries/belgium.py
Added _populate_subdiv_vlg_school_holidays, _populate_subdiv_wbr_school_holidays, _populate_subdiv_ger_school_holidays implementing subdivision-specific school holiday calculations.
Wiring / Aliases
holidays/countries/belgium.py
Added BE and BEL subclasses as aliases and integrated subdivision handling.
Documentation / Examples
docs/holiday_categories.md, README.md
Inserted Belgium "School Holidays" example (demonstrating SCHOOL and subdivisions) and updated README Belgium row to list regions VLG, WBR, GER and categories including SCHOOL.
Localization
holidays/locale/*/LC_MESSAGES/BE.po
Added new BE translation entries in de, en_US, fr, nl, and uk for multiple school-holiday names (Christmas, Carnival, Easter, Spring, Summer, Autumn, French/German community days, Christmas Eve).
Tests
tests/countries/test_belgium.py
Imported SCHOOL and added tests asserting Zomervakantie dates for subdivisions VLG, WBR, and GER.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

Suggested reviewers

  • arkid15r
  • KJhellico
  • PPsyrius
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: adding SCHOOL category support and subdivision hooks for Belgium school holidays.
Description check ✅ Passed The description clearly relates to the changeset, explaining the SCHOOL category addition, subdivision-specific hooks, and the intentional avoidance of speculative dates.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@KJhellico KJhellico marked this pull request as draft January 6, 2026 15:57
@Upanshi-Mittal Upanshi-Mittal marked this pull request as ready for review January 7, 2026 12:40
@KJhellico

Copy link
Copy Markdown
Collaborator

@Upanshi-Mittal, what review are you waiting for on this draft? %)

@KJhellico KJhellico marked this pull request as draft January 8, 2026 14:54
@Upanshi-Mittal

Copy link
Copy Markdown
Author

@KJhellico this PR is intentionally limited to structure/scaffolding only.
It introduces the SCHOOL category for Belgium and defines subdivision hooks for the three communities, without adding any actual school holiday logic or dates yet.

The idea is to first align on the approach before following up with a separate PR that implements the real holiday rules and data.
I’m not expecting this to be merged as a complete feature at this stage, and I’m fine keeping it as a draft until then.

@KJhellico

Copy link
Copy Markdown
Collaborator

I don't see the point in separate PR. Just fill this one with code.

@Upanshi-Mittal

Copy link
Copy Markdown
Author

Got it. I’ll extend this PR and add the actual Belgian school holiday logic here instead of creating a follow-up PR.

@github-actions github-actions Bot added the doc label Jan 15, 2026
@Upanshi-Mittal

Copy link
Copy Markdown
Author

This PR now includes full implementation of Belgian school holidays for VLG, WBR and GER subdivisions along with documentation examples.

Comment thread holidays/countries/belgium.py Outdated
* <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>
def nth_weekday_of_month(year, month, weekday, n):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I already noted,

Make sure you have an understanding of the established code style and approaches used in project.

The code currently doesn't meet the project requirements at all.

@Upanshi-Mittal

Copy link
Copy Markdown
Author

@KJhellico Thanks for pointing that out.

I realize now that I implemented the logic without properly following the existing country implementations and code style. I’ll refactor this PR to align with the established patterns used in the project, remove unnecessary custom helpers, and simplify the holiday calculations.

I’ll push an updated version shortly.

@Upanshi-Mittal

Copy link
Copy Markdown
Author

@KJhellico one small question before I go ahead and refactor more.

Paasvakantie is the part that is confusing me the most. I tried to derive it only from Easter-based rules, but for multiple years (like 2026, 2027, 2029) it doesn’t match the official Flemish school calendars. So I’m not sure what approach is preferred here.

Should I:

  1. keep it fully rule-based even if it’s not perfectly accurate,
  2. use an exception mapping with officially published dates for Paasvakantie,
  3. or leave Paasvakantie unimplemented for now with a TODO?

I just want to make sure I’m following the project’s direction before continuing.

@KJhellico

Copy link
Copy Markdown
Collaborator

I tried to derive it only from Easter-based rules

Try to define it in the simplest possible rule (rules) with the fewest exceptions.

@KJhellico

Copy link
Copy Markdown
Collaborator

Official rules (Art. 4):

5° de paasvakantie begint de eerste maandag van april en duurt twee weken.
Indien Pasen in de maand maart valt, dan begint de paasvakantie de maandag na Pasen.
Indien Pasen na 15 april valt, begint de paasvakantie de tweede maandag vóór Pasen.

Comment thread holidays/countries/belgium.py Outdated
Comment thread holidays/countries/belgium.py Outdated
Comment thread holidays/countries/belgium.py
Comment thread holidays/countries/belgium.py Outdated
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jan 25, 2026
@sonarqubecloud

Copy link
Copy Markdown

@KJhellico KJhellico left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since pre-commit check has already passed, we can take a look at the code. :) I also recommend starting to write tests.

Comment thread holidays/countries/belgium.py Outdated
Comment thread holidays/countries/belgium.py Outdated
Comment thread holidays/countries/belgium.py Outdated
Comment thread holidays/countries/belgium.py Outdated
Comment thread holidays/countries/belgium.py Outdated
@KJhellico

Copy link
Copy Markdown
Collaborator

@Upanshi-Mittal, are you planning to complete this PR?

@Upanshi-Mittal

Copy link
Copy Markdown
Author

@KJhellico
Yes, I’m planning to complete it.
I’m currently reviewing the remaining changes needed to align fully with the project style and will push an update this week.

@Upanshi-Mittal Upanshi-Mittal force-pushed the belgium-school-holidays branch from a33785c to 09a7fd2 Compare March 7, 2026 22:25
@github-actions github-actions Bot added the l10n label Mar 7, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@holidays/countries/belgium.py`:
- Around line 305-310: The comment above the Easter holiday logic is incorrect
and describes VLG rules instead of the actual implementation; update the comment
to accurately describe what the code does (compute paas_start via
_timedelta(easter, 1) — the Monday after Easter — and add a 13-day multi-day
holiday via self._add_multiday_holiday(paas_start, 13,
name=tr("Paasvakantie"))), or if the intended rule is the complex VLG logic then
implement that logic in place of the simple _timedelta call; adjust the comment
accordingly and keep references to paas_start, _timedelta, and
_add_multiday_holiday consistent.
- Line 181: The school-holiday calls to _add_ascension_thursday use
tr("Hemelvaart") which doesn't match the public-holiday msgid; update the
school-holiday invocations (the three places that call _add_ascension_thursday)
to use tr("O. L. H. Hemelvaart") so they match the public holiday usage and
existing BE.po entries (alternatively, add "Hemelvaart" as a msgid to all BE.po
files, but prefer aligning code to "O. L. H. Hemelvaart" for consistency).
- Line 146: Replace the magic weekday literal with the exported constant: in the
call to _get_nth_weekday_from that currently uses 0 as the weekday argument
(start = _get_nth_weekday_from(..., 0, christmas)), use the MON constant instead
so it reads start = _get_nth_weekday_from(..., MON, christmas); this keeps usage
consistent with other calls like _get_nth_weekday_from(-1, MON, krokus_raw) and
matches the imported MON from holidays.calendars.gregorian.

In `@holidays/locale/en_US/LC_MESSAGES/BE.po`:
- Around line 119-121: The translator comment for the "Heiligavond" entry lacks
a trailing period; update the comment line that begins with "#. Christmas Eve"
(the entry with msgid "Heiligavond" and msgstr "Christmas Eve") to end with a
period so it reads "#. Christmas Eve." to match en_US comment style conventions.
- Around line 91-93: The translator comment for the entry with msgid
"Kerstvakantie" and msgstr "Christmas Holidays" is singular ("Christmas
Holiday."); update the comment to use the plural form to match msgstr (e.g.,
"Christmas Holidays.") or change msgstr to singular to match the comment—edit
the comment line above msgid so the comment text and the msgstr agree.
- Around line 95-97: Update the translator comment to use the plural form to
match the msgstr; change the comment string that currently reads "Carnival
Holiday." to "Carnival Holidays." so the translator note for msgid
"Krokusvakantie" / msgstr "Carnival Holidays" correctly reflects the plural.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 1af5dc30-ef79-4759-8a7b-bfb37cc6d6cc

📥 Commits

Reviewing files that changed from the base of the PR and between 3a0efc1 and 09a7fd2.

📒 Files selected for processing (6)
  • docs/holiday_categories.md
  • holidays/countries/belgium.py
  • holidays/locale/de/LC_MESSAGES/BE.po
  • holidays/locale/en_US/LC_MESSAGES/BE.po
  • holidays/locale/fr/LC_MESSAGES/BE.po
  • holidays/locale/uk/LC_MESSAGES/BE.po

Comment thread holidays/countries/belgium.py Outdated
Comment thread holidays/countries/belgium.py Outdated
Comment thread holidays/countries/belgium.py Outdated
Comment thread holidays/locale/en_US/LC_MESSAGES/BE.po Outdated
Comment thread holidays/locale/en_US/LC_MESSAGES/BE.po Outdated
Comment thread holidays/locale/en_US/LC_MESSAGES/BE.po Outdated

@KJhellico KJhellico left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Few suggestions on Flemish holidays. The rest of methods also requires similar changes in code style.

Comment thread holidays/countries/belgium.py Outdated
Comment thread holidays/countries/belgium.py Outdated
Comment thread holidays/countries/belgium.py
Comment thread holidays/countries/belgium.py Outdated
Comment thread holidays/countries/belgium.py Outdated
Comment thread holidays/countries/belgium.py Outdated
Comment thread holidays/countries/belgium.py Outdated
Comment thread holidays/countries/belgium.py Outdated
Comment thread holidays/countries/belgium.py Outdated
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
23.2% Duplication on New Code (required ≤ 5%)

See analysis details on SonarQube Cloud

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
holidays/countries/belgium.py (1)

251-252: ⚠️ Potential issue | 🟡 Minor

Keep the l10n comments in en_US spelling.

These adjacent comments are part of the localization source. Using Labour Day. here will drift the generated catalogs away from the existing en_US wording. Change both to Labor Day..

Based on learnings, message comments should always use en_US spelling conventions.

Also applies to: 320-321

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@holidays/countries/belgium.py` around lines 251 - 252, The comment above the
localization call uses en_GB spelling "Labour Day."; update the comment text to
en_US spelling "Labor Day." for the instance adjacent to
self._add_labor_day(tr("Dag van de Arbeid")) and the other duplicate occurrence
near the second self._add_labor_day call (lines noted in the review) so the l10n
source comments remain consistent with en_US conventions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@holidays/countries/belgium.py`:
- Around line 238-240: The current code calls tr("Paasvakantie") in the Belgium
holiday block (see _get_nth_weekday_of_month and self._add_multiday_holiday use)
but that msgid is shared with the general Easter Holidays; create a distinct
Dutch msgid for the WBR spring break (e.g. replace tr("Paasvakantie") with
tr("WBR Paasvakantie") in the self._add_multiday_holiday call) and add
corresponding entries to the BE .po files (and other locale .po files as needed)
so the WBR term can be translated independently from the existing Easter
Holidays translation.

---

Duplicate comments:
In `@holidays/countries/belgium.py`:
- Around line 251-252: The comment above the localization call uses en_GB
spelling "Labour Day."; update the comment text to en_US spelling "Labor Day."
for the instance adjacent to self._add_labor_day(tr("Dag van de Arbeid")) and
the other duplicate occurrence near the second self._add_labor_day call (lines
noted in the review) so the l10n source comments remain consistent with en_US
conventions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: cebce084-23f8-4974-948b-c834e21ce2ac

📥 Commits

Reviewing files that changed from the base of the PR and between 09a7fd2 and ca3232a.

📒 Files selected for processing (2)
  • holidays/countries/belgium.py
  • holidays/locale/en_US/LC_MESSAGES/BE.po

Comment thread holidays/countries/belgium.py Outdated
@Upanshi-Mittal

Copy link
Copy Markdown
Author

The duplication mainly comes from adding public holidays (e.g. Labor Day) inside each subdivision’s school holiday method. Since these are also public holidays, I wanted to confirm whether they should remain duplicated in the school methods or be handled only in _populate_public_holidays() to avoid duplication.

Comment thread holidays/countries/belgium.py Outdated
Comment thread holidays/countries/belgium.py Outdated
Comment thread holidays/countries/belgium.py Outdated
Comment thread holidays/countries/belgium.py Outdated
Comment thread holidays/countries/belgium.py Outdated
Comment thread holidays/countries/belgium.py
# Armistice Day.
self._add_remembrance_day(tr("Wapenstilstand"))

def _populate_subdiv_wbr_school_holidays(self):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The 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.

@Upanshi-Mittal

Copy link
Copy Markdown
Author

Hi! I was working on the WBR (Fédération Wallonie-Bruxelles) school holidays and noticed something after the 2022 reform. It doesn’t seem to follow a simple relation with Easter anymore. Carnival and Spring holidays don’t match a fixed offset like “Easter - 48 days” for all years (for example 2025), so it feels more like a cycle-based system (7 weeks school + 2 weeks break).

Also, older data (pre-2022) isn’t very consistent, so it’s hard to derive a single formula that works everywhere.
What do you think would be better here:

  1. Using a cycle-based approach, or
  2. Keeping predefined dates for recent years?

I can proceed with either approach.

@KJhellico

Copy link
Copy Markdown
Collaborator

older data (pre-2022) isn’t very consistent, so it’s hard to derive a single formula that works everywhere

Can you give an example?

@Upanshi-Mittal

Copy link
Copy Markdown
Author

Can you give an example?

for example if we try something simple like “Carnival = Easter - 48 days”, it works for some years but not all.
Like in 2025, Easter is on April 20. If we subtract 48 days, we get March 3, but the actual Carnival holiday starts on February 24.
So this kind of fixed formula doesn’t always match the real dates.

And when I checked older (pre-2022) data, the holiday positions also don’t follow a consistent pattern with respect to Easter, so it’s hard to come up with one rule that works for every year.

@KJhellico

Copy link
Copy Markdown
Collaborator

Like in 2025

But the rules have changed since 2022!

@KJhellico

Copy link
Copy Markdown
Collaborator

@Upanshi-Mittal, are you planning to finish this PR?

@Upanshi-Mittal

Copy link
Copy Markdown
Author

Yes, I’m finishing this PR today. I’ve updated the holiday logic, added test coverage for school holidays, ensured category isolation, and resolved all linting issues. I’ll push the latest changes shortly.

@github-actions github-actions Bot added the test label May 6, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

♻️ Duplicate comments (1)
holidays/countries/belgium.py (1)

243-253: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Post-2022 WBR spring rule is applied to all years.

The block is documented as “post-2022”, but there is no year gate, so pre-2022 years get the reformed rule too.

Suggested fix direction
-        # Spring Holidays (WBR, post-2022):
+        # Spring Holidays (WBR, post-2022):
         # Start on the last Monday of April.
         # If this falls before April 25, shift to the following Monday (in May).
-
-        apr_30 = date(year, APR, 30)
-
-        spring_start = _get_nth_weekday_from(-1, MON, apr_30)
-
-        if spring_start.day < 25:
-            spring_start += timedelta(days=7)
-        self._add_multiday_holiday(spring_start, 14, name=tr("Lentevakantie"))
+        if year >= 2022:
+            apr_30 = date(year, APR, 30)
+            spring_start = _get_nth_weekday_from(-1, MON, apr_30)
+            if spring_start.day < 25:
+                spring_start += timedelta(days=7)
+            self._add_multiday_holiday(spring_start, 14, name=tr("Lentevakantie"))
+        else:
+            # Apply pre-2022 WBR spring-break rule (or explicit exceptions).
+            ...
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@holidays/countries/belgium.py` around lines 243 - 253, The post-2022 WBR
spring rule is being applied unconditionally; guard the logic that computes
apr_30, spring_start (using _get_nth_weekday_from) and the subsequent
shift/adding of the 14-day holiday via _add_multiday_holiday so it only runs for
year >= 2022 (or the intended reform effective year). Put a simple year check
(e.g., if year >= 2022:) around the block that computes apr_30, spring_start and
calls _add_multiday_holiday(name=tr("Lentevakantie")), leaving pre-2022 behavior
unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/holiday_categories.md`:
- Around line 319-321: The example label and subdivision mismatch: the variable
be_flemish_schools and heading say "Flemish Community" but the code uses
subdiv="WBR" (Walloon/Brussels); update the subdiv to the Flemish code (e.g.,
use subdiv="VLG") or change the heading/variable to match WBR so both are
consistent; adjust the instantiation in holidays.Belgium(...) where
be_flemish_schools and SCHOOL are used to reflect the chosen community.

In `@holidays/countries/belgium.py`:
- Around line 141-142: The category guard in
_populate_subdiv_vlg_school_holidays, _populate_subdiv_wbr_school_holidays, and
_populate_subdiv_ger_school_holidays currently returns early unless
self.categories == {SCHOOL}, which skips SCHOOL when callers request mixed
categories; change the guard to return only if SCHOOL is not in self.categories
(e.g., if SCHOOL not in self.categories: return) so SCHOOL holidays are included
for mixed-category requests, and apply the same change to the other occurrences
flagged in the file.

In `@tests/countries/test_belgium.py`:
- Around line 304-342: Add spot-check tests for other school holiday types
(Paasvakantie and Krokusvakantie/carnival) in the same style as
test_vlg_summer_holidays/test_wbr_summer_holidays/test_ger_summer_holidays:
create new test methods (e.g., test_vlg_paasvakantie, test_wbr_krokusvakantie,
test_ger_paasvakantie) that call self.assertHolidayName with
Belgium(subdiv="VLG"/"WBR"/"GER", categories=SCHOOL), the appropriate holiday
name strings ("Paasvakantie", "Krokusvakantie" or "Carnaval"/"Springvakantie" as
used by the codebase), and a few representative dates (including post-2022
reform years) to validate your implementation; mirror the pattern and assertion
helper used in the existing tests to ensure coverage beyond summer holidays.
- Around line 344-347: test_school_not_in_public currently only asserts
school-holiday absence on the base Belgium() instance; extend it to also
instantiate Belgium(subdiv=...) for each relevant subdivision (e.g., "VLG",
"WAL", "BRU") and assertNoHoliday for the same school dates ("2024-07-06",
"2024-05-02") to ensure SCHOOL-category dates do not leak into
subdivision-specific instances when categories does not include SCHOOL; locate
the test function test_school_not_in_public and add one assertion per
subdivision using Belgium(subdiv="...") without passing categories=SCHOOL.

---

Duplicate comments:
In `@holidays/countries/belgium.py`:
- Around line 243-253: The post-2022 WBR spring rule is being applied
unconditionally; guard the logic that computes apr_30, spring_start (using
_get_nth_weekday_from) and the subsequent shift/adding of the 14-day holiday via
_add_multiday_holiday so it only runs for year >= 2022 (or the intended reform
effective year). Put a simple year check (e.g., if year >= 2022:) around the
block that computes apr_30, spring_start and calls
_add_multiday_holiday(name=tr("Lentevakantie")), leaving pre-2022 behavior
unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 89252a74-fbc9-4387-a68c-5d26286aea27

📥 Commits

Reviewing files that changed from the base of the PR and between ca3232a and fb49901.

📒 Files selected for processing (9)
  • README.md
  • docs/holiday_categories.md
  • holidays/countries/belgium.py
  • holidays/locale/de/LC_MESSAGES/BE.po
  • holidays/locale/en_US/LC_MESSAGES/BE.po
  • holidays/locale/fr/LC_MESSAGES/BE.po
  • holidays/locale/nl/LC_MESSAGES/BE.po
  • holidays/locale/uk/LC_MESSAGES/BE.po
  • tests/countries/test_belgium.py

Comment on lines +319 to +321
# Flemish Community school holidays
be_flemish_schools = holidays.Belgium(subdiv="WBR", categories=SCHOOL, years=2025)
for date, name in sorted(be_flemish_schools.items()):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

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
-# Flemish Community school holidays
-be_flemish_schools = holidays.Belgium(subdiv="WBR", categories=SCHOOL, years=2025)
+# Flemish Community school holidays
+be_flemish_schools = holidays.Belgium(subdiv="VLG", categories=SCHOOL, years=2025)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Flemish Community school holidays
be_flemish_schools = holidays.Belgium(subdiv="WBR", categories=SCHOOL, years=2025)
for date, name in sorted(be_flemish_schools.items()):
# Flemish Community school holidays
be_flemish_schools = holidays.Belgium(subdiv="VLG", categories=SCHOOL, years=2025)
for date, name in sorted(be_flemish_schools.items()):
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/holiday_categories.md` around lines 319 - 321, The example label and
subdivision mismatch: the variable be_flemish_schools and heading say "Flemish
Community" but the code uses subdiv="WBR" (Walloon/Brussels); update the subdiv
to the Flemish code (e.g., use subdiv="VLG") or change the heading/variable to
match WBR so both are consistent; adjust the instantiation in
holidays.Belgium(...) where be_flemish_schools and SCHOOL are used to reflect
the chosen community.

Comment thread holidays/countries/belgium.py Outdated
Comment on lines +141 to +142
if self.categories != {SCHOOL}:
return

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Category guard drops SCHOOL holidays for mixed-category requests.

These methods return early unless categories are exactly {SCHOOL}. If a caller requests (PUBLIC, SCHOOL), subdivision SCHOOL holidays are silently skipped.

Suggested fix
-        if self.categories != {SCHOOL}:
-            return

Apply the same removal in all three methods:

  • _populate_subdiv_vlg_school_holidays
  • _populate_subdiv_wbr_school_holidays
  • _populate_subdiv_ger_school_holidays

Also applies to: 213-214, 305-306

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@holidays/countries/belgium.py` around lines 141 - 142, The category guard in
_populate_subdiv_vlg_school_holidays, _populate_subdiv_wbr_school_holidays, and
_populate_subdiv_ger_school_holidays currently returns early unless
self.categories == {SCHOOL}, which skips SCHOOL when callers request mixed
categories; change the guard to return only if SCHOOL is not in self.categories
(e.g., if SCHOOL not in self.categories: return) so SCHOOL holidays are included
for mixed-category requests, and apply the same change to the other occurrences
flagged in the file.

Comment on lines +304 to +342
def test_vlg_summer_holidays(self):
name = "Zomervakantie"

self.assertHolidayName(
name,
Belgium(subdiv="VLG", categories=SCHOOL),
"2025-07-05",
"2024-07-06",
"2023-07-01",
"2022-08-07",
"2021-08-18",
)

def test_wbr_summer_holidays(self):
name = "Zomervakantie"

self.assertHolidayName(
name,
Belgium(subdiv="WBR", categories=SCHOOL),
"2025-08-11",
"2024-07-06",
"2023-07-26",
"2022-08-07",
"2021-08-18",
)

def test_ger_summer_holidays(self):
name = "Zomervakantie"

self.assertHolidayName(
name,
Belgium(subdiv="GER", categories=SCHOOL),
"2025-07-07",
"2024-08-30",
"2023-07-20",
"2022-07-10",
"2022-07-14",
"2021-08-04",
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major | 🏗️ Heavy lift

Tests cover only Zomervakantie — other implemented school holidays have no coverage.

Per the PR description, the implementation includes Paasvakantie, carnival/spring breaks, and other school periods for VLG, WBR, and GER. The new tests assert only summer holiday dates. If the other holiday calculation logic contains bugs (the PR thread explicitly discusses correctness concerns for Paasvakantie and WBR carnival/spring breaks after the 2022 reform), there's no regression net here.

Consider adding at minimum a few spot-check dates for Paasvakantie and carnival/krokusvakantie per community.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/countries/test_belgium.py` around lines 304 - 342, Add spot-check tests
for other school holiday types (Paasvakantie and Krokusvakantie/carnival) in the
same style as
test_vlg_summer_holidays/test_wbr_summer_holidays/test_ger_summer_holidays:
create new test methods (e.g., test_vlg_paasvakantie, test_wbr_krokusvakantie,
test_ger_paasvakantie) that call self.assertHolidayName with
Belgium(subdiv="VLG"/"WBR"/"GER", categories=SCHOOL), the appropriate holiday
name strings ("Paasvakantie", "Krokusvakantie" or "Carnaval"/"Springvakantie" as
used by the codebase), and a few representative dates (including post-2022
reform years) to validate your implementation; mirror the pattern and assertion
helper used in the existing tests to ensure coverage beyond summer holidays.

Comment on lines +344 to +347
def test_school_not_in_public(self):

self.assertNoHoliday("2024-07-06")
self.assertNoHoliday("2024-05-02")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

test_school_not_in_public only checks the base (no-subdivision) Belgium instance.

The test verifies that Belgium() (no subdiv, no SCHOOL) doesn't contain school-holiday dates. It doesn't cover Belgium(subdiv="VLG") without categories=SCHOOL, which is the more realistic isolation risk. Worth adding one assertion per subdivision to guard against accidental category leakage when a subdivision is active.

🛡️ Proposed expansion
 def test_school_not_in_public(self):
     self.assertNoHoliday("2024-07-06")
     self.assertNoHoliday("2024-05-02")
+    self.assertNoHoliday(Belgium(subdiv="VLG"), "2024-07-06")
+    self.assertNoHoliday(Belgium(subdiv="WBR"), "2024-07-06")
+    self.assertNoHoliday(Belgium(subdiv="GER"), "2024-07-06")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/countries/test_belgium.py` around lines 344 - 347,
test_school_not_in_public currently only asserts school-holiday absence on the
base Belgium() instance; extend it to also instantiate Belgium(subdiv=...) for
each relevant subdivision (e.g., "VLG", "WAL", "BRU") and assertNoHoliday for
the same school dates ("2024-07-06", "2024-05-02") to ensure SCHOOL-category
dates do not leak into subdivision-specific instances when categories does not
include SCHOOL; locate the test function test_school_not_in_public and add one
assertion per subdivision using Belgium(subdiv="...") without passing
categories=SCHOOL.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 issues found across 9 files

Confidence score: 5/5

  • This looks low risk to merge: all findings are documentation/translation wording issues with no runtime behavior changes indicated.
  • Most severe item is in docs/holiday_categories.md where subdiv="WBR" conflicts with a Flemish label; this could mislead users, but it is a correctness fix in examples/docs rather than a functional regression.
  • README.md and holidays/locale/de/LC_MESSAGES/BE.po need terminology clarification (community labels and “spring holidays” wording), which is minor housekeeping with limited user impact.
  • Pay close attention to docs/holiday_categories.md, README.md, holidays/locale/de/LC_MESSAGES/BE.po - align Belgium subdivision/community terminology and translator comments to avoid confusion.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="holidays/locale/de/LC_MESSAGES/BE.po">

<violation number="1" location="holidays/locale/de/LC_MESSAGES/BE.po:123">
P3: The translator comment says “Spring Day”, but `Lentevakantie`/`Frühlingsferien` is “spring holidays/break”. Update the comment to match the string meaning.</violation>
</file>

<file name="docs/holiday_categories.md">

<violation number="1" location="docs/holiday_categories.md:320">
P2: Belgium school-holidays example uses `subdiv="WBR"` (French Community) while the comment/variable says Flemish. Use `subdiv="VLG"` for Flemish Community.</violation>
</file>

<file name="README.md">

<violation number="1" location="README.md:293">
P3: Clarify Belgium subdivision labels in the table: `VLG/WBR/GER` are school communities (Flemish/French/German-speaking), not generic “Regions”.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.

from holidays.constants import SCHOOL

# Flemish Community school holidays
be_flemish_schools = holidays.Belgium(subdiv="WBR", categories=SCHOOL, years=2025)

@cubic-dev-ai cubic-dev-ai Bot May 6, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Belgium school-holidays example uses subdiv="WBR" (French Community) while the comment/variable says Flemish. Use subdiv="VLG" for Flemish Community.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At docs/holiday_categories.md, line 320:

<comment>Belgium school-holidays example uses `subdiv="WBR"` (French Community) while the comment/variable says Flemish. Use `subdiv="VLG"` for Flemish Community.</comment>

<file context>
@@ -299,6 +299,29 @@ for date, name in sorted(germany_catholic.items()):
+from holidays.constants import SCHOOL
+
+# Flemish Community school holidays
+be_flemish_schools = holidays.Belgium(subdiv="WBR", categories=SCHOOL, years=2025)
+for date, name in sorted(be_flemish_schools.items()):
+    print(date, name)
</file context>
Suggested change
be_flemish_schools = holidays.Belgium(subdiv="WBR", categories=SCHOOL, years=2025)
be_flemish_schools = holidays.Belgium(subdiv="VLG", categories=SCHOOL, years=2025)
Fix with Cubic

msgid "Heiligavond"
msgstr "Heiligabend"

#. Spring Day.

@cubic-dev-ai cubic-dev-ai Bot May 6, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The translator comment says “Spring Day”, but Lentevakantie/Frühlingsferien is “spring holidays/break”. Update the comment to match the string meaning.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At holidays/locale/de/LC_MESSAGES/BE.po, line 123:

<comment>The translator comment says “Spring Day”, but `Lentevakantie`/`Frühlingsferien` is “spring holidays/break”. Update the comment to match the string meaning.</comment>

<file context>
@@ -87,3 +87,39 @@ msgstr "Freitag nach Christi Himmelfahrt"
+msgid "Heiligavond"
+msgstr "Heiligabend"
+
+#. Spring Day.
+msgid "Lentevakantie"
+msgstr "Frühlingsferien"
</file context>
Suggested change
#. Spring Day.
#. Spring Holidays.
Fix with Cubic

Comment thread README.md
<td>Belgium</td>
<td>BE</td>
<td></td>
<td>Regions: VLG, WBR, GER</td>

@cubic-dev-ai cubic-dev-ai Bot May 6, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: Clarify Belgium subdivision labels in the table: VLG/WBR/GER are school communities (Flemish/French/German-speaking), not generic “Regions”.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At README.md, line 293:

<comment>Clarify Belgium subdivision labels in the table: `VLG/WBR/GER` are school communities (Flemish/French/German-speaking), not generic “Regions”.</comment>

<file context>
@@ -290,9 +290,9 @@ any) in brackets, available languages and additional holiday categories. All cou
 <td>Belgium</td>
 <td>BE</td>
-<td></td>
+<td>Regions: VLG, WBR, GER</td>
 <td>de, en_US, fr, <strong>nl</strong>, uk</td>
-<td>BANK</td>
</file context>
Suggested change
<td>Regions: VLG, WBR, GER</td>
<td>Communities: VLG (Flemish), WBR (French), GER (German-speaking)</td>
Fix with Cubic

@KJhellico

Copy link
Copy Markdown
Collaborator

I've updated the code for Flemish.
For American English, the term "Break" is more natural for these types of holidays, so it's best to use it (as has already been done for Germany) I've also standardized the style of comments describing each type of holiday; I recommend following it.
I haven’t looked at the code for other subdivisions yet, but the lack of a difference before/after 2022 for WBR is noticeable.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

♻️ Duplicate comments (3)
docs/holiday_categories.md (1)

319-321: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Example label still mismatches subdivision code.

The comment says "Flemish Community" but the code uses subdiv="WBR" (French Community). The variable name be_flemish_schools also implies Flemish. Use subdiv="VLG" for the Flemish Community.

📝 Suggested fix
 # Flemish Community school holidays
-be_flemish_schools = holidays.Belgium(subdiv="WBR", categories=SCHOOL, years=2025)
+be_flemish_schools = holidays.Belgium(subdiv="VLG", categories=SCHOOL, years=2025)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/holiday_categories.md` around lines 319 - 321, The example's subdivision
code is incorrect: the variable be_flemish_schools and its comment state
"Flemish Community" but the diff uses subdiv="WBR" (Walloon/Brussels); change
the subdivision from "WBR" to "VLG" in the call to
holidays.Belgium(subdiv="WBR", categories=SCHOOL, years=2025) so it correctly
constructs the Flemish community school holidays for be_flemish_schools.
holidays/countries/belgium.py (2)

223-224: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Category guard skips SCHOOL holidays in mixed-category requests.

if self.categories != {SCHOOL}: return returns early whenever the caller passes anything besides exactly {SCHOOL} — e.g. categories=(PUBLIC, SCHOOL). School holidays for WBR and GER will then be silently dropped. The VLG method correctly omits this guard. Either remove the guard entirely (the populate dispatcher already filters by category) or invert it.

🛡️ Suggested fix
-        if self.categories != {SCHOOL}:
-            return

Apply to both _populate_subdiv_wbr_school_holidays and _populate_subdiv_ger_school_holidays.

Also applies to: 315-316

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@holidays/countries/belgium.py` around lines 223 - 224, The early-return
category check currently requires exactly {SCHOOL} and thus drops SCHOOL
holidays when callers pass mixed categories; in both
_populate_subdiv_wbr_school_holidays and _populate_subdiv_ger_school_holidays
(and the other occurrence noted), replace the equality check with a membership
check or remove it — e.g. change the guard from "if self.categories != {SCHOOL}:
return" to "if SCHOOL not in self.categories: return" (or simply remove the
guard since the dispatcher already filters by category) so SCHOOL holidays are
not silently skipped for mixed-category requests.

13-13: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Drop datetime.timedelta; use _timedelta from holidays.calendars.gregorian consistently.

The file already imports _timedelta, but lines 262, 269, 299, 300 still use datetime.timedelta. Past review explicitly asked to switch to _timedelta for the project style.

♻️ Suggested fix
-from datetime import date, timedelta
+from datetime import date

Then update remaining call sites, e.g.:

-        if spring_start.day < 25:
-            spring_start += timedelta(days=7)
+        if spring_start.day < 25:
+            spring_start = _timedelta(spring_start, 7)
-        nov_first = date(year, 11, 1)
-        last_monday = nov_first - timedelta(days=(nov_first.weekday() - MON) % 7)
-        autumn_start = last_monday - timedelta(days=7)
+        nov_first = date(year, NOV, 1)
+        last_monday = _timedelta(nov_first, -((nov_first.weekday() - MON) % 7))
+        autumn_start = _timedelta(last_monday, -7)

Or even simpler, reuse _get_nth_weekday_from(-2, MON, nov_first) for the autumn calc.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@holidays/countries/belgium.py` at line 13, Replace uses of datetime.timedelta
in this module with the project helper _timedelta from
holidays.calendars.gregorian to match the codebase style: change all
datetime.timedelta(...) call sites to _timedelta(...). For the autumn
calculation, prefer reusing _get_nth_weekday_from(-2, MON, nov_first) instead of
manually computing offsets; update any variables referencing nov_first, MON or
the autumn date accordingly (e.g., the places currently using datetime.timedelta
for that calc). Ensure imports still include _timedelta and remove any remaining
direct datetime.timedelta uses.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@holidays/countries/belgium.py`:
- Line 281: Replace manual date constructions that call
self._add_holiday(tr("..."), date(year, SEP, 27/ NOV, 15/ DEC, 24)) with the
project's helper methods: use self._add_holiday_sep_27(tr("...")),
self._add_holiday_nov_15(tr("...")), and self._add_holiday_dec_24(tr("..."))
respectively; locate the three occurrences where date(year, SEP, 27), date(year,
NOV, 15), and date(year, DEC, 24) are passed into self._add_holiday and swap
them to the corresponding _add_holiday_* helper calls to follow the idiomatic
style and remove manual date boilerplate.
- Around line 130-135: Extract the duplicated Christmas-break calculation (the
December period plus the January spillover using the `(4 - new_year.weekday()) %
7 + 3` duration) into a single private helper method (suggested name
`_add_christmas_school_break(self, year, holidays)` or similar) and replace the
copy-pasted blocks in the three subdivision methods for VLG/WBR/GER with calls
to that helper; ensure the helper accepts the year and the holidays collection
(or uses the instance state) and adds the same start/end dates so behavior
remains identical.
- Around line 388-389: The explicit addition of Heiligavond via
self._add_holiday(tr("Heiligavond"), date(year, DEC, 24)) conflicts with the
previously added Christmas break entry (the block that registers the Christmas
break spanning Dec 25 and surrounding days); either remove this Heiligavond line
to avoid collision, or if you intend Dec 24 to be a distinct named day for
Belgium, move the self._add_holiday(tr("Heiligavond"), date(year, DEC, 24)) call
so it executes before the Christmas-break registration and add a comment
documenting that it intentionally overrides/shadows the break entry for Dec 24.
- Line 263: The spring/autumn/WBR rules mix timedelta adjustments and manual
weekday math, and miss a year branch for Carnival; introduce a constant (e.g.,
WBR_REFORM_YEAR = 2022) and refactor the calculations to use the existing helper
utilities (like _get_nth_weekday_from or _get_nth_weekday_of_month) for all
break start dates (spring_start, autumn_start, carnival_start) instead of ad-hoc
timedelta/last-Monday computations, add the missing year-dependent branch for
Carnival matching the reform rule, and update calls to _add_multiday_holiday to
use those computed starts so the method uses one consistent idiom and the
reform-year constant governs branching.
- Around line 185-195: The SCHOOL-population methods are re-adding public
holidays (calls to _add_labor_day, _add_ascension_thursday,
_add_holiday_40_days_past_easter, _add_whit_monday, etc.), causing PUBLIC dates
to leak into SCHOOL and duplicate entries; remove those duplicated _add_*
public-holiday calls from the SCHOOL-specific blocks and instead register only
school-specific closures (break periods) and true community-specific school days
(e.g., Sep 27 for WBR, Nov 15 for GER, Dec 24 for GER if intended) so SCHOOL
only contains non-public school closures; ensure you rely on
_populate_public_holidays for public dates and do not re-add them (also apply
the same fix to the other occurrences referenced in the file around the other
ranges).
- Around line 121-127: Add explicit return type annotations -> None to the three
subdivision population methods that currently lack them:
_populate_subdiv_wbr_school_holidays, _populate_subdiv_ger_school_holidays, and
the third _populate_subdiv_* mentioned around lines 129/217/306 so Ruff stops
flagging them; locate each method definition and append the return annotation
(def _populate_subdiv_wbr_school_holidays(self, ...) -> None:) without changing
their bodies or behavior.
- Around line 247-251: The Carnival (Krokusvakantie) holiday is always added as
14 days via self._add_multiday_holiday(_timedelta(easter, days=-48), 14,
name=tr("Krokusvakantie")) but the duration changed after the 2022 reform;
update the logic around the call in the relevant function to branch on the year
(use the existing year variable) and pass 7 days for years < 2022 and 14 days
for years >= 2022 (keep use of _timedelta(easter, days=-48) and
tr("Krokusvakantie") unchanged so only the duration argument is conditional).

In `@holidays/locale/de/LC_MESSAGES/BE.po`:
- Around line 123-125: The translator comment for msgid "Lentevakantie" in the
de PO file is wrong ("Spring Day." vs expected "Spring Holidays."); do not edit
the `#.` line directly — instead update the holiday comment in the Python source
where the "Lentevakantie" holiday is defined (the holiday name/comment that
generates the msgid) to use "Spring Holidays.", then run `make l10n` to
regenerate the .po files so the translator comment and msgid are consistent
across locales.

---

Duplicate comments:
In `@docs/holiday_categories.md`:
- Around line 319-321: The example's subdivision code is incorrect: the variable
be_flemish_schools and its comment state "Flemish Community" but the diff uses
subdiv="WBR" (Walloon/Brussels); change the subdivision from "WBR" to "VLG" in
the call to holidays.Belgium(subdiv="WBR", categories=SCHOOL, years=2025) so it
correctly constructs the Flemish community school holidays for
be_flemish_schools.

In `@holidays/countries/belgium.py`:
- Around line 223-224: The early-return category check currently requires
exactly {SCHOOL} and thus drops SCHOOL holidays when callers pass mixed
categories; in both _populate_subdiv_wbr_school_holidays and
_populate_subdiv_ger_school_holidays (and the other occurrence noted), replace
the equality check with a membership check or remove it — e.g. change the guard
from "if self.categories != {SCHOOL}: return" to "if SCHOOL not in
self.categories: return" (or simply remove the guard since the dispatcher
already filters by category) so SCHOOL holidays are not silently skipped for
mixed-category requests.
- Line 13: Replace uses of datetime.timedelta in this module with the project
helper _timedelta from holidays.calendars.gregorian to match the codebase style:
change all datetime.timedelta(...) call sites to _timedelta(...). For the autumn
calculation, prefer reusing _get_nth_weekday_from(-2, MON, nov_first) instead of
manually computing offsets; update any variables referencing nov_first, MON or
the autumn date accordingly (e.g., the places currently using datetime.timedelta
for that calc). Ensure imports still include _timedelta and remove any remaining
direct datetime.timedelta uses.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: ca771847-f41a-4969-80d8-417b3aade542

📥 Commits

Reviewing files that changed from the base of the PR and between ca3232a and a72e716.

📒 Files selected for processing (8)
  • README.md
  • docs/holiday_categories.md
  • holidays/countries/belgium.py
  • holidays/locale/de/LC_MESSAGES/BE.po
  • holidays/locale/en_US/LC_MESSAGES/BE.po
  • holidays/locale/fr/LC_MESSAGES/BE.po
  • holidays/locale/nl/LC_MESSAGES/BE.po
  • holidays/locale/uk/LC_MESSAGES/BE.po

Comment thread holidays/countries/belgium.py
Comment on lines +130 to +135
"""
School holidays for the Flemish Community (Vlaamse Gemeenschap).

Most vacation periods are rule-based and can be calculated
algorithmically, therefore they are implemented here.
"""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The 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 (4 - new_year.weekday()) % 7 + 3 duration) is copy-pasted verbatim into all three subdivision methods. Extracting a small private helper (e.g., _add_christmas_school_break(self)) would keep the three methods focused on what differs and avoid future drift.

Also applies to: 218-221, 307-313

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@holidays/countries/belgium.py` around lines 130 - 135, Extract the duplicated
Christmas-break calculation (the December period plus the January spillover
using the `(4 - new_year.weekday()) % 7 + 3` duration) into a single private
helper method (suggested name `_add_christmas_school_break(self, year,
holidays)` or similar) and replace the copy-pasted blocks in the three
subdivision methods for VLG/WBR/GER with calls to that helper; ensure the helper
accepts the year and the holidays collection (or uses the instance state) and
adds the same start/end dates so behavior remains identical.

Comment on lines +185 to +195
# Labor Day.
self._add_labor_day(tr("Dag van de Arbeid"))

# Ascension Day.
self._add_ascension_thursday(tr("O. L. H. Hemelvaart"))

# Friday after Ascension Day.
self._add_holiday_40_days_past_easter(tr("Vrijdag na O. L. H. Hemelvaart"))

# Whit Monday.
self._add_whit_monday(tr("Pinkstermaandag"))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

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 _populate_public_holidays. Duplicating them inside the SCHOOL subdivision methods creates several problems:

  • A user requesting categories=SCHOOL only gets PUBLIC dates leaking into SCHOOL output.
  • A user requesting categories=(PUBLIC, SCHOOL) gets duplicate-name conflicts on the same dates.
  • The set of "public" days varies inconsistently between VLG/WBR/GER methods (e.g., GER adds Christmas Eve and a "German Community Day"; VLG omits Easter Monday but includes Whit Monday; WBR adds French Community Day on Sep 27).

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
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@holidays/countries/belgium.py` around lines 185 - 195, The SCHOOL-population
methods are re-adding public holidays (calls to _add_labor_day,
_add_ascension_thursday, _add_holiday_40_days_past_easter, _add_whit_monday,
etc.), causing PUBLIC dates to leak into SCHOOL and duplicate entries; remove
those duplicated _add_* public-holiday calls from the SCHOOL-specific blocks and
instead register only school-specific closures (break periods) and true
community-specific school days (e.g., Sep 27 for WBR, Nov 15 for GER, Dec 24 for
GER if intended) so SCHOOL only contains non-public school closures; ensure you
rely on _populate_public_holidays for public dates and do not re-add them (also
apply the same fix to the other occurrences referenced in the file around the
other ranges).

Comment on lines +247 to +251
# Carnival Holidays:
# Start on Carnival Monday (48 days before Easter).
# Duration is two weeks.

self._add_multiday_holiday(_timedelta(easter, days=-48), 14, name=tr("Krokusvakantie"))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

WBR Carnival break duration is hardcoded to 14 days regardless of year.

Per your earlier note about the 2022 FWB reform, WBR Carnival was a 1-week break before the 2022–2023 school year and is 2 weeks after. The Autumn block at lines 301–304 already branches on year >= 2022; Carnival needs the same gate, otherwise pre-2022 years will report a 14-day Krokusvakantie that never existed.

🛠️ Suggested fix
-        self._add_multiday_holiday(_timedelta(easter, days=-48), 14, name=tr("Krokusvakantie"))
+        # Carnival Holidays: 1 week pre-2022 reform, 2 weeks afterwards.
+        carnival_duration = 14 if year >= 2022 else 7
+        self._add_multiday_holiday(
+            _timedelta(easter, -48), carnival_duration, name=tr("Krokusvakantie")
+        )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@holidays/countries/belgium.py` around lines 247 - 251, The Carnival
(Krokusvakantie) holiday is always added as 14 days via
self._add_multiday_holiday(_timedelta(easter, days=-48), 14,
name=tr("Krokusvakantie")) but the duration changed after the 2022 reform;
update the logic around the call in the relevant function to branch on the year
(use the existing year variable) and pass 7 days for years < 2022 and 14 days
for years >= 2022 (keep use of _timedelta(easter, days=-48) and
tr("Krokusvakantie") unchanged so only the duration argument is conditional).


if spring_start.day < 25:
spring_start += timedelta(days=7)
self._add_multiday_holiday(spring_start, 14, name=tr("Lentevakantie"))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial | ⚖️ Poor tradeoff

WBR Spring break and pre/post-2022 Autumn rules conflict with the project's "simplest rule with fewest exceptions" guidance.

spring_start += timedelta(days=7) on line 262 plus the manual last-Monday computation on lines 298–300 mix two timedelta styles in the same method. Combined with the year branch on lines 301–304 and the missing year branch on Carnival, the WBR rules are difficult to follow. Consider extracting clear constants (e.g., WBR_REFORM_YEAR = 2022) and computing all break starts via _get_nth_weekday_from / _get_nth_weekday_of_month to keep one consistent calculation idiom.

Also applies to: 302-304

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@holidays/countries/belgium.py` at line 263, The spring/autumn/WBR rules mix
timedelta adjustments and manual weekday math, and miss a year branch for
Carnival; introduce a constant (e.g., WBR_REFORM_YEAR = 2022) and refactor the
calculations to use the existing helper utilities (like _get_nth_weekday_from or
_get_nth_weekday_of_month) for all break start dates (spring_start,
autumn_start, carnival_start) instead of ad-hoc timedelta/last-Monday
computations, add the missing year-dependent branch for Carnival matching the
reform rule, and update calls to _add_multiday_holiday to use those computed
starts so the method uses one consistent idiom and the reform-year constant
governs branching.

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))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Use the existing _add_holiday_* helpers instead of constructing dates manually.

self._add_holiday(tr("..."), date(year, SEP, 27)) can be replaced with self._add_holiday_sep_27(tr("...")), and likewise _add_holiday_nov_15 and _add_holiday_dec_24. The helper form is the project's idiomatic style and avoids the manual date(year, ...) boilerplate.

♻️ 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
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@holidays/countries/belgium.py` at line 281, Replace manual date constructions
that call self._add_holiday(tr("..."), date(year, SEP, 27/ NOV, 15/ DEC, 24))
with the project's helper methods: use self._add_holiday_sep_27(tr("...")),
self._add_holiday_nov_15(tr("...")), and self._add_holiday_dec_24(tr("..."))
respectively; locate the three occurrences where date(year, SEP, 27), date(year,
NOV, 15), and date(year, DEC, 24) are passed into self._add_holiday and swap
them to the corresponding _add_holiday_* helper calls to follow the idiomatic
style and remove manual date boilerplate.

Comment on lines +388 to +389
# Christmas Eve.
self._add_holiday(tr("Heiligavond"), date(year, DEC, 24))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

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 tr("Heiligavond") on Dec 24 will either collide with the existing break entry or shadow it depending on insertion order. If Dec 24 is meant to be a distinct named day for GER, add it before the break and document the override; otherwise drop it.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@holidays/countries/belgium.py` around lines 388 - 389, The explicit addition
of Heiligavond via self._add_holiday(tr("Heiligavond"), date(year, DEC, 24))
conflicts with the previously added Christmas break entry (the block that
registers the Christmas break spanning Dec 25 and surrounding days); either
remove this Heiligavond line to avoid collision, or if you intend Dec 24 to be a
distinct named day for Belgium, move the self._add_holiday(tr("Heiligavond"),
date(year, DEC, 24)) call so it executes before the Christmas-break registration
and add a comment documenting that it intentionally overrides/shadows the break
entry for Dec 24.

Comment on lines +123 to +125
#. Spring Day.
msgid "Lentevakantie"
msgstr "Frühlingsferien"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Translator comment mismatch: "Spring Day." vs sibling locales' "Spring Holidays."

The other locale files (en_US, fr, nl, uk) all use #. Spring Holidays. for msgid "Lentevakantie", but this de file says #. Spring Day. Per project workflow, translator comments are auto-generated from the Python source — please re-run make l10n after confirming the Python comment to keep the comment consistent across locales.

Based on learnings, do not edit translator comments (#. lines) directly in .po files; update the corresponding holiday-name comment in the Python source and run make l10n to regenerate the .po files so msgid/translator-comment mappings stay consistent.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@holidays/locale/de/LC_MESSAGES/BE.po` around lines 123 - 125, The translator
comment for msgid "Lentevakantie" in the de PO file is wrong ("Spring Day." vs
expected "Spring Holidays."); do not edit the `#.` line directly — instead
update the holiday comment in the Python source where the "Lentevakantie"
holiday is defined (the holiday name/comment that generates the msgid) to use
"Spring Holidays.", then run `make l10n` to regenerate the .po files so the
translator comment and msgid are consistent across locales.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 1 file (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="holidays/countries/belgium.py">

<violation number="1" location="holidays/countries/belgium.py:178">
P1: For March Easters, this sets Easter Break to the Monday before Easter instead of the required Monday after Easter, producing incorrect school holiday dates.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.

Comment thread holidays/countries/belgium.py Outdated
@sonarqubecloud

sonarqubecloud Bot commented May 6, 2026

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
13.0% Duplication on New Code (required ≤ 5%)

See analysis details on SonarQube Cloud

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (9)
holidays/countries/belgium.py (9)

223-224: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Category guard is overly restrictive — drops SCHOOL when mixed with other categories.

if self.categories != {SCHOOL}: return skips SCHOOL holidays when callers request e.g. (PUBLIC, SCHOOL). Note also that _populate_subdiv_vlg_school_holidays has no such guard, so the three methods are inconsistent with each other. If you want to keep an explicit guard, use if SCHOOL not in self.categories: return; otherwise just drop it (the dispatch in HolidayBase only invokes _populate_subdiv_*_school_holidays when SCHOOL is requested anyway).

Also applies to: 315-316

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@holidays/countries/belgium.py` around lines 223 - 224, The guard in the
method that currently reads `if self.categories != {SCHOOL}: return` wrongly
excludes SCHOOL when combined with other categories; change it to `if SCHOOL not
in self.categories: return` (or remove the guard entirely) so SCHOOL holidays
are not skipped when callers request multiple categories, and make the same
change in the other occurrence mentioned so the behavior matches
`_populate_subdiv_vlg_school_holidays`.

129-129: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Add -> None return annotation (Ruff ANN202).

Same applies to _populate_subdiv_wbr_school_holidays (line 217) and _populate_subdiv_ger_school_holidays (line 306).

-    def _populate_subdiv_vlg_school_holidays(self):
+    def _populate_subdiv_vlg_school_holidays(self) -> None:
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@holidays/countries/belgium.py` at line 129, The three private methods
_populate_subdiv_vlg_school_holidays, _populate_subdiv_wbr_school_holidays, and
_populate_subdiv_ger_school_holidays lack an explicit return type annotation;
add the explicit "-> None" return annotation to each def signature so they
comply with Ruff ANN202 (e.g., change "def
_populate_subdiv_vlg_school_holidays(self):" to "def
_populate_subdiv_vlg_school_holidays(self) -> None:" and do the same for the
other two methods).

388-389: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Christmas Eve overlaps the GER Christmas break.

Lines 320-337 already register the Christmas break, which spans the week of Dec 25 and in most years includes Dec 24. Re-adding tr("Heiligavond") on Dec 24 will collide with or shadow the break entry depending on insertion order. If Dec 24 is meant to be a distinct named day for GER, register it before the break and document the override; otherwise drop it.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@holidays/countries/belgium.py` around lines 388 - 389, The Christmas Eve
entry using self._add_holiday(tr("Heiligavond"), date(year, DEC, 24)) conflicts
with the previously added Christmas break (the block that registers the
week-long Dec 25 break); either remove the Heiligavond line to avoid shadowing,
or move it so it is added before the Christmas break with a comment documenting
that it intentionally overrides/precedence for Dec 24; update or add a comment
explaining the chosen behavior (distinct named day vs. part of the break) so
future changes know why the override/removal was made.

281-281: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Use _add_holiday_<mmm>_<dd> helpers and align _add_holiday arg order.

Three small things on these lines:

  • Prefer self._add_holiday_sep_27(tr(...)), self._add_holiday_nov_15(tr(...)), self._add_holiday_dec_24(tr(...)) over manual date(year, …) boilerplate.
  • The current self._add_holiday(tr("…"), date(year, …)) argument order is also reversed — _add_holiday takes (name, *args) where args build the date, but passing a pre-built date second works only by coincidence. The dedicated helpers avoid that pitfall.
-        self._add_holiday(tr("Feestdag van de Franse Gemeenschap"), date(year, SEP, 27))
+        self._add_holiday_sep_27(tr("Feestdag van de Franse Gemeenschap"))

Also applies to: 377-377, 389-389

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@holidays/countries/belgium.py` at line 281, Replace the manual date
construction and reversed-argument usage of _add_holiday by calling the
month/day helper methods (e.g. use self._add_holiday_sep_27(tr("Feestdag van de
Franse Gemeenschap")) instead of self._add_holiday(tr(...), date(year, SEP,
27))); do the same for the other occurrences (use self._add_holiday_nov_15 and
self._add_holiday_dec_24 where appropriate) and ensure you do not pass a
pre-built date object to _add_holiday (it expects name then args that build the
date).

262-262: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Replace raw timedelta(days=…) arithmetic with _timedelta and existing weekday helpers.

Project style is to use holidays.calendars.gregorian._timedelta rather than datetime.timedelta, and _get_nth_weekday_from / _get_nth_weekday_of_month rather than ad-hoc weekday() modular arithmetic. Lines 262, 269 and the autumn block at 298-300 still use the raw form.

-        if spring_start.day < 25:
-            spring_start += timedelta(days=7)
+        if spring_start.day < 25:
+            spring_start = _timedelta(spring_start, +7)
-        if summer_start.day == 1:
-            summer_start += timedelta(days=7)
+        if summer_start.day == 1:
+            summer_start = _timedelta(summer_start, +7)
-        nov_first = date(year, 11, 1)
-        last_monday = nov_first - timedelta(days=(nov_first.weekday() - MON) % 7)
-        autumn_start = last_monday - timedelta(days=7)
+        nov_first = date(year, NOV, 1)
+        autumn_start = _get_nth_weekday_from(-2, MON, nov_first)

Also applies to: 269-269, 299-300

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@holidays/countries/belgium.py` at line 262, Replace direct use of
datetime.timedelta and manual weekday arithmetic with the project's helper
utilities: use holidays.calendars.gregorian._timedelta instead of
timedelta(days=...) and replace ad-hoc weekday adjustments on
spring_start/spring_end and the autumn block with calls to _get_nth_weekday_from
or _get_nth_weekday_of_month as appropriate; specifically, find where
spring_start is incremented with timedelta(days=7) and where weekday() modular
math is used (around symbols spring_start, spring_end and the autumn start/end
calculations) and rewrite them to call _get_nth_weekday_from(...) or
_get_nth_weekday_of_month(...) to compute the correct date, using _timedelta
only where a timedelta object is required by those helpers.

185-195: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Public holidays should not be re-emitted inside SCHOOL population methods.

Labor Day, Ascension Day, Friday after Ascension, Whit Monday and Armistice Day are already populated by _populate_public_holidays. Re-adding them here means:

  • Requesting categories=SCHOOL only leaks PUBLIC dates into SCHOOL output.
  • Requesting categories=(PUBLIC, SCHOOL) produces duplicate-name conflicts on the same dates.
  • The duplicated set drifts inconsistently across VLG/WBR/GER (e.g., GER omits Armistice in some places, WBR omits Friday after Ascension, etc.).

If the intent is "school-closed days that aren't already public", drop these calls and only register break periods plus genuinely school-only/community-specific days (e.g., Sep 27 for WBR, Nov 15 for GER). Same applies to lines 274-290 and 370-389.

Also applies to: 214-215

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@holidays/countries/belgium.py` around lines 185 - 195, Remove any calls that
re-add public holidays inside the SCHOOL population method (e.g., calls to
_add_labor_day, _add_ascension_thursday, _add_holiday_40_days_past_easter,
_add_whit_monday and any _add_armistice_day invocations) so that public dates
remain the responsibility of _populate_public_holidays; instead only register
school-specific closures and break periods (school vacations and genuinely
school-only dates such as Sep 27 for WBR or Nov 15 for GER) inside the
school-population logic (the same change should be applied to the other
duplicated blocks noted around lines 214-215, 274-290 and 370-389). Ensure no
duplicate-name registrations occur by removing these public-holiday calls from
methods that populate SCHOOL category only.

13-13: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Drop datetime.timedelta import — project convention is _timedelta.

The WBR method (lines 262, 269, 299, 300) still uses timedelta(days=…) instead of _timedelta. Once those call sites are migrated, timedelta can come off the import line entirely. date is also re-exported from holidays.calendars.gregorian if you'd like to consolidate the imports.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@holidays/countries/belgium.py` at line 13, The import currently brings in
timedelta from datetime but project convention uses _timedelta and the WBR
method still calls timedelta(days=...) in several places; update all timedelta
usages inside the WBR method (the calls constructing offsets at the WBR logic)
to use _timedelta(days=...) instead, then remove timedelta from the import line
(keep date or switch to importing date from holidays.calendars.gregorian if you
prefer consolidation). Locate the WBR logic by the WBR method name and replace
each timedelta call with _timedelta, then remove the now-unused timedelta
import.

251-251: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

WBR Carnival break duration is hardcoded to 14 days regardless of year.

Per the 2022 FWB reform you flagged, Carnival was 1 week before the 2022-2023 school year and 2 weeks after — the Autumn block at lines 301-304 already gates on year >= 2022, so Carnival needs the same gate or pre-2022 years will report a 14-day Krokusvakantie that never existed.

-        self._add_multiday_holiday(_timedelta(easter, days=-48), 14, name=tr("Krokusvakantie"))
+        # Carnival Holidays: 1 week pre-2022 reform, 2 weeks afterwards.
+        carnival_duration = 14 if year >= 2022 else 7
+        self._add_multiday_holiday(
+            _timedelta(easter, -48), carnival_duration, name=tr("Krokusvakantie")
+        )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@holidays/countries/belgium.py` at line 251, The Krokusvakantie call currently
hardcodes 14 days; change it to use the year to avoid reporting a 14-day
Carnival pre-reform: compute a duration variable (e.g., duration = 14 if year >=
2022 else 7) and call self._add_multiday_holiday(_timedelta(easter, days=-48),
duration, name=tr("Krokusvakantie")); ensure you reference the existing symbol
self._add_multiday_holiday and the local year variable so older years get the
shorter break or are gated appropriately.

130-159: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Deduplicate the Christmas-break block across VLG/WBR/GER.

Lines 130-159, 228-245 and 320-337 are essentially identical. A small private helper (e.g., _add_school_christmas_break(self)) would keep each subdivision focused on what differs and avoid future drift if one community changes its rule.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@holidays/countries/belgium.py` around lines 130 - 159, Extract the duplicated
"Christmas Break" logic into a private helper like
_add_school_christmas_break(self, name=None) and call it from each subdivision
instead of duplicating lines; the helper should read self._year and
self._christmas_day, compute christmas_start using _get_nth_weekday_from(1 if
self._is_weekend(self._christmas_day) else -1, MON, self._christmas_day), call
self._add_multiday_holiday(christmas_start, 32 - christmas_start.day, name=name
or tr("Kerstvakantie")), then compute new_year = date(self._year, JAN, 1),
calculate january_duration = (4 - new_year.weekday()) % 7 + 3 and call
self._add_multiday_holiday(new_year, january_duration, name=name or
tr("Kerstvakantie")); replace the duplicated blocks in the VLG/WBR/GER classes
with calls to this helper.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@holidays/countries/belgium.py`:
- Around line 292-304: Inline the year-based branch and add the VLG-style rule
comment block for the Autumn Holidays: compute nov_first, last_monday and
autumn_start as currently done, then call self._add_multiday_holiday once with
duration = 14 if year >= 2022 else 7; keep the holiday name
tr("Herfstvakantie"). Also add the two-line comment pattern before the code: a
"# Autumn Holidays: <rule>" line describing the rule and a following "# Autumn
Holidays." line, matching the style used elsewhere in the file and using the
existing symbols nov_first, last_monday, autumn_start and the call to
_add_multiday_holiday.

---

Duplicate comments:
In `@holidays/countries/belgium.py`:
- Around line 223-224: The guard in the method that currently reads `if
self.categories != {SCHOOL}: return` wrongly excludes SCHOOL when combined with
other categories; change it to `if SCHOOL not in self.categories: return` (or
remove the guard entirely) so SCHOOL holidays are not skipped when callers
request multiple categories, and make the same change in the other occurrence
mentioned so the behavior matches `_populate_subdiv_vlg_school_holidays`.
- Line 129: The three private methods _populate_subdiv_vlg_school_holidays,
_populate_subdiv_wbr_school_holidays, and _populate_subdiv_ger_school_holidays
lack an explicit return type annotation; add the explicit "-> None" return
annotation to each def signature so they comply with Ruff ANN202 (e.g., change
"def _populate_subdiv_vlg_school_holidays(self):" to "def
_populate_subdiv_vlg_school_holidays(self) -> None:" and do the same for the
other two methods).
- Around line 388-389: The Christmas Eve entry using
self._add_holiday(tr("Heiligavond"), date(year, DEC, 24)) conflicts with the
previously added Christmas break (the block that registers the week-long Dec 25
break); either remove the Heiligavond line to avoid shadowing, or move it so it
is added before the Christmas break with a comment documenting that it
intentionally overrides/precedence for Dec 24; update or add a comment
explaining the chosen behavior (distinct named day vs. part of the break) so
future changes know why the override/removal was made.
- Line 281: Replace the manual date construction and reversed-argument usage of
_add_holiday by calling the month/day helper methods (e.g. use
self._add_holiday_sep_27(tr("Feestdag van de Franse Gemeenschap")) instead of
self._add_holiday(tr(...), date(year, SEP, 27))); do the same for the other
occurrences (use self._add_holiday_nov_15 and self._add_holiday_dec_24 where
appropriate) and ensure you do not pass a pre-built date object to _add_holiday
(it expects name then args that build the date).
- Line 262: Replace direct use of datetime.timedelta and manual weekday
arithmetic with the project's helper utilities: use
holidays.calendars.gregorian._timedelta instead of timedelta(days=...) and
replace ad-hoc weekday adjustments on spring_start/spring_end and the autumn
block with calls to _get_nth_weekday_from or _get_nth_weekday_of_month as
appropriate; specifically, find where spring_start is incremented with
timedelta(days=7) and where weekday() modular math is used (around symbols
spring_start, spring_end and the autumn start/end calculations) and rewrite them
to call _get_nth_weekday_from(...) or _get_nth_weekday_of_month(...) to compute
the correct date, using _timedelta only where a timedelta object is required by
those helpers.
- Around line 185-195: Remove any calls that re-add public holidays inside the
SCHOOL population method (e.g., calls to _add_labor_day,
_add_ascension_thursday, _add_holiday_40_days_past_easter, _add_whit_monday and
any _add_armistice_day invocations) so that public dates remain the
responsibility of _populate_public_holidays; instead only register
school-specific closures and break periods (school vacations and genuinely
school-only dates such as Sep 27 for WBR or Nov 15 for GER) inside the
school-population logic (the same change should be applied to the other
duplicated blocks noted around lines 214-215, 274-290 and 370-389). Ensure no
duplicate-name registrations occur by removing these public-holiday calls from
methods that populate SCHOOL category only.
- Line 13: The import currently brings in timedelta from datetime but project
convention uses _timedelta and the WBR method still calls timedelta(days=...) in
several places; update all timedelta usages inside the WBR method (the calls
constructing offsets at the WBR logic) to use _timedelta(days=...) instead, then
remove timedelta from the import line (keep date or switch to importing date
from holidays.calendars.gregorian if you prefer consolidation). Locate the WBR
logic by the WBR method name and replace each timedelta call with _timedelta,
then remove the now-unused timedelta import.
- Line 251: The Krokusvakantie call currently hardcodes 14 days; change it to
use the year to avoid reporting a 14-day Carnival pre-reform: compute a duration
variable (e.g., duration = 14 if year >= 2022 else 7) and call
self._add_multiday_holiday(_timedelta(easter, days=-48), duration,
name=tr("Krokusvakantie")); ensure you reference the existing symbol
self._add_multiday_holiday and the local year variable so older years get the
shorter break or are gated appropriately.
- Around line 130-159: Extract the duplicated "Christmas Break" logic into a
private helper like _add_school_christmas_break(self, name=None) and call it
from each subdivision instead of duplicating lines; the helper should read
self._year and self._christmas_day, compute christmas_start using
_get_nth_weekday_from(1 if self._is_weekend(self._christmas_day) else -1, MON,
self._christmas_day), call self._add_multiday_holiday(christmas_start, 32 -
christmas_start.day, name=name or tr("Kerstvakantie")), then compute new_year =
date(self._year, JAN, 1), calculate january_duration = (4 - new_year.weekday())
% 7 + 3 and call self._add_multiday_holiday(new_year, january_duration,
name=name or tr("Kerstvakantie")); replace the duplicated blocks in the
VLG/WBR/GER classes with calls to this helper.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 4a3858b5-988c-4bde-886d-2a4162941f63

📥 Commits

Reviewing files that changed from the base of the PR and between a72e716 and 8196320.

📒 Files selected for processing (1)
  • holidays/countries/belgium.py

Comment on lines +292 to +304
# Autumn Holidays:
# - Start → second last Monday of October.
# - Before the 2022-2023 school year → duration was 1 week.
# - From the 2022-2023 school year → duration is 2 weeks.

# Autumn Holidays.
nov_first = date(year, 11, 1)
last_monday = nov_first - timedelta(days=(nov_first.weekday() - MON) % 7)
autumn_start = last_monday - timedelta(days=7)
if year >= 2022:
self._add_multiday_holiday(autumn_start, 14, name=tr("Herfstvakantie"))
else:
self._add_multiday_holiday(autumn_start, 7, name=tr("Herfstvakantie"))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Inline the year branch and add a # Autumn Holidays. rule comment block — match the VLG style.

The rest of the file uses a # <Period>: <rule> description block followed by # <Period>. then the call. Here the rule comment is present but the calculation reads less linearly than VLG's autumn block. Consolidating the if/else into a single _add_multiday_holiday call also helps:

-        nov_first = date(year, 11, 1)
-        last_monday = nov_first - timedelta(days=(nov_first.weekday() - MON) % 7)
-        autumn_start = last_monday - timedelta(days=7)
-        if year >= 2022:
-            self._add_multiday_holiday(autumn_start, 14, name=tr("Herfstvakantie"))
-        else:
-            self._add_multiday_holiday(autumn_start, 7, name=tr("Herfstvakantie"))
+        autumn_start = _get_nth_weekday_from(-2, MON, date(year, NOV, 1))
+        # Autumn Holidays.
+        self._add_multiday_holiday(
+            autumn_start, 14 if year >= 2022 else 7, name=tr("Herfstvakantie")
+        )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@holidays/countries/belgium.py` around lines 292 - 304, Inline the year-based
branch and add the VLG-style rule comment block for the Autumn Holidays: compute
nov_first, last_monday and autumn_start as currently done, then call
self._add_multiday_holiday once with duration = 14 if year >= 2022 else 7; keep
the holiday name tr("Herfstvakantie"). Also add the two-line comment pattern
before the code: a "# Autumn Holidays: <rule>" line describing the rule and a
following "# Autumn Holidays." line, matching the style used elsewhere in the
file and using the existing symbols nov_first, last_monday, autumn_start and the
call to _add_multiday_holiday.

@Upanshi-Mittal

Copy link
Copy Markdown
Author

I noticed that after removing the category filter, some of the existing tests specific holidays are failing. It seems those tests rely on the holidays being restricted to Public -only categories.

Should we adjust the tests to account for mixed-category behavior, or is there a preferred way to handle Public-only holiday population in this case?

@KJhellico

Copy link
Copy Markdown
Collaborator

I don't review tests until the main code is finished or nearly finished.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants