swiss_holidays 0.1.0 copy "swiss_holidays: ^0.1.0" to clipboard
swiss_holidays: ^0.1.0 copied to clipboard

Swiss public holidays for Dart — cantonal and communal, with a cited legal source for every entry, half-day start times, and working-day arithmetic.

Swiss public holidays for Dart — cantonal and communal, with legal sources #

Swiss public holidays are set by cantonal law, and sometimes at commune level. There is no single national list. Good Friday is a holiday in Zürich and not in Ticino; Corpus Christi is the reverse. Zürich city staff get the afternoon of Sechseläuten off, on a date that is not the third Monday in April in four of the next ten years.

This package encodes that, and — the point of it — every single entry carries a citation to an official source with a URL that was actually fetched.

import 'package:swiss_holidays/swiss_holidays.dart';

// Corpus Domini: a holiday in Ticino, an ordinary working day in Zürich.
SwissHolidays.isHoliday(DateTime(2026, 6, 4), 'CH-TI').status; // full
SwissHolidays.isHoliday(DateTime(2026, 6, 4), 'CH-ZH').status; // none

// Half days carry the hour, because a time-tracking system needs it.
final s = SwissHolidays.isHoliday(DateTime(2026, 4, 20), 'CH-ZH', communeId: 261);
s.status;    // half
s.startsAt;  // "12:00"

// Why is it a holiday? Ask.
s.occurrences.single.holiday.legalBasis.first.citation;
// Stadt Zürich, Ausführungsbestimmungen zum Personalrecht (AB PR),
// AS 177.101, Art. 169 Abs. 1; Stadtratsbeschluss vom 27. März 2002 (447) …

This package is a convenience, not a legal authority.

  • Collective labour agreements (CCL / GAV / CCT) can add holidays and are entirely out of scope. So can individual contracts and company rules. A payroll system must layer those on top of this data.
  • Verify against the cited sources before using this for payroll, invoicing deadlines, or anything else with money or legal consequences attached. Every entry exposes its legalBasis with a URL and a checkedOn date precisely so you can.
  • 24 of 26 cantons are not covered. An uncovered canton returns "not a holiday" for every date — indistinguishable from a real answer unless you check SwissHolidays.coveredCantons first. See GAPS.md.
  • "Is a holiday" is not the same as "the Labour Act's Sunday rules apply." Six of Ticino's fifteen official holidays are holidays without being Sunday-equated. Read legalEffect — see Legal effect below.
  • The half-day start times are derived, not quoted from the source. See GAPS.md §5.

Cantonal law changes. The checkedOn date on each citation tells you when it was last verified.

Install #

dependencies:
  swiss_holidays: ^0.1.0

The package has no runtime dependencies. The dataset is compiled from YAML into Dart source at build time, so there is no asset loading and no YAML parser at runtime — it works in pure Dart, Flutter, and on the web.

Usage #

Is a given date a holiday? #

isHoliday returns a HolidayLookup whose status is an enum — none, full or half — never a bool, because "half day" is a real and common case that a boolean silently destroys. The lookup also carries the half-day start time, which an enum alone cannot.

final lookup = SwissHolidays.isHoliday(DateTime(2026, 12, 25), 'CH-ZH');

switch (lookup.status) {
  case HolidayStatus.none: print('working day');
  case HolidayStatus.full: print('full holiday');
  case HolidayStatus.half: print('free from ${lookup.startsAt}');
}

All holidays in a year #

for (final o in SwissHolidays.holidaysIn(2026, 'CH-TI')) {
  print('${o.date} ${o.holiday.name('it')} ${o.status}');
}
// 2026-01-01 Capodanno full
// 2026-01-06 Epifania full
// 2026-03-19 San Giuseppe full
// … 15 entries

Names are available in de, fr, it and en via holiday.name(langCode), falling back to English.

Being called a public holiday and having legal consequences under the federal Labour Act are two different things, decided by two different statutes. Ticino lists fifteen official holidays in one law (RL 843.200) and then splits them in another (Art. 6 LALL, RL 843.100): nine are parificati alle domeniche, six are not. On the six, the Sunday-work provisions of Art. 18–19 LL do not apply and the day counts as a working day for labour-law purposes.

There is a hard ceiling that makes this checkable: Art. 20a Abs. 2 ArG lets a canton equate at most eight further days per year to Sunday beyond 1 August. No canton can have more than nine. Both cantons here sit exactly at that ceiling, and a test asserts it.

final corpusDomini = SwissHolidays.isHoliday(DateTime(2026, 6, 4), 'CH-TI');
corpusDomini.status;             // full  — it is a holiday
corpusDomini.isSundayEquivalent; // false — but Art. 18-19 LL do not apply
corpusDomini.legalEffects;       // {LegalEffect.cantonalRestDay}

Zürich is structured differently and was verified separately: § 1 Abs. 3 RLG equates all nine of its rest days in a single sentence, so every Zürich entry is sundayEquivalent. Do not infer one canton's structure from another's — 1 May and Whit Monday are Sunday-equated in Zürich and not in Ticino.

Filter to the subset you need:

// Payroll: only days governed by the Labour Act's Sunday rules.
SwissHolidays.holidaysIn(2026, 'CH-TI',
    effects: {LegalEffect.sundayEquivalent});           // 9 entries

// Booking or opening hours: every official holiday.
SwissHolidays.holidaysIn(2026, 'CH-TI');                // 15 entries

The three values are sundayEquivalent, cantonalRestDay and personnelLawRestDay (work-free under a public administration's own personnel law, binding that employer only). Every entry carries one, sourced — often from a different statute than the one that lists the holiday.

Working-day arithmetic #

// Thu 2 April 2026 + 1 working day.
// Zürich: Good Friday, weekend, Easter Monday → Tue 7 April.
SwissHolidays.addWorkingDays(DateTime(2026, 4, 2), 1, 'CH-ZH'); // 2026-04-07
// Ticino: no Good Friday → Fri 3 April.
SwissHolidays.addWorkingDays(DateTime(2026, 4, 2), 1, 'CH-TI'); // 2026-04-03

SwissHolidays.nextWorkingDay(DateTime(2026, 12, 24), 'CH-ZH');    // 2026-12-28
SwissHolidays.addWorkingDays(DateTime(2026, 6, 17), -5, 'CH-ZH'); // backwards

By default Saturday and Sunday are non-working, full holidays are non-working, and half days count as working days (work is performed for part of them). Override with WorkingDayPolicy:

const policy = WorkingDayPolicy(
  halfDaysAreWorkingDays: false,
  weekendWeekdays: {DateTime.sunday},          // six-day week
  include: {AppliesTo.generalPublic},          // ignore staff-only entries
  effects: {LegalEffect.sundayEquivalent},     // only Labour-Act Sunday days
);

Communes #

Communes are identified by official FSO/BFS commune number, never by name — communes merge and names change, numbers are stable and official.

// 261 = Zürich. Verified against the BFS Amtliches Gemeindeverzeichnis.
SwissHolidays.holidaysIn(2026, 'CH-ZH', communeId: 261);

Passing a commune with no sourced data falls back to the cantonal set; it never inherits another commune's entries.

Check coverage before you trust a result #

if (!SwissHolidays.coveredCantons.contains(canton)) {
  throw StateError('$canton is not covered — see GAPS.md');
}

Reading the citations #

for (final b in holiday.legalBasis) {
  print('${b.citation}\n  ${b.url}\n  checked ${b.checkedOn} (${b.kind})');
  if (b.quote != null) print('  "${b.quote}"');
}

Coverage #

Honest status. "Sourced" means every entry has a citation to an official source that was fetched, with the URL recorded.

Scope Entries In force from Status
CH-ZH (canton Zürich) 9 full days, all Sunday-equated 2000-12-01 Sourced — § 1 Abs. 1 lit. b and Abs. 3 RLG (LS 822.4)
CH-TI (canton Ticino) 15 full days: 9 Sunday-equated, 6 not 2010-02-09 Sourced — Art. 1 RL 843.200 and Art. 6 LALL (RL 843.100)
Commune 261 (Stadt Zürich) 1 full + 3 half days, personnel-law only 2002-07-01 Sourced, staff-scoped — Art. 169 AB PR (AS 177.101). Half-day times derived, not quoted
Other 24 cantons Not yet sourced
All other communes Not yet sourced

Sechseläuten dates cover 2024–2035 and Knabenschiessen 2026–2035, bounded by the published tables. Outside those ranges the engine returns nothing rather than extrapolating.

Full detail, including every known hole and every deliberate modelling decision, is in GAPS.md. It is worth reading before you depend on this.

Sources actually fetched #

Source Used for
Kanton Zürich, RLG (LS 822.4), § 1 The nine Zürich rest days
Kanton Zürich, AWA, «Feiertage» Corroboration; 2025 & 2026 golden dates
Cantone Ticino, Legge RL 843.200, Art. 1 The fifteen Ticino holidays
Cantone Ticino, LALL (RL 843.100), Art. 6 Which nine Ticino days are Sunday-equated
Cantone Ticino, Gran Consiglio, IE 547 rapporto di minoranza Independent confirmation of the nine/six split and the Art. 20a ceiling
Cantone Ticino, UIL, «Giorni festivi in Ticino» Corroboration; 2026 & 2027 golden dates
Stadt Zürich, AB PR (AS 177.101), Art. 169 The four extra city days
Stadt Zürich, «Feiertage und Betriebsferientage» Corroboration of the half days
ZZZ, «Sechseläuten-Daten 2024–2035» Sechseläuten dates
Schützengesellschaft der Stadt Zürich, «Zukünftige Festdaten» Knabenschiessen dates
BAK, Lebendige Traditionen, «Knabenschiessen» Evidence the date rule is not computable
BFS, Amtliches Gemeindeverzeichnis (snapshot API) Commune number 261

Wikipedia is a fine place to learn what to look for. It is never a citation here, and a test enforces that.

Architecture #

data/
  schema/holiday.schema.json   JSON Schema (draft 2020-12) — the contract
  cantons/ch-zh.yaml           sourced cantonal definitions
  cantons/ch-ti.yaml
  communes/ch-zh-261.yaml      sourced communal definitions
lib/
  src/easter.dart              Meeus/Jones/Butcher. Pure.
  src/rules.dart               rule → date. Pure, no I/O.
  src/models.dart              value types
  src/query.dart               lookup + working-day arithmetic
  src/generated/dataset.g.dart GENERATED — do not edit
  swiss_holidays.dart          public API
tool/
  build_dataset.dart           YAML → validated Dart asset

Data is separate from code. The build step validates every YAML file against the schema and refuses to emit anything if validation fails:

dart run tool/build_dataset.dart            # regenerate
dart run tool/build_dataset.dart --check    # CI: fail if stale

Rule types #

Type Example
fixed 1 August; 8 December
easter_relative Good Friday −2, Easter Monday +1, Ascension +39, Whit Monday +50, Corpus Christi +60
nth_weekday third Monday in April (nth: -1 for last)
explicit_dates Sechseläuten, Knabenschiessen — see below

explicit_dates exists because of a real finding: Sechseläuten is not the third Monday in April. The Zentralkomitee der Zünfte Zürich's own published table puts it on the fourth Monday in 2025, 2028, 2031 and 2033, and the second Monday (8 April) in 2030. Any nth_weekday encoding would produce wrong dates. The federal BAK dossier likewise describes Knabenschiessen as "am zweiten Wochenende im September oder eine Woche vor dem Eidgenössischen Bettag" — a disjunction, not a calendar rule. So both are encoded as the official date tables they are, and the engine returns nothing for unlisted years instead of extrapolating.

Tests #

dart test    # 138 tests
  • Easter — verified against an independently-structured second algorithm (Gauss) over all 717 years from 1583 to 2299, plus the published invariants (always a Sunday; always 22 March – 25 April, both extremes exercised), plus the Easter-derived dates published by the Ticino labour inspectorate for 2026 and 2027. (A published multi-decade table was the first choice; the USNO page turned out to publish the algorithm and the bounds but no table, so it is not cited as one. The reasoning is recorded at the top of test/easter_test.dart.)
  • Golden tests per canton per year — CH-ZH 2025 & 2026 and CH-TI 2026 & 2027, asserted against the official published calendars, with the source URL in the test file header.
  • Round-trip — every YAML entry validates against the schema, every entry survives into the generated dataset, and the generated file is checked to be up to date. Plus ~20 malformed fixtures asserted to fail, so the round-trip test cannot pass vacuously.
  • Provenance — every entry has a citation with an https URL and a checkedOn date; an allow-list of official hosts; a ban-list including Wikipedia and the commercial calendar sites.
  • Edge cases — half days, holidays on weekends (and not shifted to Monday, because Switzerland has no such rule), valid_from/valid_to boundaries at exactly ±1 day, 29 February, non-existent 5th weekdays, fixed-date overflow, time-zone independence.

Contributing a canton #

Short version: a data contribution is only accepted with a citation and a URL. Fetch the cantonal act, quote the article, record the URL and the date you checked it. If you cannot verify something, add it to GAPS.md instead.

Full instructions in CONTRIBUTING.md.

Prior art #

Nothing on pub.dev covers Switzerland (checked 2026-07-29). The Python vacanza/holidays package covers all 26 cantons with HALF_DAY/OPTIONAL categories and Stadt Zürich, and is the more complete dataset today. This package differs in being Dart-native, in carrying a cited legal source per entry, in modelling commune scope by BFS number, in giving half days an explicit clock time with recorded provenance, and in shipping working-day arithmetic. If you need breadth today, use the Python package. If you need Dart and auditability, use this.

Maintained by #

Built and maintained by The Hexagon Swiss Saglhttps://hexagonswiss.com.

Corrections to the data are especially welcome: if an entry is wrong, unsourced, or its link has rotted, open an issue. Removing bad data counts as a contribution. See CONTRIBUTING.md.

Licence #

MIT, Copyright (c) 2026 The Hexagon Swiss Sagl — see LICENSE.

The holiday data is compiled from official cantonal and communal legal sources, each cited in the dataset. Legal texts published by Swiss public authorities are excluded from copyright protection under Art. 5 of the Swiss Copyright Act (URG). See NOTICE.md.

1
likes
160
points
7
downloads

Documentation

API reference

Publisher

verified publisherhexagonswiss.com

Weekly Downloads

Swiss public holidays for Dart — cantonal and communal, with a cited legal source for every entry, half-day start times, and working-day arithmetic.

Homepage
Repository (GitHub)
View/report issues
Contributing

Topics

#switzerland #holidays #calendar #date

License

MIT (license)

More

Packages that depend on swiss_holidays