By Ether DataRequest data sample
All sections

Holidays

National holiday flags derived from public-calendar rules rather than a scraped feed; v0 covers US, UK, and Canada, and a holiday is a 24-local-hour slot-set, not a single slot.

stable5 min read
Source time
local_date, country
Destination time
holiday_flag, slot_set
approximate

Purpose

A holiday flag answers "is this local calendar date a national holiday in this country" — used to explain otherwise-anomalous demand, traffic, or audience patterns without a manual lookup table. This KB computes holidays from rules (fixed dates, the nth or last weekday of a month, and the Gregorian Easter computus) rather than from a scraped or licensed calendar feed. That makes the result small, auditable, and reproducible for any year, at the cost of completeness: v0 covers US federal, UK bank, and Canada national holidays only, and does not attempt regional, lunar, or locally-observed holidays. Critically, a holiday is not a slot — it is a 24-local-hour band, i.e. a slot-set once mapped to UTC, exactly like a broadcast day.

Source and destination

Source: a local_date (YYYY-MM-DD) and a country code. Destination: a holiday_flag (matched holiday name, if any) and, when mapped to UTC for measurement, a slot_set covering that local calendar date's 24 hours.

Exactness: approximate

approximate. The rule set is exact for what it models (a fixed date always falls on that date; an nth-weekday rule always resolves the same way; the Easter computus is a well-defined deterministic algorithm), but the coverage is approximate relative to "all holidays that matter" in a given country: it omits state/provincial holidays, substitute (in-lieu) days by default, and any lunar or movable holiday not already enumerated. Treat a false result as "not flagged by this rule set," not as an authoritative "not a holiday anywhere in this jurisdiction."

Algorithm

import { holidaysFor, isHoliday, easterSunday } from "@/lib/time/holidays";

holidaysFor("US", 2026).find((h) => h.name === "Thanksgiving");
// -> { date: "2026-11-26", name: "Thanksgiving", country: "US" }
// (4th Thursday of November — nthWeekday(year, month=11, isoWeekday=Thursday, n=4))

easterSunday(2026);
// -> { month: 4, day: 5 }  (2026-04-05, via the Anonymous/Meeus computus)

isHoliday("2026-04-05", "UK");
// -> null — Easter SUNDAY itself is not a UK bank holiday; Good Friday
// (2026-04-03) and Easter Monday (2026-04-06) are, and are computed as
// offsets from easterSunday() rather than looked up separately.

isHoliday("2026-07-04", "US");
// -> { date: "2026-07-04", name: "Independence Day", country: "US" }

Each country's holiday list is a small, explicit array built from three primitives: a literal fixed date (iso(y, 12, 25) for Christmas), an nth-weekday rule (nthWeekday(y, 1, 1, 3) for the third Monday in January — Martin Luther King Jr. Day), and a last-weekday rule (lastWeekday(y, 5, 1) for the last Monday in May — Memorial Day). Easter-derived UK holidays compute an offset in days from easterSunday(y) rather than encoding their own date rule, so they stay correct in every year without a separate lookup table.

Parameters

country
One of US | UK | CA in v0. Each has its own rule builder.
year
Calendar year the rules are evaluated for; all rules are computable for any year, past or future.
dateISO
For isHoliday: the local calendar date to test, YYYY-MM-DD.

Outputs

holidaysFor returns every Holiday ({date, name, country}) the rule set produces for that country and year. isHoliday returns the matching Holiday or null. easterSunday returns {month, day} for the Gregorian Easter Sunday of a given year — the anchor several UK holidays are computed from.

Units and convention

Holiday dates are local calendar dates (YYYY-MM-DD) in the country's own civil calendar, not UTC instants. Mapping a holiday to the KB's canonical UTC slots requires an explicit zone and produces a 24-hour slot-set (which, like a broadcast day, may be 23 or 25 UTC hours across a DST transition in that country) rather than a single slot.

DST and disambiguation behavior

Holiday date computation itself has no DST dependency — it is pure calendar arithmetic. DST only enters once a holiday's local date is converted to a UTC slot-set for measurement, at which point the same gap/fold handling as DST Handling applies to the conversion, not to the holiday rule.

Quality and provenance

This is intentionally a small, auditable rule set, not a substitute for an authoritative feed anywhere legal observance matters (payroll, banking closures, contractual SLAs). State the tzdb/rule-set version alongside any holiday-flag output, and do not silently extend v0's three-country coverage by inference — an unmodeled country should report "unknown," not "not a holiday."

Edge cases

Substitute (in-lieu) holiday days: when a fixed-date holiday lands on a Saturday or Sunday, many countries (UK, much of APAC) observe a substitute weekday instead — this rule set does not apply in-lieu substitution, so a fixed-date holiday falling on a weekend is flagged on its nominal date only, which will disagree with the country's actual observed closure date. Movable and regional holidays: lunar-calendar holidays (Eid, Diwali, Lunar New Year) shift against the Gregorian calendar year to year and are not covered by this rule set at all; regional holidays (US state, Canadian province) are omitted from the national-only lists above.

Python parity

def easter_sunday(year: int) -> tuple[int, int]:
    a = year % 19
    b, c = divmod(year, 100)
    d, e = divmod(b, 4)
    f = (b + 8) // 25
    g = (b - f + 1) // 3
    h = (19 * a + b - d - g + 15) % 30
    i, k = divmod(c, 4)
    l = (32 + 2 * e + 2 * i - h - k) % 7
    m = (a + 11 * h + 22 * l) // 451
    month = (h + l - 7 * m + 114) // 31
    day = (h + l - 7 * m + 114) % 31 + 1
    return month, day  # e.g. easter_sunday(2026) == (4, 5)

The tested reference implementation is the TypeScript in lib/time/holidays.ts; this Python is the identical Anonymous/Meeus computus (integer division in place of Math.floor), producing the same (month, day) for every year. The fixed-date and nth/last-weekday rules translate directly using calendar.monthrange or manual weekday arithmetic and are omitted here for brevity — the algorithm is the same as nthWeekday/lastWeekday above.

Edge cases affecting this page

Week SystemsTime systems & zones