日本語 · English

Standard premise: Gregorian

Translated from the canonical Japanese page stdlib/gregorian.md. The source_sha above records the source revision; a consistency check flags this page when the Japanese original changes.

Gregorian is a transparent standard premise bundled with Kairos (a primitive-definition root: it has no derivation source). “Transparent” means it is not language built-in magic: it is written in the same primitive-definition syntax a user can write (../spec/20-premise-layer.md §3.6), so its contents can be read and swapped out. This page explains its complete definition and each of its words. The language specification (../spec/) cites this Gregorian only as “an example of a primitive definition”; the exhaustive account is this page’s charge.

1. Complete definition

premise Gregorian {
  day     = chronos grid 1d                                 # atom (carve the continuous base Chronos into civil days)
  weekday = day cycle [Mon, Tue, Wed, Thu, Fri, Sat, Sun] anchor: 2000-01-03

  epochYear    = 1970                                       # auxiliary value functions (the epoch year; the language default of ADR-31)
  monthOf      = m => m mod 12
  yearOf       = m => epochYear + m div 12
  monthLengths = leap => [31, leap ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]

  isLeap      = y => y mod 4 == 0 and not(y mod 100 == 0 and y mod 400 != 0)
  daysInMonth = m => monthLengths(isLeap(yearOf(m)))[monthOf(m)]
  daysInMonthOf = d => daysInMonth(epochOrdinal(month, d))       # point → its month's day count (§4.9 calendar-coordinate sugar family; ADR-46 era, the third feedback batch F101)
  month   = day   span daysInMonth label: (p => monthNo(p))        # basic grouping: bundles day (year-independent)
  year    = month span (_ => 12) phase: 0 label: (p => yearNo(p))  # bundles month twelve at a time
  quarter = year  split (_ => [3, 3, 3, 3]) by: month       # year's dependent window

  weekStart = day |> filter(d => weekday(d) == wkst)        # wkst lazily resolves the user-side preamble declaration (§4.5)
  week      = day |> segmentBy(weekStart, edges: clip, empties: error)

  monthStart = month |> first                               # public boundary words (selector reuse)
  monthEnd   = month |> last
  yearStart  = year  |> first
  # yearEnd is undefined (kept asymmetric because no demand has arisen; if needed, it is the one
  # line year |> last, and the fiscal year end is reached via Fiscal's within(year) |> last — fiscal.md)

  yearNo  = d => yearOf(epochOrdinal(month, d))             # calendar-coordinate sugar (spec §4.9): the calendar year (2026 etc.)
  monthNo = d => monthOf(epochOrdinal(month, d)) + 1        # the calendar month (1..12)
  dayNo   = d => ordinalIn(day, month, d)                   # the calendar day (1..31)
}

The auxiliary value functions (top-level bindings inside the complete-definition block = public bindings; derivations, too, reference them by bare name — Fiscal’s fiscalYearNo using yearOf is this inheritance. All of them compute from the month’s serial number m alone and do not reference the year window):

The epoch at which the window ordinals m and n originate is the language default 1970-01-01T00:00 (in-scope tz), and ordinals are 0-based (m = 0 is January 1970. ADR-31). epochYear = 1970 is a value drawn from this default. A calendar system with a different basis can re-anchor the reference with the primitive-definition member epoch: (in which case write the auxiliary value functions to match that epoch).

Note: day’s width is a civil day (not 86400 seconds)

The 1d in day = chronos grid 1d is the width convention “one civil day (one day of the calendar)”, not 86400s (a fixed number of seconds). A civil day is not constant in elapsed-time length — it runs 23 or 25 hours on DST transition days (ADR-11; leap seconds are out of scope — chronos is a uniform idealized axis without leap seconds (each UTC day = 86,400 seconds), and 23:59:60 is a lexical error. ADR-33). If day were ticked at the fixed amount 86400s, that would be an “elapsed-time” width, drifting away from the civil day at every DST transition (ADR-12 verified, on a UK DST day, that “one day later” turns into different datetimes under elapsed time 24h versus civil time). Gregorian’s day is the calendar’s day = the civil day, so its width is given by the civil-time convention 1d, not physical seconds. Elapsed-time widths (24h, 86400s, and so on) are a concept for other uses, such as shift’s unit:; they are not used to partition the calendar.

grid’s phase aligns by default — civil-time widths (d) to the start instant of each civil day in the in-scope tz: (midnight on ordinary days. ADR-31 revised), elapsed-time widths (h/m/s) to the epoch. That day = chronos grid 1d, with nothing specified, becomes “calendar days cut at midnight” is due to this default. Only when a different phase is needed is it overridden with anchor: (ADR-31).

2. Each word

Word Kind Description
day Window (atom) The calendar’s atom: the continuous base Chronos uniformly partitioned (grid) at width one day.
weekday Parallel labels Attaches weekday labels to each day cyclically (cycle). A label, not a window. Details in §4.
isLeap Value expression The leap-year test (the Gregorian rule). The argument is a calendar year (a value).
daysInMonth Value expression Returns the day count from a month ordinal. Views leap as a value (§3).
daysInMonthOf Value expression Sugar returning, from a point, its month’s day count (daysInMonth(epochOrdinal(month, d))). Lets “the latter half of the month only” and “from N days before month-end” be written with no approximation (the third feedback batch, F101)
month Window The basic grouping. Bundles a variable number (28–31) of days (span). Independent of year. Standard label = the calendar month number (ADR-42) — month(5) = the days of every May (window-instance reference).
year Window Bundles month twelve at a time (span). phase: 0 = January start. Standard label = the calendar year (ADR-42) — year(2026) = the days of 2026.
quarter Window The dependent window splitting year into three-month pieces (split by: month). Auto-tracks changes to year.
week Window The 7-day window at WKST phase, parallel to (not nested in) month and year. Segments the day sequence at weekStart (the wkst-labeled days). wkst lazily resolves the user-side preamble declaration (§4.5).
monthStart/monthEnd/yearStart Public boundary words The first and last points of each window, derived by reusing the selectors (first/last). The generator monthEnd (the calendar-day month end) is in reality this.
yearNo/monthNo/dayNo Value functions (calendar coordinates) Projection sugar reading a point’s calendar coordinates (the composition of epochOrdinal + ordinalIn + auxiliary value functions that spec §4.9 announces). Fixed days via dayNo(d) == 11, and so on.

Under the standard labels, year(d) and yearNo(d) become two equivalent spellings of the same value (likewise month(d)/monthNo(d)). The calendar-coordinate sugar (the yearNo family) is canonicallabel: chiefly grants the qualification for window-instance reference (the value-argument side) (spec §4.9, ADR-42). The canonical form for narrowing to a specific period:

# eval: 2026-04-28..2026-05-03
@JP
month(5) & year(2026)
#=> 2026-05-01 2026-05-02

With daysInMonthOf (point → month length), “the latter half of the month only” and “from N days before month-end” can be written without dayNo(d) > 15-class approximations (the third feedback batch F101 = the symmetric completion of the §4.9 sugar family):

# eval: 2026-02-01..2026-03-05
@JP
everyDay |> filter(d => dayNo(d) > daysInMonthOf(d) - 3)
#=> 2026-02-26 2026-02-27 2026-02-28

3. The dependency direction, and “leap is a value, not a window”

Dependencies are primarily bottom-up aggregation: day → month → year. month is the basic grouping, and year is its 12-fold aggregation. Only quarter is a dependent window dividing year (top-down).

The pivot of this direction is “leap is a value, not a window”. “Does February have 28 or 29 days?” looks like a dependency on the window year, but it is in fact a value dependency computable from the month ordinal m (m → calendar year → isLeap). Hence month need not depend on the year window, and month can stand as the parent (the basic grouping). If month were made a child of year, then when a derivation (the fiscal calendar, §6) re-bundles year from month, month ↔ year would become circular. Viewing leap as a value and placing month as the parent keeps the cycle from ever arising — this is the ground on which the fiscal calendar can be written in one line, with no workaround.

4. weekday (cycle) and WKST are different things

This is where misunderstanding is most likely. The rhythm of weekday labels (weekday) and the start of the week (WKST) are independent, separate concepts: Gregorian carries only the former, never the latter.

Concept What it decides Where it lives Universal or culture-dependent
weekday (cycle) Which day is Mon/Tue/… (the weekday label) Gregorian (calendar-system-pure) Universal (Monday is Monday the world over)
WKST Where the week starts (the week windows’ cut points; the origin of “the Nth week”) A premise member (declared in the preamble) Culture- and use-dependent

4.1 The list order is not “week start”

The definition’s cycle [Mon, Tue, Wed, Thu, Fri, Sat, Sun] is written Monday-first, but it is a cyclic sequence (after Mon comes Tue … after Sun comes Mon). Being circular, its head carries no “week start” meaning. It decides only two things:

Writing [Sun, Mon, …, Sat] with the same anchor yields the identical calendar (the same days carry the same weekday labels). It does not express a week start.

4.2 The start of the week is decided by WKST

“Where the week is cut” is carried by WKST (a premise member of the preamble; a dangerous member = declaration-required leaning, with no language default). The cut points of the week window within(week), and the origin of “the Nth” against that window, depend on WKST (the two-step dependency. ADR-24).

# the same Gregorian (weekday labels unchanged), yet WKST moves the week windows' cut points
@JP wkst: Mon    …|> within(week)…   # weeks run Mon–Sun
@US wkst: Sun    …|> within(week)…   # weeks run Sun–Sat

The weekday labels (which day is Monday) are invariant regardless of WKST. Only the week window’s first day moves.

4.3 The reality of “week start” (why it is not baked into Gregorian)

Week starts split by culture and use; there is no single right answer:

These differences are “conventions for counting weeks on top of the Gregorian calendar”, not the calendar system itself. Hence Gregorian bakes in no week start; it is declared per use as WKST. This is consistent with “WKST is not a two-way choice” (../../design/90-open-questions.md; Saturday-start organizations really exist).

4.4 Examples where WKST does and does not bite

“The Nth week” depends on WKST; “the Nth Monday” (the Nth of that weekday within the month) does not. They are easy to confuse, so the design keeps them separate. For the same reason the sugar nextWeekday(d) (advance to the next d-weekday) is also WKST-independent — its expansion is not a week window but a forward roll (roll(Following, on: the sequence of d-labeled days). spec §4.8).

4.5 The week window — standing on lazy resolution of wkst

week is a public word of Gregorian, yet Gregorian itself knows no week start (§4.3). The definition’s right-hand side references the preamble member wkst: and is lazily resolved in the user side’s in-scope premise (the same rule as sugar definitions):

weekStart = day |> filter(d => weekday(d) == wkst)
week      = day |> segmentBy(weekStart, edges: clip, empties: error)

Using within(week) under a premise with no wkst: declared is a static error — the “declaration-required leaning” governance (ADR-16) applies as-is. Generation uses segmentBy (interval-sequence type), but because the weekday cycle lets exhaustiveness and non-overlap be proven by the I5 check, it can be used with within(week) (partition-hood is established by the check, not by the generating word).

5. Scope (what Gregorian does not bear)

Gregorian is a mathematical model on an idealized continuous time axis (Chronos); it bears no historical facts:

These are not a layer for calendar-system-pure (I8) generation rules to carry. When needed, handle them with a separate premise (a historically attested edition) or with asof/source annotations. The standard Gregorian stays a clean generation rule.

6. A derivation example: Fiscal (the fiscal calendar)

Rearranging only year’s grouping on top of Gregorian yields the fiscal calendar (a premise → premise derivation; details in ../spec/20-premise-layer.md §3.7).

premise Fiscal = Gregorian with {
  year = month span (_ => 12) phase: 3 label: (p => yearNo(p))   # April start; starting-calendar-year label
}

month is untouched, so calendar days and month ends are fixed. quarter’s inherited definition (year split by month) auto-tracks the new year and becomes the fiscal quarters (Apr-Jun/…). Because §3 placed month as the parent (leap is a value), no holding pin such as month = Gregorian.month and no cycle avoidance is needed — the single year line suffices. label: is not inherited on override (it is part of the definition; ADR-42/F96), so if window-instance reference for fiscal years is wanted (year(2026) = the days of fiscal 2026), reattach it explicitly — the rephase expansion preserves the base’s label: (F65), so without the reattachment the equivalence of the two forms breaks.

Fiscal has been promoted to a standalone commentary as a standard premise — the exhaustive account, through fiscal-year numbering, fiscal month numbers, the variants (the US type, half-years), and what is out of reach, is fiscal.md. Likewise derived from Gregorian there is iso-week.md (the ISO week calendar); among calendar systems that cut months with data there is kyureki.md (the Kyureki — Japanese — data-heavy, not mirrored).