Skip to content

Update Sweden holidays: add legally-recognized non-public holidays as DE_FACTO category#3138

Merged
arkid15r merged 5 commits intovacanza:devfrom
vruss:fix/sweden-de-facto-holidays-3118
Dec 18, 2025
Merged

Update Sweden holidays: add legally-recognized non-public holidays as DE_FACTO category#3138
arkid15r merged 5 commits intovacanza:devfrom
vruss:fix/sweden-de-facto-holidays-3118

Conversation

@vruss
Copy link
Copy Markdown
Contributor

@vruss vruss commented Dec 16, 2025

Proposed change

This PR introduces a new DE_FACTO holiday category to address issue #3118 where is_working_day() incorrectly identified certain Swedish holidays as working days.

According to Swedish Annual Leave Law (SFS 1977:480), Midsummer's Eve, Christmas Eve, and New Year's Eve must be treated equivalently to Sundays and public holidays for working day calculations, even though they are not official public holidays.

Changes

  • New category: Added DE_FACTO constant in holidays/constants.py for holidays that are legally treated like public holidays but are not officially designated as such
  • Sweden implementation:
    • Moved Midsummer's Eve (Midsommarafton), Christmas Eve (Julafton), and New Year's Eve (Nyårsafton) from BANK and OPTIONAL categories to the new DE_FACTO category
    • Added _populate_de_facto_holidays() method to handle these special holidays
    • Updated supported categories to include DE_FACTO
  • Documentation: Added comprehensive documentation in docs/holiday_categories.md explaining the new category with usage examples
  • Localization: Updated all Swedish locale files (en_US, sv, th, uk)
  • Tests: Added comprehensive test coverage including specific is_working_day() validation
  • README: Updated to reflect the new category availability for Sweden

Breaking Change

This is a MINOR breaking change for Sweden holidays with the OPTIONAL category.
Previous behavior:

  • Midsummer's Eve, Christmas Eve, and New Year's Eve were included in both BANK and OPTIONAL categories
    New behavior:
  • These three holidays have been moved to the new DE_FACTO category
  • They are still included in BANK category (via _populate_de_facto_holidays() call)
  • They are NO LONGER included in OPTIONAL category
    Impact:
  • ✅ Users relying on categories=BANK will continue to see these three holidays (no breaking change)
  • ⚠️ Users relying on categories=OPTIONAL will no longer see these three holidays (breaking change)
  • Users need to explicitly include DE_FACTO category for accurate working day calculations: Sweden(categories=(PUBLIC, DE_FACTO))

Rationale:
These holidays are not optional holidays—they are legally mandated to be treated like public holidays for working day calculations per Swedish Annual Leave Law (SFS 1977:480). The Riksbank (Swedish central bank) also recognizes them as bank holidays.

Usage

For accurate working day calculations in Sweden, use:

import holidays
from holidays.constants import PUBLIC, DE_FACTO

se = holidays.Sweden(categories=(PUBLIC, DE_FACTO), years=2024)
print(se.is_working_day('2024-12-24'))  # False (Christmas Eve)
print(se.is_working_day('2024-12-31'))  # False (New Year's Eve)
print(se.is_working_day('2024-06-21'))  # False (Midsummer's Eve)

Legal Reference

Per Swedish Annual Leave Law (SFS 1977:480, Section 7):

"Med söndag jämställs allmän helgdag samt midsommarafton, julafton och nyårsafton"

(Sundays are equivalent to public holidays as well as Midsummer's Eve, Christmas Eve, and New Year's Eve)

Source: https://www.riksdagen.se/sv/dokument-och-lagar/dokument/svensk-forfattningssamling/semesterlag-1977480_sfs-1977-480/

Fixes #3118

Type of change

  • New country/market holidays support (thank you!)
  • Supported country/market holidays update (calendar discrepancy fix, localization)
  • Existing code/documentation/test/process quality improvement (best practice, cleanup, refactoring, optimization)
  • Dependency update (version deprecation/pin/upgrade)
  • Bugfix (non-breaking change which fixes an issue)
  • Breaking change (a code change causing existing functionality to break)
  • New feature (new holidays functionality in general)

Checklist

Copilot AI review requested due to automatic review settings December 16, 2025 00:19
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Dec 16, 2025

Summary by CodeRabbit

  • New Features

    • Added a DE_FACTO holiday category; Sweden now classifies Midsummer Eve, Christmas Eve, and New Year's Eve as de facto holidays.
  • Documentation

    • Guidance updated to include DE_FACTO and how to treat de facto days like public holidays for some calculations.
  • Tests

    • Tests updated and new coverage added to validate DE_FACTO behavior for Sweden.
  • Localization

    • Added and consolidated translations for the three de facto holiday names across multiple locales.
  • Contributors

    • Added a new contributor entry.

✏️ Tip: You can customize this high-level summary in your review settings.

Walkthrough

Adds a new DE_FACTO holiday category and constant; updates Sweden to expose and populate de-facto holidays (Midsommarafton, Julafton, Nyårsafton); updates docs, tests, and localized message catalogs to reflect the new category and translations.

Changes

Cohort / File(s) Summary
Core constant
holidays/constants.py
Added DE_FACTO = "de_facto" constant.
Sweden country logic
holidays/countries/sweden.py
Added DE_FACTO to supported_categories; introduced _populate_de_facto_holidays to add Midsommarafton, Julafton, Nyårsafton; removed those from _populate_common; _populate_bank_holidays now calls the new method.
Documentation & README
README.md, docs/holiday_categories.md
Documented new DE_FACTO category, added Sweden example, usage snippet, and guidance clarifying DE_FACTO handling.
Localization / message catalogs
holidays/locale/.../LC_MESSAGES/SE.po
holidays/locale/en_US/LC_MESSAGES/SE.po, holidays/locale/sv/LC_MESSAGES/SE.po, holidays/locale/th/LC_MESSAGES/SE.po, holidays/locale/uk/LC_MESSAGES/SE.po
Bumped Project-Id-Version to 0.88; added translations for Midsommarafton, Julafton, Nyårsafton; removed duplicate entries and adjusted ordering.
Tests
tests/countries/test_sweden.py
Imported DE_FACTO; updated Sweden test setup to include DE_FACTO; changed assertions for Midsommarafton/Julafton/Nyårsafton to DE_FACTO and added test_de_facto_2022.
Contributors
CONTRIBUTORS
Added contributor "Viktor Rosvall".

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Review focus: holidays/countries/sweden.py for correct category registration and interactions with bank/public holiday population and is_working_day semantics.
  • Verify tests in tests/countries/test_sweden.py (edge dates/year boundaries) and that category filtering behaves as intended.
  • Check .po files for unintended removal of msgstr entries or encoding/header regressions.

Possibly related PRs

Suggested reviewers

  • arkid15r
  • KJhellico

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: introducing a DE_FACTO category for Swedish holidays that are legally treated like public holidays.
Description check ✅ Passed The description comprehensively explains the change, its rationale, legal basis, breaking changes, and usage examples, all directly related to the changeset.
Linked Issues check ✅ Passed The PR fully addresses #3118: it enables correct is_working_day() behavior for Sweden by introducing DE_FACTO category for the three legally-mandated eves, allowing accurate working day calculations.
Out of Scope Changes check ✅ Passed All changes are scope-aligned: new DE_FACTO constant, Sweden category updates, documentation, localization, tests, README updates, and contributor addition all directly support the objective of fixing is_working_day() for Swedish holidays.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 06aaf46 and 88f9fce.

📒 Files selected for processing (1)
  • CONTRIBUTORS (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: PPsyrius
Repo: vacanza/holidays PR: 2881
File: tests/countries/test_french_polynesia.py:19-22
Timestamp: 2025-11-08T05:09:56.159Z
Learning: In the vacanza/holidays project's test framework (PR #2881 onwards), the base class CommonCountryTests provides a default test_no_holidays implementation that automatically tests years=start_year - 1 for standard PUBLIC category cases. Individual country test files should only override test_no_holidays when the country supports additional non-PUBLIC categories (GOVERNMENT, WORKDAY, OPTIONAL, etc.) with different start years requiring separate validation.
Learnt from: PPsyrius
Repo: vacanza/holidays PR: 2881
File: tests/countries/test_brazil.py:28-30
Timestamp: 2025-09-10T14:35:54.603Z
Learning: In the holidays project, the test_no_holidays method should test ALL supported_categories (via categories=CountryClass.supported_categories) rather than just the default PUBLIC category, to ensure comprehensive validation that no holidays exist before start_year across any supported category including OPTIONAL and subdivision categories.
Learnt from: KJhellico
Repo: vacanza/holidays PR: 2783
File: tests/countries/test_eritrea.py:130-147
Timestamp: 2025-08-18T13:06:16.919Z
Learning: The `assertLocalizedHolidays` method in the vacanza/holidays project requires a complete list of all holidays from all categories (PUBLIC, GOVERNMENT, etc.), not just the holidays from the default category. This is a framework requirement for comprehensive localization testing.
Learnt from: PPsyrius
Repo: vacanza/holidays PR: 2881
File: tests/countries/test_curacao.py:29-29
Timestamp: 2025-09-20T12:24:28.864Z
Learning: For Curacao in the holidays library, PUBLIC and HALF_DAY categories have different start years - PUBLIC holidays begin at the country's start_year while HALF_DAY holidays begin in 2010. When testing no_holidays methods, each category should be tested separately with appropriate pre-start years rather than using a unified supported_categories approach.
Learnt from: PPsyrius
Repo: vacanza/holidays PR: 2881
File: holidays/countries/south_africa.py:69-75
Timestamp: 2025-09-14T07:26:25.431Z
Learning: When reviewing historical holiday implementations in the vacanza/holidays repository, trust the maintainers' research and implementation decisions for specific historical edge cases, especially when they can provide sources like Wikipedia or other historical documentation that supports unusual or complex date calculation rules during specific time periods.
Learnt from: PPsyrius
Repo: vacanza/holidays PR: 2537
File: tests/countries/test_finland.py:23-26
Timestamp: 2025-05-09T18:36:09.607Z
Learning: The holidays project prioritizes complete historical coverage in tests, verifying holidays from their first year of observance (e.g., 1853 for Finland) through future projections, rather than using shorter sliding windows.
Learnt from: KJhellico
Repo: vacanza/holidays PR: 2854
File: tests/countries/test_sudan.py:29-31
Timestamp: 2025-09-12T21:37:10.710Z
Learning: For countries in the holidays library that only use the default PUBLIC category (like Sudan), there's no need to specify `categories=Sudan.supported_categories` in test_no_holidays methods, as it would be equivalent to the default behavior.
Learnt from: KJhellico
Repo: vacanza/holidays PR: 2854
File: tests/countries/test_sudan.py:29-31
Timestamp: 2025-09-12T21:37:10.710Z
Learning: For countries in the holidays library that only use the default PUBLIC category (like Sudan), there's no need to specify `categories=Sudan.supported_categories` in test_no_holidays methods, as it would be equivalent to the default behavior.
Learnt from: KJhellico
Repo: vacanza/holidays PR: 2854
File: tests/countries/test_sudan.py:29-31
Timestamp: 2025-09-12T21:37:10.710Z
Learning: For countries in the holidays library that only use the default PUBLIC category (like Sudan), there's no need to specify `categories=Sudan.supported_categories` in test_no_holidays methods, as it would be equivalent to the default behavior.
Learnt from: KJhellico
Repo: vacanza/holidays PR: 2851
File: docs/holiday_categories.md:272-282
Timestamp: 2025-08-26T14:43:53.605Z
Learning: In the holidays library documentation, it's strongly advisable to recommend the use of constants from holidays.constants (e.g., PUBLIC, CATHOLIC) instead of direct string values when specifying holiday categories, as constants provide better type safety, IDE support, and prevent typos.
Learnt from: PPsyrius
Repo: vacanza/holidays PR: 2881
File: tests/countries/test_antigua_and_barbuda.py:27-29
Timestamp: 2025-09-14T16:19:23.651Z
Learning: For Antigua and Barbuda in the holidays library, only the default PUBLIC category is used, so there's no need to specify `categories=AntiguaAndBarbuda.supported_categories` in test_no_holidays methods, as it would be equivalent to the default behavior and AntiguaAndBarbuda doesn't define supported_categories.
Learnt from: PPsyrius
Repo: vacanza/holidays PR: 2881
File: tests/countries/test_antigua_and_barbuda.py:27-29
Timestamp: 2025-09-14T16:19:23.651Z
Learning: For Antigua and Barbuda in the holidays library, only the default PUBLIC category is used, so there's no need to specify `categories=AntiguaAndBarbuda.supported_categories` in test_no_holidays methods, as it would be equivalent to the default behavior and AntiguaAndBarbuda doesn't define supported_categories.
🔇 Additional comments (1)
CONTRIBUTORS (1)

168-168: LGTM!

Contributor correctly positioned in alphabetical order.


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.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces a new DE_FACTO holiday category to fix incorrect working day calculations for Sweden, where certain holidays (Midsummer's Eve, Christmas Eve, and New Year's Eve) are legally required to be treated as non-working days per Swedish Annual Leave Law (SFS 1977:480), despite not being official public holidays.

Key changes:

  • Added new DE_FACTO constant and category with legal backing for holidays treated equivalently to public holidays
  • Migrated three Swedish holidays from BANK and OPTIONAL categories to the new DE_FACTO category
  • Updated documentation and locale files to support the new category

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated no comments.

Show a summary per file
File Description
holidays/constants.py Added DE_FACTO constant definition
holidays/countries/sweden.py Added _populate_de_facto_holidays() method and moved three holidays to new category
tests/countries/test_sweden.py Added comprehensive test coverage for DE_FACTO category and is_working_day() validation
docs/holiday_categories.md Added documentation explaining the new DE_FACTO category with usage examples
holidays/locale/en_US/LC_MESSAGES/SE.po Reorganized translations for the three migrated holidays
holidays/locale/sv/LC_MESSAGES/SE.po Reorganized translations for the three migrated holidays
holidays/locale/th/LC_MESSAGES/SE.po Reorganized translations for the three migrated holidays
holidays/locale/uk/LC_MESSAGES/SE.po Reorganized translations for the three migrated holidays
README.md Updated Sweden's supported categories to include DE_FACTO

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

@codecov
Copy link
Copy Markdown

codecov bot commented Dec 16, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (44238ca) to head (88f9fce).
⚠️ Report is 16 commits behind head on dev.

Additional details and impacted files
@@            Coverage Diff            @@
##               dev     #3138   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files          306       306           
  Lines        18167     18248   +81     
  Branches      2301      2327   +26     
=========================================
+ Hits         18167     18248   +81     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@KJhellico KJhellico changed the title Add DE_FACTO holiday category for Sweden's legally-recognized non-pub… Update Sweden holidays: add legally-recognized non-public holidays as DE_FACTO category Dec 16, 2025
- Include de facto holidays in bank holidays via _populate_de_facto_holidays()
- Update tests to reflect de facto holidays in BANK category
- Simplify comment in _populate_de_facto_holidays()
@vruss
Copy link
Copy Markdown
Contributor Author

vruss commented Dec 17, 2025

Made sure to populate the bank days with defacto too. Optional ones are not being populated with DE_FACTO yet. If we also were to populate optional then I would question why DE_FACTO is even necessary in the first place since they would always be added.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (4)
holidays/locale/sv/LC_MESSAGES/SE.po (1)

99-99: Duplicate: Comment formatting issue.

Same issue as en_US file. Will be automatically fixed when make l10n is run after correcting the en_US file.

holidays/locale/th/LC_MESSAGES/SE.po (1)

99-99: Duplicate: Comment formatting issue.

Same issue as en_US file. Will be automatically fixed when make l10n is run after correcting the en_US file.

tests/countries/test_sweden.py (1)

415-422: Remove test method docstring and reorder.

Test methods in this project typically don't have docstrings. Also, consider moving this method after test_bank_2022 to group it with related category tests.

Apply this diff:

-    def test_de_facto_2022(self):
-        """Test all DE_FACTO holidays for 2022."""
+    def test_de_facto_2022(self):
         self.assertHolidays(

And consider moving the entire method to line 402 (right after test_bank_2022).

holidays/countries/sweden.py (1)

151-159: Fix comment formatting.

Line 152 is missing a period, inconsistent with other comments in the file.

Apply this diff:

     def _populate_de_facto_holidays(self):
-        # Midsummer Eve
+        # Midsummer Eve.
         self._add_holiday_1st_fri_from_jun_19(tr("Midsommarafton"))

After making this change, run make l10n to update all .po files with the corrected comment.

@sonarqubecloud
Copy link
Copy Markdown

PPsyrius
PPsyrius previously approved these changes Dec 18, 2025
Copy link
Copy Markdown
Collaborator

@PPsyrius PPsyrius left a comment

Choose a reason for hiding this comment

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

LGTM 🇸🇪

Feel free to add your own name to CONTRIBUTORS as well 🎉

Signed-off-by: Viktor Rosvall <[email protected]>
Copy link
Copy Markdown
Collaborator

@KJhellico KJhellico left a comment

Choose a reason for hiding this comment

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

LGTM.

@arkid15r arkid15r added this pull request to the merge queue Dec 18, 2025
Copy link
Copy Markdown
Collaborator

@arkid15r arkid15r left a comment

Choose a reason for hiding this comment

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

@vruss thanks for taking care of this 👍

Merged via the queue into vacanza:dev with commit 933ef6a Dec 18, 2025
31 checks passed
@vruss vruss deleted the fix/sweden-de-facto-holidays-3118 branch December 19, 2025 00:40
@arkid15r arkid15r mentioned this pull request Jan 5, 2026
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.

Incorrect working days for Sweden

5 participants