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.

example/swiss_holidays_example.dart

// Run with: dart run example/swiss_holidays_example.dart
//
// NOT LEGAL ADVICE. Collective labour agreements (CCL/GAV/CCT) can add holidays
// and are out of scope. Verify against the cited sources before using this for
// payroll. See README.md and GAPS.md.

import 'package:swiss_holidays/swiss_holidays.dart';

void main() {
  print('Coverage: ${SwissHolidays.coveredCantons.toList()..sort()}');
  print('Communes: ${SwissHolidays.coveredCommunes}\n');

  _cantonsDiffer();
  _yearListing();
  _halfDays();
  _legalEffect();
  _workingDays();
  _citations();
  _historyMatters();
  _uncoveredScope();
}

/// The same date is a holiday in one canton and a working day in the other.
void _cantonsDiffer() {
  print('--- Same date, different answer ---');

  const dates = {
    'Good Friday 2026': (2026, 4, 3),
    'Corpus Christi 2026': (2026, 6, 4),
    'Epiphany 2026': (2026, 1, 6),
    'Christmas 2026': (2026, 12, 25),
  };

  for (final entry in dates.entries) {
    final (y, m, d) = entry.value;
    final date = DateTime(y, m, d);
    final zh = SwissHolidays.isHoliday(date, 'CH-ZH').status.name;
    final ti = SwissHolidays.isHoliday(date, 'CH-TI').status.name;
    print('  ${entry.key.padRight(22)} ZH=${zh.padRight(5)} TI=$ti');
  }
  print('');
}

void _yearListing() {
  print('--- Ticino 2026 (${SwissHolidays.holidaysIn(2026, 'CH-TI').length} '
      'official holidays) ---');
  for (final o in SwissHolidays.holidaysIn(2026, 'CH-TI')) {
    print('  ${_iso(o.date)}  ${_weekday(o.date)}  ${o.holiday.name('it')}');
  }
  print('');
}

/// Half days carry a clock time, not a boolean.
void _halfDays() {
  print('--- Stadt Zürich (commune 261) half days in 2026 ---');

  for (final o in SwissHolidays.holidaysIn(2026, 'CH-ZH', communeId: 261)) {
    if (o.status != HolidayStatus.half) continue;

    final observance = o.holiday.observance as HalfDay;
    print('  ${_iso(o.date)}  ${o.holiday.name('de')}');
    print('    free from ${o.startsAt} (time_basis: '
        '${observance.timeBasis.name})');
    if (observance.timeBasis == TimeBasis.derived) {
      print('    NOTE: the source says only "Nachmittag" — this time is '
          'derived, not quoted.');
    }
  }

  // These bind the City towards its own staff, not the general public.
  final generalOnly = SwissHolidays.holidaysIn(
    2026,
    'CH-ZH',
    communeId: 261,
    include: {AppliesTo.generalPublic},
  );
  print('  Filtering to generalPublic leaves ${generalOnly.length} entries '
      '(the cantonal nine).\n');
}

/// Being a holiday and attracting the Labour Act's Sunday rules differ.
void _legalEffect() {
  print('--- Legal effect: Ticino 2026 ---');

  final all = SwissHolidays.holidaysIn(2026, 'CH-TI');
  final sundayEquated =
      all.where((o) => o.legalEffect == LegalEffect.sundayEquivalent).toList();
  final restDaysOnly =
      all.where((o) => o.legalEffect == LegalEffect.cantonalRestDay).toList();

  print('  ${all.length} official holidays, of which:');
  print('    ${sundayEquated.length} Sunday-equated (Art. 6 lit. a LALL) '
      '- Art. 18-19 LL apply');
  print('    ${restDaysOnly.length} not Sunday-equated (Art. 6 lit. b LALL) '
      '- those rules do NOT apply');
  print('  Art. 20a LL caps a canton at nine, so nine is the ceiling.');

  print('  Not Sunday-equated:');
  for (final o in restDaysOnly) {
    print('    ${_iso(o.date)}  ${o.holiday.name('it')}');
  }

  // The same calendar date answers a payroll question and a booking question
  // differently.
  final corpusDomini = SwissHolidays.isHoliday(DateTime(2026, 6, 4), 'CH-TI');
  print('  Corpus Domini 2026: status=${corpusDomini.status.name}, '
      'isSundayEquivalent=${corpusDomini.isSundayEquivalent}');

  // 1 May is Sunday-equated in Zurich but not in Ticino.
  final mayFirst = DateTime(2026, 5, 1);
  print('  1 May 2026: ZH sundayEquivalent='
      '${SwissHolidays.isHoliday(mayFirst, 'CH-ZH').isSundayEquivalent}, '
      'TI sundayEquivalent='
      '${SwissHolidays.isHoliday(mayFirst, 'CH-TI').isSundayEquivalent}');
  print('');
}

void _workingDays() {
  print('--- Working-day arithmetic ---');

  final thu = DateTime(2026, 4, 2);
  print('  From ${_iso(thu)} (Thursday), +1 working day:');
  print('    CH-ZH -> ${_iso(SwissHolidays.addWorkingDays(thu, 1, 'CH-ZH'))} '
      '(Good Friday, weekend, Easter Monday all skipped)');
  print('    CH-TI -> ${_iso(SwissHolidays.addWorkingDays(thu, 1, 'CH-TI'))} '
      '(no Good Friday in Ticino)');

  final xmasEve = DateTime(2026, 12, 24);
  print('  nextWorkingDay after ${_iso(xmasEve)}:');
  print('    CH-ZH -> ${_iso(SwissHolidays.nextWorkingDay(xmasEve, 'CH-ZH'))}');

  print('  20 working days from 2026-01-05:');
  print('    CH-ZH -> '
      '${_iso(SwissHolidays.addWorkingDays(DateTime(2026, 1, 5), 20, 'CH-ZH'))}');
  print('    CH-TI -> '
      '${_iso(SwissHolidays.addWorkingDays(DateTime(2026, 1, 5), 20, 'CH-TI'))}');

  // Holidays on a weekend are not moved: Switzerland has no such rule.
  final aug1 = DateTime(2026, 8, 1);
  print('  1 August 2026 is a ${_weekday(aug1)} and is NOT shifted; '
      'Mon 3 Aug is a working day: '
      '${SwissHolidays.isWorkingDay(DateTime(2026, 8, 3), 'CH-ZH')}\n');
}

/// Every entry can say why it exists.
void _citations() {
  print('--- Provenance ---');

  final corpusDomini = SwissHolidays.isHoliday(DateTime(2026, 6, 4), 'CH-TI')
      .occurrences
      .single
      .holiday;

  print('  ${corpusDomini.name('it')} (${corpusDomini.id})');
  for (final b in corpusDomini.legalBasis) {
    print('    [${b.kind.name}] ${b.citation}');
    print('      ${b.url}');
    print('      checked ${_iso(b.checkedOn)}');
  }
  print('');
}

/// A query about a past year uses the rules in force then.
void _historyMatters() {
  print('--- Validity windows ---');

  for (final year in [2009, 2010, 2015, 2026]) {
    final ti = SwissHolidays.holidaysIn(year, 'CH-TI').length;
    final zh = SwissHolidays.holidaysIn(year, 'CH-ZH').length;
    print('  $year: CH-TI $ti entries, CH-ZH $zh entries');
  }
  print(
      '  Ticino RL 843.200 entered into force 2010-02-09; the 1934 decree it');
  print('  replaced is not sourced, so 2009 is empty rather than guessed.\n');
}

/// Uncovered scopes are empty, which callers must detect rather than trust.
void _uncoveredScope() {
  print('--- Uncovered cantons ---');
  const canton = 'CH-BE';
  if (!SwissHolidays.coveredCantons.contains(canton)) {
    print('  $canton is NOT covered. isHoliday would return '
        '"${SwissHolidays.isHoliday(DateTime(2026, 12, 25), canton).status.name}"'
        ' for Christmas —');
    print('  indistinguishable from a real answer. Always check '
        'coveredCantons first. See GAPS.md.');
  }
}

String _iso(DateTime d) => '${d.year.toString().padLeft(4, '0')}-'
    '${d.month.toString().padLeft(2, '0')}-'
    '${d.day.toString().padLeft(2, '0')}';

const _weekdayNames = [
  'Monday',
  'Tuesday',
  'Wednesday',
  'Thursday',
  'Friday',
  'Saturday',
  'Sunday',
];

String _weekday(DateTime d) => _weekdayNames[d.weekday - 1];
1
likes
160
points
6
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