Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

RFD 0047 — The temporal value library, and the value/ontology boundary

  • State: discussion
  • Depends on: RFD 0043 (theory packages + the neutrality boundary — std reflects, never smuggles, commitment), RFD 0044 (packages — how a vendored backing is distributed)
  • Relates to: std::temporal (the DatalogMTL operators, §17 — a distinct namespace, not this), std::datetime (the existing neutral value layer this enriches)

Question

What is Argon’s standard date/time support, and where is the line between a temporal value (a thing you compute with) and a temporal commitment (a thing the ontology is about)?

Today the temporal layer is ad-hoc: Date/DateTime/Duration are opaque ISO-text literal primordials (Literal::Date("2026-01-01")) with no Rust temporal library behind them — no arithmetic, no time zones, no parsing/formatting, no calendar math, only < comparison. std::datetime (174 lines) builds the Allen interval calculus + a TimeInterval value on top of that thin base. Domain ontologies then re-implement TimeInterval/AllenRelationType/AllenHolds again (the sharpe-ontology common/datetime duplicates std::datetime verbatim). The result: every temporal need re-derives the same primitives over string-shaped dates, and anything beyond < (durations, time zones, “the third Tuesday”, DST) is unavailable.

Context

  • Date/DateTime/Duration are primordials carrying ISO text, lowered in oxc-instantiate (expr_lower.rs); the runtime stores the string and compares it. No chrono/jiff/temporal_rs dependency exists.
  • std::datetime is the ontology-neutral value home: TimeInterval { startsOn: Date, endsOn: Date }, AllenRelationType, the thirteen Allen derives, AllenHolds. Correct as far as it goes; starved of a real value layer beneath it.
  • std::temporal is a different thing — the reserved namespace for the DatalogMTL convenience operators (ever/always/since_event/…, §7.3.2), behind the V1 macro system. The temporal logic plane. Do not conflate it with the value library; do not reuse its name.
  • TC39 Temporal is the modern, settled design for date/time values (it fixed the legacy Date/Joda/java.time pitfalls: no ambiguous “month 0”, explicit time-zone vs wall-clock, immutable, calendar-aware). Two mature Rust implementations track it: temporal_rs (the Boa reference impl, spec-faithful, churns) and jiff (BurntSushi, Temporal-inspired, stability-first API). temporal_rs is, notably, the crate whose version drift broke a local build during this work.
  • Doctrine (RFD 0043): std may reflect a commitment the substrate already made, never smuggle one it withholds. A temporal value makes no ontological commitment — so it belongs in std, free of ufo.

Decision

D1 — Adopt the TC39 Temporal model for Argon’s temporal value layer

Argon’s temporal values are the Temporal type set, ontology-neutral, in std::datetime (the existing neutral home, enriched — not a new namespace):

  • Instant (a fixed point on the timeline, UTC), Duration (calendar-aware span);
  • PlainDate, PlainTime, PlainDateTime (wall-clock, no zone);
  • ZonedDateTime (instant + TimeZone + calendar);
  • TimeZone, and a value-level Calendar system (ISO-8601/Gregorian arithmetic — leap years, month lengths);
  • the operations: construction, parse/format (ISO-8601), comparison, arithmetic (add/subtract/until/since/round), field access.

The existing TimeInterval + Allen calculus stays, now expressed over these richer values rather than bare ISO strings.

D2 — The value/ontology boundary (the rule)

A temporal value — an instant, a date, a duration, a zone, a calendar system — is ontology-neutral and lives in std. Temporal ontology — a calendar as a social artifact, a reified interval that mediates individuals, time-indexed facts — is committed and lives in a ufo-based package.

A PlainDate is a value like an Int; you do not ontologically commit to “2026-06-24”. The Gregorian calendar as ISO arithmetic is a value-level Calendar system (std::datetime); the Gregorian calendar as a NormativeDescription a society adopts is a domain kind (the ontology layer). Both exist, separated by this line. Not everything temporal needs ontological commitment — most of it is values.

D3 — Runtime-back the primitives; do not reimplement temporal math

The Date/DateTime/Duration/… primordials gain a real backing: their arithmetic, parsing, formatting, time-zone resolution, and calendar math are runtime intrinsics that delegate to a vendored Rust Temporal implementation — not hand-rolled, not computed in Argon source. Calendar and time-zone arithmetic is a notorious correctness sink; we consume a spec-tracking library, we do not author one.

Backing choice: temporal_rs is spec-faithful but churns; jiff is stability-first. For a runtime intrinsic that must pin to a stable API, lean jiff — but the Argon surface is TC39 Temporal regardless of which backs it, so this is an isolated, reversible impl decision (kept behind the intrinsic boundary). Pin it deliberately (RFD 0044 distribution).

D4 — Two temporal namespaces, kept distinct

std::datetime = the value library (D1). std::temporal = the DatalogMTL operators (§17). They compose (an MTL operator ranges over events stamped with std::datetime instants) but are different layers — the value algebra vs. the temporal logic. No rename; this RFD only clarifies the boundary.

D5 — Domain temporal layers depend on std::datetime, never re-implement it

A domain/ontology package needing time uses std::datetime for values + the Allen calculus, and adds only commitment on top (e.g. Calendar <: NormativeDescription, a reified TimeInterval-as-individual when it must mediate). The sharpe-ontology common/datetime duplication of TimeInterval/AllenRelationType/AllenHolds collapses to a re-export of / dependency on std::datetime.

Rationale

The current ISO-text primordials are a floor, not a library — anything past < is absent, and every consumer re-derives the same intervals. TC39 Temporal is the one date/time model worth standardizing on (it is the lesson learned from every prior date/time API), and two Rust impls already exist, so the cost is binding, not authoring. The value/ontology split (D2) is forced by the neutrality doctrine and by common sense: a date is a value; a calendar-as-institution is a commitment. Keeping the math in a vendored intrinsic (D3) avoids the single most error-prone thing a language can try to write itself.

Alternatives

  • Keep the ad-hoc ISO-text primordials. Rejected — no arithmetic/zones/calendars; every domain re-derives intervals over strings; correctness hazards (DST, leap) unaddressed.
  • Author temporal math from scratch (Argon or hand-rolled Rust). Rejected — calendar/time-zone arithmetic is a correctness sink with a maintained standard impl available.
  • Model everything temporal ontologically (ufo-committed dates). Rejected — a value is not a commitment; it would force ufo into std and make trivial date math a reasoning problem.

Consequences

  • The Date/DateTime/Duration primordials are enriched with runtime intrinsics backed by the chosen Rust impl; new value types (Instant, ZonedDateTime, TimeZone, …) are added to std::datetime.
  • std::datetime grows from a 174-line Allen layer into the Temporal value library; its Allen calculus re-expresses over the richer values.
  • Domain temporal duplication (sharpe-ontology common/datetime) is removed in favor of depending on std::datetime.
  • A new vendored runtime dependency (jiff/temporal_rs), pinned; the only place temporal math lives.
  • std::temporal (MTL) is unaffected beyond the documented boundary.

Open questions

  • jiff vs temporal_rs as the intrinsic backing (lean jiff for stability; both expose a TC39-Temporal-shaped API).
  • v1 surface scope — the full Temporal type set, or Instant/PlainDate/PlainDateTime/Duration/ZonedDateTime first and TimeZone/non-ISO calendars later.
  • Relation to the bitemporal substrate (tx_from/tx_to, as_of N) — those extents should be std::datetime instants; confirm the wiring.
  • Whether TimeInterval stays a std::datetime value type, with the reified-individual form a separate ontology-layer concept (D2/D5).