Time and IDs

Public holidays

Public holidays of 27 countries for any year from 1970 to 2099, computed rather than listed: Easter and its feasts, "the last Monday of May", Japan's equinoxes, with the day each is taken when it falls on a weekend.

Version 1.0.0 · 5 functions · 2 crates

#dates#calendar#delivery#locale

Functions

What use holidays.qwer gives a program, with the types Quillo sees. To take only some, or under other names: use holidays_list, holidays_on from holidays (Quillo 0.6).

holidays_list(country: str, year: int) → listor an error

The public holidays of country ("US", "GB", "DE", "JP", "PH"...) in year: a list of { date, name, weekday, observed }, in date order. observed is the day off when the holiday is moved for falling on a weekend (US Saturday holidays on the Friday, UK and Australian ones on the next Monday...), else date. National holidays only: not regional ones, nor ones fixed each year by decree.

holidays_on(country: str, date: str) → str?or an error

The holiday on date in country, as its name, counting the day it is observed on; null when it is a working day. For "closed today" banners and delivery dates.

holidays_next(country: str, date: str, count: int) → listor an error

The next count holidays in country from date on (that day included).

holidays_countries() → list

The countries this knows: a list of { code, name }.

holidays_easter(year: int, orthodox: bool) → stror an error

Easter Sunday of year, Western, or Orthodox when orthodox (as a Gregorian date).

Get it

$ quillo install holidays               # holidays.qwer into this folder$ quillo install holidays --example     # and its example, holidays.qwe

Or with curl:

$ curl -O https://quillolang.org/plugins/files/holidays.qwer$ curl -O https://quillolang.org/plugins/files/holidays.qwe

Then, in your program, all of it or only what you call:

main.qwe
use holidays.qweruse holidays_list, holidays_on from holidays      // Quillo 0.6: only these

It uses these crates, which cargo fetches the first time the plugin is built:

[dependencies]
chrono = { version = "0.4", default-features = false, features = ["std", "clock"] }serde_json = { version = "1", features = ["preserve_order"] }

Example

holidays.qwe
// A delivery company's site: "closed today" banners, the next days off in each country it ships to, and Easter for the spring sale.////   quillo run holidays.qwe      then open http://localhost:8080/holidays/US/2026use "holidays.qwer"route GET /holidays/:country/:year(int) = holidays_list(country, year)?route GET /check {    {        us_july_4_2026: holidays_list("US", 2026)? | find((h) => h.name == "Independence Day"),        uk_christmas_2027: holidays_list("GB", 2027)? | filter((h) => h.date >= "2027-12-25") | map((h) => `${h.name} ${h.date} off ${h.observed}`),        closed_de: holidays_on("DE", "2026-10-03")?,        closed_us: holidays_on("US", "2026-07-03")?,        open: holidays_on("FR", "2026-10-29")?,        next_ph: holidays_next("PH", "2026-10-29", 3)? | map((h) => h.name),        japan_2026_september: holidays_list("JP", 2026)? | filter((h) => slice(h.date, 5, 7) == "09") | map((h) => `${h.date} ${h.name}`),        easter: [holidays_easter(2026, false)?, holidays_easter(2026, true)?],        countries: len(holidays_countries()),        unknown: holidays_list("XX", 2026).error,    }}
$ quillo run holidays.qwe$ curl 'localhost:8080/check'

Source

holidays.qwer
//! Public holidays of 27 countries for any year from 1970 to 2099, computed (Easter and its feasts, "the last Monday of May", Japan's equinoxes): their dates, the weekday they are taken when they fall on a weekend, whether a date is one, and the next ones.//! version: 1.0.0//!//! [dependencies]//! chrono = { version = "0.4", default-features = false, features = ["std", "clock"] }//! serde_json = { version = "1", features = ["preserve_order"] }use chrono::{Datelike, Duration, NaiveDate, Weekday};use serde_json::{json, Value};type D = NaiveDate;fn ymd(y: i32, m: u32, d: u32) -> D { NaiveDate::from_ymd_opt(y, m, d).unwrap() }/// Western Easter Sunday (the Gregorian computus).fn easter(y: i32) -> D {    let a = y % 19;    let b = y / 100;    let c = y % 100;    let d = b / 4;    let e = b % 4;    let f = (b + 8) / 25;    let g = (b - f + 1) / 3;    let h = (19 * a + b - d - g + 15) % 30;    let i = c / 4;    let k = c % 4;    let l = (32 + 2 * e + 2 * i - h - k) % 7;    let m = (a + 11 * h + 22 * l) / 451;    let month = (h + l - 7 * m + 114) / 31;    let day = (h + l - 7 * m + 114) % 31 + 1;    ymd(y, month as u32, day as u32)}/// Orthodox Easter Sunday (the Julian computus, as a Gregorian date).fn orthodox_easter(y: i32) -> D {    let a = y % 4;    let b = y % 7;    let c = y % 19;    let d = (19 * c + 15) % 30;    let e = (2 * a + 4 * b - d + 34) % 7;    let month = (d + e + 114) / 31;    let day = (d + e + 114) % 31 + 1;    ymd(y, month as u32, day as u32) + Duration::days(13)}/// The `n`th `wd` of a month (n = -1 for the last).fn nth(y: i32, m: u32, wd: Weekday, n: i32) -> D {    if n > 0 {        let first = ymd(y, m, 1);        let shift = (wd.num_days_from_monday() as i64 - first.weekday().num_days_from_monday() as i64).rem_euclid(7);        first + Duration::days(shift + 7 * (n as i64 - 1))    } else {        let last = if m == 12 { ymd(y + 1, 1, 1) } else { ymd(y, m + 1, 1) } - Duration::days(1);        let shift = (last.weekday().num_days_from_monday() as i64 - wd.num_days_from_monday() as i64).rem_euclid(7);        last - Duration::days(shift)    }}/// The first `wd` on or after a date.fn on_or_after(d: D, wd: Weekday) -> D {    d + Duration::days((wd.num_days_from_monday() as i64 - d.weekday().num_days_from_monday() as i64).rem_euclid(7))}#[derive(Clone, Copy, PartialEq)]enum Shift { None, UsStyle, NextWeekday, SundayToMonday, Japan }struct Country { code: &'static str, name: &'static str, shift: Shift }const COUNTRIES: [Country; 27] = [    Country { code: "AT", name: "Austria", shift: Shift::None },    Country { code: "AU", name: "Australia", shift: Shift::NextWeekday },    Country { code: "BE", name: "Belgium", shift: Shift::None },    Country { code: "BR", name: "Brazil", shift: Shift::None },    Country { code: "CA", name: "Canada", shift: Shift::NextWeekday },    Country { code: "CH", name: "Switzerland", shift: Shift::None },    Country { code: "CZ", name: "Czechia", shift: Shift::None },    Country { code: "DE", name: "Germany", shift: Shift::None },    Country { code: "DK", name: "Denmark", shift: Shift::None },    Country { code: "ES", name: "Spain", shift: Shift::None },    Country { code: "FI", name: "Finland", shift: Shift::None },    Country { code: "FR", name: "France", shift: Shift::None },    Country { code: "GB", name: "United Kingdom (England and Wales)", shift: Shift::NextWeekday },    Country { code: "GR", name: "Greece", shift: Shift::None },    Country { code: "IE", name: "Ireland", shift: Shift::None },    Country { code: "IT", name: "Italy", shift: Shift::None },    Country { code: "JP", name: "Japan", shift: Shift::Japan },    Country { code: "MX", name: "Mexico", shift: Shift::None },    Country { code: "NL", name: "Netherlands", shift: Shift::None },    Country { code: "NO", name: "Norway", shift: Shift::None },    Country { code: "NZ", name: "New Zealand", shift: Shift::NextWeekday },    Country { code: "PH", name: "Philippines", shift: Shift::None },    Country { code: "PL", name: "Poland", shift: Shift::None },    Country { code: "PT", name: "Portugal", shift: Shift::None },    Country { code: "SE", name: "Sweden", shift: Shift::None },    Country { code: "US", name: "United States (federal)", shift: Shift::UsStyle },    Country { code: "ZA", name: "South Africa", shift: Shift::SundayToMonday },];use Weekday::*;// (date, name, moved when it falls on a weekend)fn rules(code: &str, y: i32) -> Vec<(D, &'static str, bool)> {    let e = easter(y);    let ed = |n: i64| e + Duration::days(n);    let mut v: Vec<(D, &'static str, bool)> = Vec::new();    let mut add = |d: D, n: &'static str, moved: bool| v.push((d, n, moved));    match code {        "US" => {            add(ymd(y, 1, 1), "New Year's Day", true);            add(nth(y, 1, Mon, 3), "Martin Luther King Jr. Day", false);            add(nth(y, 2, Mon, 3), "Washington's Birthday", false);            add(nth(y, 5, Mon, -1), "Memorial Day", false);            if y >= 2021 { add(ymd(y, 6, 19), "Juneteenth", true) }            add(ymd(y, 7, 4), "Independence Day", true);            add(nth(y, 9, Mon, 1), "Labor Day", false);            add(nth(y, 10, Mon, 2), "Columbus Day", false);            add(ymd(y, 11, 11), "Veterans Day", true);            add(nth(y, 11, Thu, 4), "Thanksgiving Day", false);            add(ymd(y, 12, 25), "Christmas Day", true);        }        "CA" => {            add(ymd(y, 1, 1), "New Year's Day", true);            add(ed(-2), "Good Friday", false);            add(on_or_after(ymd(y, 5, 18), Mon), "Victoria Day", false);            add(ymd(y, 7, 1), "Canada Day", true);            add(nth(y, 9, Mon, 1), "Labour Day", false);            if y >= 2021 { add(ymd(y, 9, 30), "National Day for Truth and Reconciliation", true) }            add(nth(y, 10, Mon, 2), "Thanksgiving", false);            add(ymd(y, 11, 11), "Remembrance Day", true);            add(ymd(y, 12, 25), "Christmas Day", true);            add(ymd(y, 12, 26), "Boxing Day", true);        }        "GB" => {            add(ymd(y, 1, 1), "New Year's Day", true);            add(ed(-2), "Good Friday", false);            add(ed(1), "Easter Monday", false);            add(nth(y, 5, Mon, 1), "Early May bank holiday", false);            add(nth(y, 5, Mon, -1), "Spring bank holiday", false);            add(nth(y, 8, Mon, -1), "Summer bank holiday", false);            add(ymd(y, 12, 25), "Christmas Day", true);            add(ymd(y, 12, 26), "Boxing Day", true);        }        "IE" => {            add(ymd(y, 1, 1), "New Year's Day", false);            if y >= 2023 { let f1 = ymd(y, 2, 1); add(if f1.weekday() == Fri { f1 } else { nth(y, 2, Mon, 1) }, "Saint Brigid's Day", false) }            add(ymd(y, 3, 17), "Saint Patrick's Day", false);            add(ed(1), "Easter Monday", false);            add(nth(y, 5, Mon, 1), "May Day", false);            add(nth(y, 6, Mon, 1), "June bank holiday", false);            add(nth(y, 8, Mon, 1), "August bank holiday", false);            add(nth(y, 10, Mon, -1), "October bank holiday", false);            add(ymd(y, 12, 25), "Christmas Day", false);            add(ymd(y, 12, 26), "Saint Stephen's Day", false);        }        "FR" => {            add(ymd(y, 1, 1), "New Year's Day", false);            add(ed(1), "Easter Monday", false);            add(ymd(y, 5, 1), "Labour Day", false);            add(ymd(y, 5, 8), "Victory in Europe Day", false);            add(ed(39), "Ascension Day", false);            add(ed(50), "Whit Monday", false);            add(ymd(y, 7, 14), "Bastille Day", false);            add(ymd(y, 8, 15), "Assumption", false);            add(ymd(y, 11, 1), "All Saints' Day", false);            add(ymd(y, 11, 11), "Armistice Day", false);            add(ymd(y, 12, 25), "Christmas Day", false);        }        "DE" => {            add(ymd(y, 1, 1), "New Year's Day", false);            add(ed(-2), "Good Friday", false);            add(ed(1), "Easter Monday", false);            add(ymd(y, 5, 1), "Labour Day", false);            add(ed(39), "Ascension Day", false);            add(ed(50), "Whit Monday", false);            add(ymd(y, 10, 3), "German Unity Day", false);            add(ymd(y, 12, 25), "Christmas Day", false);            add(ymd(y, 12, 26), "Second Day of Christmas", false);        }        "NL" => {            add(ymd(y, 1, 1), "New Year's Day", false);            add(e, "Easter Sunday", false);            add(ed(1), "Easter Monday", false);            let kd = ymd(y, 4, 27);            add(if kd.weekday() == Sun { ymd(y, 4, 26) } else { kd }, "King's Day", false);            add(ymd(y, 5, 5), "Liberation Day", false);            add(ed(39), "Ascension Day", false);            add(ed(49), "Whit Sunday", false);            add(ed(50), "Whit Monday", false);            add(ymd(y, 12, 25), "Christmas Day", false);            add(ymd(y, 12, 26), "Second Day of Christmas", false);        }        "BE" => {            add(ymd(y, 1, 1), "New Year's Day", false);            add(ed(1), "Easter Monday", false);            add(ymd(y, 5, 1), "Labour Day", false);            add(ed(39), "Ascension Day", false);            add(ed(50), "Whit Monday", false);            add(ymd(y, 7, 21), "National Day", false);            add(ymd(y, 8, 15), "Assumption", false);            add(ymd(y, 11, 1), "All Saints' Day", false);            add(ymd(y, 11, 11), "Armistice Day", false);            add(ymd(y, 12, 25), "Christmas Day", false);        }        "ES" => {            add(ymd(y, 1, 1), "New Year's Day", false);            add(ymd(y, 1, 6), "Epiphany", false);            add(ed(-2), "Good Friday", false);            add(ymd(y, 5, 1), "Labour Day", false);            add(ymd(y, 8, 15), "Assumption", false);            add(ymd(y, 10, 12), "National Day of Spain", false);            add(ymd(y, 11, 1), "All Saints' Day", false);            add(ymd(y, 12, 6), "Constitution Day", false);            add(ymd(y, 12, 8), "Immaculate Conception", false);            add(ymd(y, 12, 25), "Christmas Day", false);        }        "IT" => {            add(ymd(y, 1, 1), "New Year's Day", false);            add(ymd(y, 1, 6), "Epiphany", false);            add(e, "Easter Sunday", false);            add(ed(1), "Easter Monday", false);            add(ymd(y, 4, 25), "Liberation Day", false);            add(ymd(y, 5, 1), "Labour Day", false);            add(ymd(y, 6, 2), "Republic Day", false);            add(ymd(y, 8, 15), "Ferragosto", false);            add(ymd(y, 11, 1), "All Saints' Day", false);            add(ymd(y, 12, 8), "Immaculate Conception", false);            add(ymd(y, 12, 25), "Christmas Day", false);            add(ymd(y, 12, 26), "Saint Stephen's Day", false);        }        "PT" => {            add(ymd(y, 1, 1), "New Year's Day", false);            add(ed(-2), "Good Friday", false);            add(e, "Easter Sunday", false);            add(ymd(y, 4, 25), "Freedom Day", false);            add(ymd(y, 5, 1), "Labour Day", false);            add(ymd(y, 6, 10), "Portugal Day", false);            add(ed(60), "Corpus Christi", false);            add(ymd(y, 8, 15), "Assumption", false);            add(ymd(y, 10, 5), "Republic Day", false);            add(ymd(y, 11, 1), "All Saints' Day", false);            add(ymd(y, 12, 1), "Restoration of Independence", false);            add(ymd(y, 12, 8), "Immaculate Conception", false);            add(ymd(y, 12, 25), "Christmas Day", false);        }        "AT" => {            add(ymd(y, 1, 1), "New Year's Day", false);            add(ymd(y, 1, 6), "Epiphany", false);            add(ed(1), "Easter Monday", false);            add(ymd(y, 5, 1), "Labour Day", false);            add(ed(39), "Ascension Day", false);            add(ed(50), "Whit Monday", false);            add(ed(60), "Corpus Christi", false);            add(ymd(y, 8, 15), "Assumption", false);            add(ymd(y, 10, 26), "National Day", false);            add(ymd(y, 11, 1), "All Saints' Day", false);            add(ymd(y, 12, 8), "Immaculate Conception", false);            add(ymd(y, 12, 25), "Christmas Day", false);            add(ymd(y, 12, 26), "Saint Stephen's Day", false);        }        "CH" => {            add(ymd(y, 1, 1), "New Year's Day", false);            add(ed(-2), "Good Friday", false);            add(ed(1), "Easter Monday", false);            add(ed(39), "Ascension Day", false);            add(ed(50), "Whit Monday", false);            add(ymd(y, 8, 1), "Swiss National Day", false);            add(ymd(y, 12, 25), "Christmas Day", false);            add(ymd(y, 12, 26), "Saint Stephen's Day", false);        }        "SE" => {            add(ymd(y, 1, 1), "New Year's Day", false);            add(ymd(y, 1, 6), "Epiphany", false);            add(ed(-2), "Good Friday", false);            add(e, "Easter Sunday", false);            add(ed(1), "Easter Monday", false);            add(ymd(y, 5, 1), "May Day", false);            add(ed(39), "Ascension Day", false);            add(ed(49), "Whit Sunday", false);            add(ymd(y, 6, 6), "National Day", false);            add(on_or_after(ymd(y, 6, 19), Fri), "Midsummer Eve", false);            add(on_or_after(ymd(y, 6, 20), Sat), "Midsummer Day", false);            add(on_or_after(ymd(y, 10, 31), Sat), "All Saints' Day", false);            add(ymd(y, 12, 24), "Christmas Eve", false);            add(ymd(y, 12, 25), "Christmas Day", false);            add(ymd(y, 12, 26), "Second Day of Christmas", false);            add(ymd(y, 12, 31), "New Year's Eve", false);        }        "NO" => {            add(ymd(y, 1, 1), "New Year's Day", false);            add(ed(-3), "Maundy Thursday", false);            add(ed(-2), "Good Friday", false);            add(e, "Easter Sunday", false);            add(ed(1), "Easter Monday", false);            add(ymd(y, 5, 1), "Labour Day", false);            add(ymd(y, 5, 17), "Constitution Day", false);            add(ed(39), "Ascension Day", false);            add(ed(49), "Whit Sunday", false);            add(ed(50), "Whit Monday", false);            add(ymd(y, 12, 25), "Christmas Day", false);            add(ymd(y, 12, 26), "Saint Stephen's Day", false);        }        "DK" => {            add(ymd(y, 1, 1), "New Year's Day", false);            add(ed(-3), "Maundy Thursday", false);            add(ed(-2), "Good Friday", false);            add(e, "Easter Sunday", false);            add(ed(1), "Easter Monday", false);            if y < 2024 { add(ed(26), "Great Prayer Day", false) }            add(ed(39), "Ascension Day", false);            add(ed(49), "Whit Sunday", false);            add(ed(50), "Whit Monday", false);            add(ymd(y, 12, 25), "Christmas Day", false);            add(ymd(y, 12, 26), "Second Day of Christmas", false);        }        "FI" => {            add(ymd(y, 1, 1), "New Year's Day", false);            add(ymd(y, 1, 6), "Epiphany", false);            add(ed(-2), "Good Friday", false);            add(e, "Easter Sunday", false);            add(ed(1), "Easter Monday", false);            add(ymd(y, 5, 1), "May Day", false);            add(ed(39), "Ascension Day", false);            add(ed(49), "Whit Sunday", false);            add(on_or_after(ymd(y, 6, 19), Fri), "Midsummer Eve", false);            add(on_or_after(ymd(y, 6, 20), Sat), "Midsummer Day", false);            add(on_or_after(ymd(y, 10, 31), Sat), "All Saints' Day", false);            add(ymd(y, 12, 6), "Independence Day", false);            add(ymd(y, 12, 24), "Christmas Eve", false);            add(ymd(y, 12, 25), "Christmas Day", false);            add(ymd(y, 12, 26), "Saint Stephen's Day", false);        }        "PL" => {            add(ymd(y, 1, 1), "New Year's Day", false);            if y >= 2011 { add(ymd(y, 1, 6), "Epiphany", false) }            add(e, "Easter Sunday", false);            add(ed(1), "Easter Monday", false);            add(ymd(y, 5, 1), "Labour Day", false);            add(ymd(y, 5, 3), "Constitution Day", false);            add(ed(49), "Whit Sunday", false);            add(ed(60), "Corpus Christi", false);            add(ymd(y, 8, 15), "Assumption", false);            add(ymd(y, 11, 1), "All Saints' Day", false);            add(ymd(y, 11, 11), "Independence Day", false);            if y >= 2025 { add(ymd(y, 12, 24), "Christmas Eve", false) }            add(ymd(y, 12, 25), "Christmas Day", false);            add(ymd(y, 12, 26), "Second Day of Christmas", false);        }        "CZ" => {            add(ymd(y, 1, 1), "Restoration Day of the Independent Czech State", false);            add(ed(-2), "Good Friday", false);            add(ed(1), "Easter Monday", false);            add(ymd(y, 5, 1), "Labour Day", false);            add(ymd(y, 5, 8), "Liberation Day", false);            add(ymd(y, 7, 5), "Saints Cyril and Methodius Day", false);            add(ymd(y, 7, 6), "Jan Hus Day", false);            add(ymd(y, 9, 28), "Statehood Day", false);            add(ymd(y, 10, 28), "Independent Czechoslovak State Day", false);            add(ymd(y, 11, 17), "Struggle for Freedom and Democracy Day", false);            add(ymd(y, 12, 24), "Christmas Eve", false);            add(ymd(y, 12, 25), "Christmas Day", false);            add(ymd(y, 12, 26), "Saint Stephen's Day", false);        }        "GR" => {            let o = orthodox_easter(y);            add(ymd(y, 1, 1), "New Year's Day", false);            add(ymd(y, 1, 6), "Epiphany", false);            add(o - Duration::days(48), "Clean Monday", false);            add(ymd(y, 3, 25), "Independence Day", false);            add(o - Duration::days(2), "Orthodox Good Friday", false);            add(o, "Orthodox Easter Sunday", false);            add(o + Duration::days(1), "Orthodox Easter Monday", false);            add(ymd(y, 5, 1), "Labour Day", false);            add(o + Duration::days(50), "Orthodox Whit Monday", false);            add(ymd(y, 8, 15), "Assumption", false);            add(ymd(y, 10, 28), "Ohi Day", false);            add(ymd(y, 12, 25), "Christmas Day", false);            add(ymd(y, 12, 26), "Synaxis of the Mother of God", false);        }        "BR" => {            add(ymd(y, 1, 1), "New Year's Day", false);            add(ed(-48), "Carnival Monday", false);            add(ed(-47), "Carnival Tuesday", false);            add(ed(-2), "Good Friday", false);            add(ymd(y, 4, 21), "Tiradentes", false);            add(ymd(y, 5, 1), "Labour Day", false);            add(ed(60), "Corpus Christi", false);            add(ymd(y, 9, 7), "Independence Day", false);            add(ymd(y, 10, 12), "Our Lady of Aparecida", false);            add(ymd(y, 11, 2), "All Souls' Day", false);            add(ymd(y, 11, 15), "Republic Proclamation Day", false);            if y >= 2024 { add(ymd(y, 11, 20), "Black Consciousness Day", false) }            add(ymd(y, 12, 25), "Christmas Day", false);        }        "MX" => {            add(ymd(y, 1, 1), "New Year's Day", false);            add(nth(y, 2, Mon, 1), "Constitution Day", false);            add(nth(y, 3, Mon, 3), "Benito Juárez's Birthday", false);            add(ymd(y, 5, 1), "Labour Day", false);            add(ymd(y, 9, 16), "Independence Day", false);            if y >= 2024 && (y - 2024) % 6 == 0 { add(ymd(y, 10, 1), "Inauguration Day", false) }            add(nth(y, 11, Mon, 3), "Revolution Day", false);            add(ymd(y, 12, 25), "Christmas Day", false);        }        "AU" => {            add(ymd(y, 1, 1), "New Year's Day", true);            add(ymd(y, 1, 26), "Australia Day", true);            add(ed(-2), "Good Friday", false);            add(ed(-1), "Easter Saturday", false);            add(ed(1), "Easter Monday", false);            add(ymd(y, 4, 25), "Anzac Day", false);            add(nth(y, 6, Mon, 2), "King's Birthday", false);            add(ymd(y, 12, 25), "Christmas Day", true);            add(ymd(y, 12, 26), "Boxing Day", true);        }        "NZ" => {            add(ymd(y, 1, 1), "New Year's Day", true);            add(ymd(y, 1, 2), "Day after New Year's Day", true);            add(ymd(y, 2, 6), "Waitangi Day", y >= 2014);            add(ed(-2), "Good Friday", false);            add(ed(1), "Easter Monday", false);            add(ymd(y, 4, 25), "Anzac Day", y >= 2015);            add(nth(y, 6, Mon, 1), "King's Birthday", false);            let matariki = [(2022, 6, 24), (2023, 7, 14), (2024, 6, 28), (2025, 6, 20), (2026, 7, 10), (2027, 6, 25), (2028, 7, 14), (2029, 7, 6), (2030, 6, 21)];            if let Some((_, m, d)) = matariki.iter().find(|(yy, _, _)| *yy == y) { add(ymd(y, *m, *d), "Matariki", false) }            add(nth(y, 10, Mon, 4), "Labour Day", false);            add(ymd(y, 12, 25), "Christmas Day", true);            add(ymd(y, 12, 26), "Boxing Day", true);        }        "PH" => {            add(ymd(y, 1, 1), "New Year's Day", false);            add(ed(-3), "Maundy Thursday", false);            add(ed(-2), "Good Friday", false);            add(ed(-1), "Black Saturday", false);            add(ymd(y, 4, 9), "Araw ng Kagitingan", false);            add(ymd(y, 5, 1), "Labor Day", false);            add(ymd(y, 6, 12), "Independence Day", false);            add(ymd(y, 8, 21), "Ninoy Aquino Day", false);            add(nth(y, 8, Mon, -1), "National Heroes Day", false);            add(ymd(y, 11, 1), "All Saints' Day", false);            add(ymd(y, 11, 30), "Bonifacio Day", false);            add(ymd(y, 12, 8), "Feast of the Immaculate Conception", false);            add(ymd(y, 12, 24), "Christmas Eve", false);            add(ymd(y, 12, 25), "Christmas Day", false);            add(ymd(y, 12, 30), "Rizal Day", false);            add(ymd(y, 12, 31), "Last Day of the Year", false);        }        "JP" => {            let since = (y - 1980) as f64;            let leap = ((y - 1980) / 4) as f64;            add(ymd(y, 1, 1), "New Year's Day", true);            add(nth(y, 1, Mon, 2), "Coming of Age Day", false);            add(ymd(y, 2, 11), "National Foundation Day", true);            if y >= 2020 { add(ymd(y, 2, 23), "Emperor's Birthday", true) }            add(ymd(y, 3, (20.8431 + 0.242194 * since - leap).floor() as u32), "Vernal Equinox Day", true);            add(ymd(y, 4, 29), "Showa Day", true);            add(ymd(y, 5, 3), "Constitution Memorial Day", true);            add(ymd(y, 5, 4), "Greenery Day", true);            add(ymd(y, 5, 5), "Children's Day", true);            add(nth(y, 7, Mon, 3), "Marine Day", false);            if y >= 2016 { add(ymd(y, 8, 11), "Mountain Day", true) }            add(nth(y, 9, Mon, 3), "Respect for the Aged Day", false);            add(ymd(y, 9, (23.2488 + 0.242194 * since - leap).floor() as u32), "Autumnal Equinox Day", true);            add(nth(y, 10, Mon, 2), "Sports Day", false);            add(ymd(y, 11, 3), "Culture Day", true);            add(ymd(y, 11, 23), "Labour Thanksgiving Day", true);        }        "ZA" => {            add(ymd(y, 1, 1), "New Year's Day", true);            add(ymd(y, 3, 21), "Human Rights Day", true);            add(ed(-2), "Good Friday", false);            add(ed(1), "Family Day", false);            add(ymd(y, 4, 27), "Freedom Day", true);            add(ymd(y, 5, 1), "Workers' Day", true);            add(ymd(y, 6, 16), "Youth Day", true);            add(ymd(y, 8, 9), "National Women's Day", true);            add(ymd(y, 9, 24), "Heritage Day", true);            add(ymd(y, 12, 16), "Day of Reconciliation", true);            add(ymd(y, 12, 25), "Christmas Day", true);            add(ymd(y, 12, 26), "Day of Goodwill", true);        }        _ => {}    }    v.sort_by_key(|x| x.0);    v}fn country(code: &str) -> Result<&'static Country, String> {    let c = code.trim().to_uppercase();    let c = if c == "UK" { "GB".to_string() } else { c };    COUNTRIES.iter().find(|x| x.code == c).ok_or_else(|| format!("`{}` is not a country this knows: {}", code, COUNTRIES.iter().map(|c| c.code).collect::<Vec<_>>().join(", ")))}struct Holiday { date: D, name: &'static str, observed: D }fn year_list(c: &Country, y: i32) -> Vec<Holiday> {    let raw = rules(c.code, y);    let weekend = |d: D| matches!(d.weekday(), Sat | Sun);    let mut taken: Vec<D> = raw.iter().map(|h| h.0).filter(|d| !weekend(*d)).collect();    let mut out = Vec::new();    for (d, name, moved) in &raw {        let mut obs = *d;        if *moved {            match c.shift {                Shift::UsStyle => { if d.weekday() == Sat { obs = *d - Duration::days(1) } else if d.weekday() == Sun { obs = *d + Duration::days(1) } }                Shift::NextWeekday => {                    if weekend(*d) {                        let mut n = *d + Duration::days(1);                        while weekend(n) || taken.contains(&n) { n += Duration::days(1) }                        obs = n;                        taken.push(n);                    }                }                Shift::SundayToMonday => { if d.weekday() == Sun { obs = *d + Duration::days(1) } }                Shift::Japan => {                    if d.weekday() == Sun {                        let all: Vec<D> = raw.iter().map(|h| h.0).collect();                        let mut n = *d + Duration::days(1);                        while all.contains(&n) || taken.contains(&n) { n += Duration::days(1) }                        obs = n;                        taken.push(n);                    }                }                Shift::None => {}            }        }        out.push(Holiday { date: *d, name, observed: obs });    }    if c.shift == Shift::Japan {        // A day between two holidays is a holiday too (kokumin no kyūjitsu).        let dates: Vec<D> = out.iter().map(|h| h.date).collect();        for &d in &dates {            let gap = d + Duration::days(1);            if dates.contains(&(d + Duration::days(2))) && !dates.contains(&gap) && gap.weekday() != Sun {                out.push(Holiday { date: gap, name: "Citizens' Holiday", observed: gap });            }        }        out.sort_by_key(|h| h.date);    }    out}fn to_json(h: &Holiday) -> Value {    json!({        "date": h.date.to_string(),        "name": h.name,        "weekday": h.date.format("%A").to_string(),        "observed": h.observed.to_string(),    })}fn year_ok(y: i64) -> Result<i32, String> {    if !(1970..=2099).contains(&y) { return Err(format!("years 1970 to 2099, not {}", y)) }    Ok(y as i32)}fn parse(d: &str) -> Result<D, String> {    NaiveDate::parse_from_str(d.trim().get(..10).unwrap_or(d), "%Y-%m-%d").map_err(|_| format!("`{}` is not a date (2026-12-25)", d))}/// The public holidays of `country` (`"US"`, `"GB"`, `"DE"`, `"JP"`, `"PH"`...) in `year`: a list of `{ date, name, weekday, observed }`, in date order./// `observed` is the day off when the holiday is moved for falling on a weekend (US Saturday holidays on the Friday, UK and Australian ones on the next Monday...), else `date`. National holidays only: not regional ones, nor ones fixed each year by decree.pub fn holidays_list(country: &str, year: i64) -> Result<Vec<Value>, String> {    let c = self::country(country)?;    Ok(year_list(c, year_ok(year)?).iter().map(to_json).collect())}/// The holiday on `date` in `country`, as its name, counting the day it is observed on; null when it is a working day. For "closed today" banners and delivery dates.pub fn holidays_on(country: &str, date: &str) -> Result<Option<String>, String> {    let c = self::country(country)?;    let d = parse(date)?;    let y = year_ok(d.year() as i64)?;    let hit = year_list(c, y).into_iter().chain(if y < 2099 { year_list(c, y + 1) } else { vec![] }).find(|h| h.date == d || h.observed == d);    Ok(hit.map(|h| if h.date == d { h.name.to_string() } else { format!("{} (observed)", h.name) }))}/// The next `count` holidays in `country` from `date` on (that day included).pub fn holidays_next(country: &str, date: &str, count: i64) -> Result<Vec<Value>, String> {    let c = self::country(country)?;    let d = parse(date)?;    let mut out = Vec::new();    let mut y = year_ok(d.year() as i64)?;    while (out.len() as i64) < count.min(200) && y <= 2099 {        for h in year_list(c, y) {            if h.date >= d && (out.len() as i64) < count { out.push(to_json(&h)) }        }        y += 1;    }    Ok(out)}/// The countries this knows: a list of `{ code, name }`.pub fn holidays_countries() -> Vec<Value> {    COUNTRIES.iter().map(|c| json!({ "code": c.code, "name": c.name })).collect()}/// Easter Sunday of `year`, Western, or Orthodox when `orthodox` (as a Gregorian date).pub fn holidays_easter(year: i64, orthodox: bool) -> Result<String, String> {    if !(1583..=4099).contains(&year) { return Err("years 1583 to 4099".into()) }    Ok(if orthodox { orthodox_easter(year as i32) } else { easter(year as i32) }.to_string())}
Chinese calendar chinese_calendar.qwer · 1.0.0The Chinese lunisolar calendar: a date's lunar year, month and day in Chinese, leap months included, the year's stem and branch, zodiac animal and element, Chinese New Year, lunar dates back to Gregorian, and the 24 solar terms.datescalendarchineselocale8 views · 7 downloadsHijri calendar hijri.qwer · 1.0.0Islamic (Hijri) dates: a Gregorian date in Umm al-Qura, civil or observational reckoning, written in English and Arabic; a Hijri date back to the Gregorian; a month's first and last days; and the year's holy days.datescalendarlocalei18n7 views · 7 downloadsWorld calendars world_calendars.qwer · 1.0.0Dates in the world's other calendars, each way and with month names: Hebrew, Persian, Ethiopian, Coptic, Indian national, Thai Buddhist, Japanese eras (令和8年) and Taiwan's Minguo; and the Jewish holidays of a year.datescalendarlocalei18n8 views · 6 downloadsBusiness days business_days.qwer · 1.0.0Working days for due dates, delivery estimates and SLAs: add them, count them between dates, skip weekends and holidays, with US, UK, French and German public holidays built in.datestimeinvoicesshipping6 views · 5 downloadsCalendar files ical.qwer · 1.0.0Calendar files (.ics) for bookings and events, added to Google Calendar, Outlook and Apple Calendar in a click.calendaricsbookingsevents6 views · 6 downloadsEnglish dates dates_english.qwer · 1.0.0Dates typed in English read as times, in the user's time zone and US or UK style: next friday at 3pm, tomorrow 9am, in 2 weeks, 3 days ago.datestimeformstext8 views · 10 downloads
Thread
811Useful? Like it, or add to its thread below.

Thread 0 posts

On Public holidays: questions, tips, corrections, and code that uses it.

No posts yet. The first one opens the thread.