RFD 0047 — The temporal value library, and the value/ontology boundary
- State: discussion
- Depends on: RFD 0043 (theory packages + the neutrality boundary —
stdreflects, 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/Durationare primordials carrying ISO text, lowered inoxc-instantiate(expr_lower.rs); the runtime stores the string and compares it. Nochrono/jiff/temporal_rsdependency exists.std::datetimeis 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::temporalis 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.timepitfalls: 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) andjiff(BurntSushi, Temporal-inspired, stability-first API).temporal_rsis, notably, the crate whose version drift broke a local build during this work. - Doctrine (RFD 0043):
stdmay reflect a commitment the substrate already made, never smuggle one it withholds. A temporal value makes no ontological commitment — so it belongs instd, free ofufo.
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-levelCalendarsystem (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 aufo-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 forceufointostdand make trivial date math a reasoning problem.
Consequences
- The
Date/DateTime/Durationprimordials are enriched with runtime intrinsics backed by the chosen Rust impl; new value types (Instant,ZonedDateTime,TimeZone, …) are added tostd::datetime. std::datetimegrows 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 onstd::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
jiffvstemporal_rsas the intrinsic backing (leanjifffor stability; both expose a TC39-Temporal-shaped API).- v1 surface scope — the full Temporal type set, or
Instant/PlainDate/PlainDateTime/Duration/ZonedDateTimefirst andTimeZone/non-ISO calendars later. - Relation to the bitemporal substrate (
tx_from/tx_to,as_of N) — those extents should bestd::datetimeinstants; confirm the wiring. - Whether
TimeIntervalstays astd::datetimevalue type, with the reified-individual form a separate ontology-layer concept (D2/D5).