Reference
This page restates the language as lookup tables. The specification is the normative text; where wording differs, the specification governs.
Document keys
Section titled “Document keys”| Key | Required | Value |
|---|---|---|
version |
✓ | The spec version the document is written against, as an "x.y" string |
timezone |
✓ | An IANA Time Zone Database name ("Asia/Tokyo", "UTC"); fixed offsets are invalid |
resolvers |
Non-empty list of names the host must bind; omitted when there are none | |
calendar |
The definitions (below) | |
schedules |
✓ | Non-empty list of schedules, combined with OR |
label |
Annotation (below): one line, 1–100 characters | |
description |
Annotation (below): 1–1,000 characters, LF as the only line break |
Unknown keys are an error at every level — document, calendar, schedule, and the times object.
Duplicate member names are an error in every object, names compared
after escape resolution ("timezone" and "\u0074imezone" are the
same name).
Calendar keys
Section titled “Calendar keys”| Key | Value | Role |
|---|---|---|
business_days |
date list or name | Top layer: days the organization works, overriding everything below |
business_holidays |
date list or name | The organization’s own closures |
holidays |
date list or name | Public holidays |
workweek |
non-empty list of day names | Bottom layer: the weekly default (omitted = mon–fri) |
business_hours |
non-empty list of windows | The window list behind "business_hour" |
date_sets |
object: name → date list | The open namespace of the document’s own named date lists (no layer semantics) |
- A date-list position accepts exactly two forms: an array of date
literals, or a name (a
date_setsentry, or a name the host binds to a resolver). A date-shaped string matches neither form - A date list may be empty — an explicit empty list states that there are no such days
Calendar vocabulary
Section titled “Calendar vocabulary”| Word | Asks |
|---|---|
weekday |
day-of-week ∈ Mon–Fri — fixed; workweek plays no part |
weekend |
day-of-week ∈ Sat–Sun — fixed |
holiday |
date ∈ holidays, that list alone |
business_day |
the stacked conclusion (procedure below) |
business_holiday |
the exact complement of business_day |
business_day(date): if date ∈ business_days: true — the top layer wins if date ∈ business_holidays: false if date ∈ holidays: false otherwise: day-of-week(date) ∈ workweek (workweek omitted = mon–fri)holiday requires the holidays definition; business_day /
business_holiday require all three of holidays /
business_holidays / business_days. Using a word without its
required definitions is a document validation error.
Schedule fields
Section titled “Schedule fields”| Field | Value | Notes |
|---|---|---|
years |
non-empty list of integers 1–9999 | Date axis |
months |
non-empty list of integers 1–12 | Date axis |
days |
non-empty list of day atoms | Date axis |
if |
filter tuple | Filters base days without moving them; applied before shift |
shift |
shift tuple | Moves each base day until a landing condition holds |
times |
list or grid object | Time part |
allday |
true |
Time part: a day-level occurrence |
every |
[count, unit] |
Time part: an interval sequence anchored at from |
from |
"YYYY-MM-DD HH:MM" |
Validity start, inclusive |
until |
"YYYY-MM-DD HH:MM" |
Validity end, exclusive |
label |
string | Annotation (below); inert |
description |
string | Annotation (below); inert |
- Exactly one of
times/allday/everyis required - A schedule with a top-level
everytakes noyears/months/days/shift/if, and requiresfrom fromis also required by the["every", N, "day"]day atom- With both
fromanduntil, the resolved instant offrommust be strictly earlier - An omitted date axis means no restriction on that axis
- The algebra: within an axis’s array — OR; between fields — AND; between schedules — OR
Annotations
Section titled “Annotations”label and description may appear on the document and on each
schedule. They are inert: never part of validation, evaluation, or
any query’s answer, preserved unmodified through round-trips, and not
identifiers (no uniqueness, no referring to a schedule by label).
| Field | Form |
|---|---|
label |
One line; 1–100 characters |
description |
1–1,000 characters; LF as the only line break |
- Control characters are forbidden (LF in
descriptionis the single exception), as are ZWSP, the word joiner, the BOM, and the bidi embedding / override / isolate controls. ZWJ / ZWNJ and the bidi marks are legal - Must contain at least one non-whitespace character — omit the key rather than write an empty annotation
Day atoms
Section titled “Day atoms”| Atom | Example | Selects |
|---|---|---|
| Number 1–31 | 25 |
That day of the month (a day the month does not have simply does not match) |
| Day name | "mon" |
That weekday |
| Calendar word | "holiday" |
The five vocabulary words above |
| Ordinal tuple | ["3rd", "mon"] |
The Nth / last such weekday of the month |
| End of month | "last_day_of_month" |
The last day of the month |
| Day-cycle tuple | ["every", 2, "day"] |
Every Nth day, counting the date of from as day one |
| Name | "founding-day" |
Membership in the named date set |
- Ordinals:
"1st""2nd""3rd""4th""5th""last" - Day names:
"mon""tue""wed""thu""fri""sat""sun" - The day-cycle count is an integer ≥ 1, at most 3,652,058 (bounded by
the date domain); the unit is the fixed
"day". The tuple is allowed only as an element ofdays, not as ashiftlanding condition or anifcondition - A date literal cannot be written in
days— give the date a name underdate_setsand refer to it - There are no negative day numbers
["prev" | "next", atom] exclusive: strictly before / after["prev" | "next", "or_same", atom] inclusive: the base day itself is tested first- The landing condition is a day atom (not a day-cycle tuple)
- Maximum displacement: 366 calendar days. A base day whose landing condition never holds within that range produces no occurrences
- Consecutive base days may land on the same day and collapse into a single match
- The landing day is not bound by the date axes and may move into an adjacent month or year
[atom] the day itself matches["not", atom] the day itself does not match["prev" | "next", atom] the neighbouring day matches["prev" | "next", "not", atom] the neighbouring day does not matchif filters the base days first; shift then moves what remains.
| Form | Example | Meaning |
|---|---|---|
| List | ["09:00", "12:00"] |
Non-empty enumeration of fixed times |
| Grid | {"every": [1, "hour"], "between": ["08:00", "20:00"]} |
Clock positions, laid out per day and per window |
| All-day | "allday": true |
A day-level occurrence; time does not apply to it |
The grid object:
every—[count, unit]. Unit and maximum count:"hour"24,"minute"1,440,"second"86,400 (one day’s worth in each unit); the count is an integer ≥ 1between— a window (half-open[start, end)), or"business_hour"(the window list ofcalendar.business_hours, which must then be defined). Omitted = the whole day [00:00, 24:00)- The grid anchors at the start of each window and carries nothing over from the previous day or window
every (directly on a schedule)
Section titled “every (directly on a schedule)”[count, unit] — the points from + k × interval (k = 0, 1, 2, …),
counting across days with no per-day re-anchoring.
- Unit and maximum count:
"hour"87,649,415,"minute"5,258,964,959,"second"315,537,897,599 (bounded by the date domain); the count is an integer ≥ 1. The unit"day"is invalid here — whole-day cycles are the["every", N, "day"]atom combined withtimes fromis required;untilis optional
Literal forms
Section titled “Literal forms”| Kind | Form | Notes |
|---|---|---|
| Date | "YYYY-MM-DD" |
Proleptic Gregorian; years 1–9999; must be a real date |
| Time | "HH:MM" |
Zero-padded; no seconds; "24:00" is a token allowed only as a window end |
| Window | ["HH:MM", "HH:MM"] |
Half-open [start, end); start < end; windows must not overlap (touching is legal); cannot cross midnight |
| Date-time | "YYYY-MM-DD HH:MM" |
The from / until form; zero-padded, a single space (U+0020), no seconds, no 24:00; the date part must be a real date |
| Timezone | IANA name | "Asia/Tokyo", "UTC"; fixed offsets ("+09:00") are rejected |
| Version | "x.y" |
|
| Day of week | "mon"–"sun" |
A name — a date_sets key, a declared resolver, or the reference
written in a date-list position or in days — must be a non-empty
string that is not: digits only, time-shaped (HH:MM), date-shaped
(YYYY-MM-DD), or a reserved word. All names share one namespace: a
name must not be both a date_sets key and a declared resolver, and a
name that is used but neither defined nor declared is a document
validation error.
The reserved words:
weekday weekend holiday business_day business_holiday business_hourmon tue wed thu fri sat sun1st 2nd 3rd 4th 5th lastlast_day_of_monthnot prev next or_samehour minute second dayversion timezone resolvers calendar schedulesyears months days shift if times allday every between from untilholidays business_holidays business_days workweek business_hours date_setslabel descriptionDeliberately unsupported
Section titled “Deliberately unsupported”Year cycles · relative intervals (“N seconds since the last run”) · computed dates anchored to a fixed date · anything that crosses the date plane and the time plane · windows that cross midnight · per-weekday business hours · user-defined window names · definition macros. The reasoning is in the specification’s Deliberately unsupported section.