Skip to content
docs
Arxo ↗

Time and calendars

For LLMs11 sections

Every legal answer holds as of a date. This stage dates the norms (when each rule takes part), dates the case facts (when each statement holds), and stamps every scenario with the four axes the machine reads instead of the system clock. It sits between rule drafting and scenario writing: rules name their windows, this stage fixes the contexts scenarios run under, and review replays them.

  • The act’s entry-into-force provisions and any later amendments with their dates.
  • The pinned edition dates: adoption and force interval where the act states them.
  • The case event dates: when the loss occurred, when payment fell due, when delay began.
  • The forum timezone, written as a zone name such as Asia/Almaty, never as a bare offset.
  • Stamp every scenario with all four axes: legal_time (the date whose law applies), decision_time (when the answer is issued), knowledge_time (the boundary of known data), and timezone.
  • Date each norm once, in one place: either a window on the rule for norms that enter into force apart from the act, or the force interval of the edition the rule’s source anchor points to.
  • Compare event dates in the rule body, against the fact’s own date — never against legal_time.
  • Keep kinds apart: dates compare with dates, instants with instants; the machine refuses mixed comparisons with LDC-E2108.
  • Never read the system clock: a scenario without explicit axes is incomplete, not current.
  • One window per rule: a second window clause in the same block is refused (LDC-E1329), so overlapping force periods must be modeled as separate rules or separate editions.
  • Rule window or edition dating: a lone article with its own commencement date earns a window on its rule; an act dated as a whole is dated by its edition, and a spare window on the rule will sooner or later drift from the edition interval.
  • Date or instant thresholds: write the threshold in the kind of the fact it meets.

The artifact is a time matrix plus boundary checks. The matrix lists every dated element of the package, names the axis that filters it, and states the silent-answer symptom when the filter removes it:

Dated elementFiltering axisSymptom when filtered out
Rule windowlegal_timethe rule takes no part; the answer stays undecided
Edition force intervallegal_timeanchored rules take no part
Fact validity windowlegal_timethe fact is excluded from supports
Event-date comparison in a rule bodynone — the fact’s own date decidesthe rule stays silent on non-matching facts
Answer moment and data boundarynone — they stamp the answernot applicable

Boundary checks are scenario pairs that differ only in legal_time: one date inside each window, one outside. Each pair pins the reading that prose alone leaves open.

The running example is time-flat, and the matrix says so explicitly. Package kz.corpus.employee_accident_insurance, version 0.1.0, language 0.2: all seven scenarios share one context, with answer moment and data boundary at 2026-09-13T12:00+05:00, law date 2026-09-13, zone Asia/Almaty:

Arxo Law
context {
decision_time @2026-09-13T12:00:00+05:00;
knowledge_time @2026-09-13T12:00:00+05:00;
legal_time @2026-09-13;
timezone "Asia/Almaty";
}

No rule carries its own window; the pinned edition EAI_EDITION carries an adoption date but no force interval; no case fact carries a validity window; no rule compares an event date. Materialization is PINNED_UNOFFICIAL_COPY — an Adilet API copy retrieved 2026-09-13, sha256 pinned, local copy kept in the package. Per path, each axis filters nothing here: legal_time admits every rule and every fact on any date, while the answer moment and data boundary only stamp the run. That flatness is a matrix entry, not an oversight: the duty, tariff, payout, and penalty branches of the four modeled articles carry no commencement split in the pinned edition, so every matrix row reads absent — and any amendment with its own commencement date reopens this page.

Read the contract of that flatness narrowly. The absence of a separate commencement for four modeled articles does not establish that the edition as a whole has no temporal boundary: the act may have entered into force on a date the pinned copy does not state per article, and unmodeled articles may carry their own windows. The running example is a snapshot of the model under one law date, not a historical choice of applicable law — it answers “what follows from these rules on 2026-09-13”, never “which edition governed this event”. A case with an event date outside the retrieval date needs the edition’s own force interval first; this page’s matrix has no row to give it.

The costly confusion is reading decision_time as the law selector: an answer issued this year about last year’s event still counts under last year’s law. Only legal_time selects norms. The second confusion is a dateless edition: an edition without a force interval dates nothing, so rules anchored to it float free of legal_time — the suite stays green on any date, which looks like stability and is actually silence about time. Name it in the matrix either way.

The stage is done when the check passes, all seven scenarios pass, and the matrix covers every dated element including the absent ones. Observed with tool version law 0.1.0, semantics law.core/0.2:

Output
$ law engine check corpus/laws/kz/laws/employee-accident-insurance
check OK: corpus/laws/kz/laws/employee-accident-insurance
$ law test corpus/laws/kz/laws/employee-accident-insurance
ok [kz.corpus.employee_accident_insurance#authored] tests/core.lawtest / EAI-EMPLOYER-MUST-INSURE
ok [kz.corpus.employee_accident_insurance#authored] tests/core.lawtest / EAI-MINING-CLASS-22-PREMIUM
ok [kz.corpus.employee_accident_insurance#authored] tests/core.lawtest / EAI-LOSS-THIRTY-QUALIFIES
ok [kz.corpus.employee_accident_insurance#authored] tests/core.lawtest / EAI-LOSS-TWENTY-NINE-IS-NOT-INSURER-PAYOUT
ok [kz.corpus.employee_accident_insurance#authored] tests/core.lawtest / EAI-SPECIAL-LATE-PAYMENT-PENALTY
ok [kz.corpus.employee_accident_insurance#authored] tests/goals.lawtest / GoalEstablishedCapacityLossHasPayout-world
ok [kz.corpus.employee_accident_insurance#authored] tests/goals.lawtest / urn:kz:corpus:clir:employee-accident-insurance#GoalEstablishedCapacityLossHasPayout

Criterion: rerun the suite with legal_time moved to a second date and confirm the matrix predicts the outcome row by row — for the running example, identical results, because the matrix claims flatness. Any divergence means a hidden window the matrix missed.

The running example proves flatness; it cannot prove that windows work. The executable half of this stage is the time drill — a synthetic package with two windowed rules over one question and one counted term with a pinned calendar. Observed with the same tool version:

Output
$ law test docs/handbook/files/fixtures/time-terms
law test demo.handbook.time_terms: world demo.handbook.time_terms
ok [demo.handbook.time_terms#authored] tests/window.lawtest / inside the old window
ok [demo.handbook.time_terms#authored] tests/window.lawtest / inside the new window
ok [demo.handbook.time_terms#authored] tests/window.lawtest / before every window stays silent
ok [demo.handbook.time_terms#authored] tests/window.lawtest / decision moment does not select the law
ok [demo.handbook.time_terms#authored] tests/deadline.lawtest / thirty calendar days from filing
ok [demo.handbook.time_terms#authored] tests/deadline.lawtest / function form counts one day later (TIME-F1, observed)
ok [demo.handbook.time_terms#authored] tests/deadline.lawtest / no policy means no counted date
total: 7 checked, 7 passed, 0 failed, 0 not run; code 0

Each scene was inspected in the suite files; the setups and expectations read:

SceneSetupExpects
inside the old windowlegal_time 2024-06-01, registeredTRUE_ONLY, OldTerm applied, NewTerm excluded
inside the new windowlegal_time 2025-06-01, registeredTRUE_ONLY, NewTerm applied, OldTerm excluded
before every window stays silentlegal_time 2023-06-01, registeredNEITHER, neither rule applied
decision moment does not select the lawlegal_time 2024-06-01, decision_time 2026TRUE_ONLY via OldTerm
thirty calendar days from filingfiled 2026-02-01, policy selected2026-03-03, derived by hand from the policy
function form counts one day latersame inputs, add_calendar_period2026-03-04, observed (TIME-F1, open)
no policy means no counted datesame inputs, no policyMISSING_POLICY

Two facts deserve emphasis. First, the applied expectations pin which rule fired: the window pair proves the boundary moves the derivation, not just the answer. Second, the two deadline entry points disagree by one day on identical inputs — the term counts to the hand-derived 2026-03-03, the function form to 2026-03-04 — and pinning the calendar changes nothing for the function form. That disagreement is recorded as open finding TIME-F1 with an observed expectation, in the same style as the compatibility case on the coverage page: the drill proves counting runs, and it also proves the two ways of counting do not agree.

The calendar pin is enforced, not decorative: flipping one byte of the resource copy makes the run refuse before evaluation with a contentHash ... diverges message naming both hashes (probed on a scratch copy, whole suite SKIP, code 2).

This page covers dating fully and counting for calendar days; working-day counting is the stated profile limit. The drill counts thirty calendar days on a synthetic Mon–Fri calendar, but no scene counts business days against an official calendar — add_business_days without a calendar ends in MISSING_INPUT, and the bundle ships no official calendar to select. A deadline reading that depends on working days is therefore provisional by construction: record its policy openly and do not claim a verified count. The running example sets no deadlines at all: the penalty counts plain days of delay from a fed fact, so no calendar enters. A matrix row marked absent is a promise to recheck on amendment, not proof the act is silent forever.

Continue with Calculations and external inputs, which fixes money, rounding, and table policy for the amounts dated here.

Documentation for Arxo. Writings — blog.arxo.io.

Anonymous visit counts on stats.arxo.io, no cookies.