Wado
<!-- Auto-generated by: wado doc -f markdown core:temporal --> <!-- Do not edit this file directly. -->

core:temporal

Minimal date and time types, modelled on TC39 Temporal.

This is the MVP described in docs/wep-2026-06-05-core-temporal.md: it provides only the two value types every temporal API is built around, an exact point on the timeline (Instant) and that instant paired with a time-zone interpretation (ZonedDateTime). They carry ISO 8601 / RFC 3339 formatting (Display), civil field accessors, RFC 3339 parsing, epoch constructors, and serde Serialize/Deserialize (an RFC 3339 string under CBOR's date/time tag 0, a bare string in JSON; an epoch-seconds number is also accepted on decode); arithmetic, now(), and Duration are deferred to follow-up work. The types exist so a serde format such as core:json or core:cbor has a concrete Wado type for timestamps.

Relationship to wasi:clocks: wasi:clocks exposes its own Instant record ({ seconds, nanoseconds }) for the system clock. That type is a Component Model binding — pinned to a specific WASI version and regenerated by wado-from-idl — whereas core:temporal's Instant is a plain, version-independent Wado type that grows methods. They share a name and field layout deliberately: it is "the same concept, a different type". To bridge them without making either depend on the other's representation, core:temporal provides From impls both ways (a field-for-field copy), so a clock reading converts with Instant::from(SystemClock::now()) and back with ClockInstant::from. Merely naming the wasi:clocks record in these impls is not a runtime use of the clock, so it pulls in no WASI import for a component that never calls one.

Both core:temporal types are ISO 8601 only and so store no calendar.

Synopsis

let epoch = Instant::from_epoch_seconds(0);
assert `{epoch}` == "1970-01-01T00:00:00Z";
assert epoch < Instant::from_epoch_seconds(1_000_000_000);

assert ZonedDateTime::parse_rfc3339("2023-11-14T22:13:20+09:00") matches { Ok(zoned) && zoned.hour() == 22
    && `{zoned}` == "2023-11-14T22:13:20+09:00" };

Structs

pub struct Instant

An exact point on the timeline, as the offset from the Unix epoch (1970-01-01T00:00:00Z). Time-zone- and calendar-independent.

Corresponds to Temporal.Instant and the wasi:clocks instant record. Where Temporal stores epochNanoseconds as a BigInt, this uses an i64 second count (≈ ±292 billion years, far beyond Temporal's ±273,790-year range) plus a u32 sub-second part for full nanosecond resolution — the exact shape of wasi:clocks instant.

Ord is auto-derived and orders chronologically: seconds is compared first, then nanoseconds.

seconds: i64

Whole seconds since the Unix epoch. Negative values are before it.

nanoseconds: u32

Sub-second component, always in 0..1_000_000_000. Incrementing nanoseconds always moves forward in time, even when seconds < 0 (e.g. one nanosecond before the epoch is Instant { seconds: -1, nanoseconds: 999_999_999 }). Use Instant::new to normalize an arbitrary count into this range; the accessors assume it.

pub fn new(seconds: i64, nanoseconds: i64) -> Instant

Construct an instant nanoseconds after seconds since the Unix epoch, normalizing so the stored sub-second part lands in 0..1_000_000_000 even when nanoseconds is negative or >= 1_000_000_000. This is the canonical funnel that upholds the field invariant the accessors rely on.

pub fn from_epoch_seconds(seconds: i64) -> Instant

Construct an instant from whole seconds since the Unix epoch (CBOR tag 1 integer form).

pub fn from_epoch_milliseconds(milliseconds: i64) -> Instant

Construct an instant from milliseconds since the Unix epoch. The inverse of epoch_milliseconds.

pub fn from_epoch_nanoseconds(nanoseconds: i64) -> Instant

Construct an instant from nanoseconds since the Unix epoch. The inverse of epoch_nanoseconds, and like it limited to the i64 range (≈ ±292 years).

pub fn from_unix_seconds(seconds: f64) -> Instant

Construct an instant from fractional seconds since the Unix epoch (CBOR tag 1 floating-point form). The fraction is rounded to the nearest nanosecond; f64 precision limits sub-microsecond accuracy near the present.

pub fn epoch_milliseconds(&self) -> i64

Milliseconds since the Unix epoch (Temporal.Instant.epochMilliseconds).

pub fn epoch_nanoseconds(&self) -> i64

Nanoseconds since the Unix epoch (Temporal.Instant.epochNanoseconds).

Temporal uses a BigInt; this returns an i64 and so is only valid within ≈ ±292 years of the epoch (1678..=2262) — outside that range the multiplication traps. Use the seconds/nanoseconds fields directly for the full range.

pub fn to_rfc3339(&self) -> String

RFC 3339 / ISO 8601 string in UTC (…Z), using the least sub-second precision (0, 3, 6, or 9 fraction digits) that represents the instant exactly. This is the serde wire form.

impl Display for Instant

fn fmt(&self, f: &mut Formatter)

impl DisplayAlt for Instant

fn fmt_alt(&self, f: &mut Formatter)

impl Serialize for Instant

fn serialize<S: Serializer>(&self, s: &mut S) -> Result<(), SerializeError>

impl Deserialize for Instant

fn deserialize<D: Deserializer>(d: &mut D) -> Result<Instant, DeserializeError>

impl From<ClockInstant> for Instant

fn from(value: ClockInstant) -> Instant

pub struct ZonedDateTime

An exact instant together with the time zone it is interpreted in.

Corresponds to Temporal.ZonedDateTime — the only "complete" temporal value (an instant plus a wall-clock interpretation). The calendar is always ISO 8601 and therefore not stored. The broken-down wall-clock fields (year, month, day, hour, …) are a function of instant + time_zone, computed on demand by accessors rather than stored state.

Ord is auto-derived and orders by instant first, then time_zone lexically. That is not a meaningful chronological order across different zones beyond the instant; ordering distinct zones is the caller's concern.

instant: Instant

The exact instant.

time_zone: String

IANA time-zone identifier (e.g. "America/New_York", "UTC") or a fixed UTC offset (e.g. "+09:00"). Mirrors the Temporal time-zone slot, which is also a string after the removal of Temporal.TimeZone. Only "UTC"/"Z" and fixed ±HH:MM offsets are interpretable today; every operation traps on an IANA name until a tz database is bundled.

pub fn parse_rfc3339(text: String) -> Result<ZonedDateTime, DeserializeError>

Parse an RFC 3339 / ISO 8601 timestamp such as "2023-11-14T22:13:20.5+09:00" or "1970-01-01T00:00:00Z", preserving the offset as a fixed-offset zone (so it round-trips through to_rfc3339 / Display in canonical form).

The grammar is [±]Y…Y-MM-DDThh:mm:ss[.fraction](Z|±hh:mm): the year is four or more digits with an optional sign (ISO 8601 extended years), the date/time separator may be T, t, or a space, the fraction is truncated to nanosecond precision, and the offset is mandatory. "Z" and "+00:00" both normalize to a "Z" zone. Field values are range-checked against the ISO 8601 calendar (so e.g. "2023-02-29" and "…T24:00:00Z" are rejected); malformed or out-of-range input returns a DeserializeError carrying the byte offset of the problem.

pub fn to_rfc3339(&self) -> String

RFC 3339 / ISO 8601 string in this zone's local time, with the offset suffix (or Z) and the least sub-second precision that is exact. This is the serde wire form. Traps on an IANA zone name (offset resolution is future work).

pub fn year(&self) -> i32

ISO year in the zone's local time. May be negative or beyond four digits.

pub fn month(&self) -> i32

Month of the year, 1..=12.

pub fn day(&self) -> i32

Day of the month, 1..=31.

pub fn hour(&self) -> i32

Hour of the day, 0..=23.

pub fn minute(&self) -> i32

Minute of the hour, 0..=59.

pub fn second(&self) -> i32

Second of the minute, 0..=59.

pub fn millisecond(&self) -> i32

Millisecond of the second, 0..=999.

pub fn microsecond(&self) -> i32

Microsecond of the millisecond, 0..=999.

pub fn nanosecond(&self) -> i32

Nanosecond of the microsecond, 0..=999.

pub fn day_of_week(&self) -> i32

Day of the week, 1 (Monday) through 7 (Sunday), per ISO 8601.

pub fn day_of_year(&self) -> i32

Day of the year, 1..=366.

pub fn days_in_month(&self) -> i32

Number of days in this month, 28..=31.

pub fn days_in_year(&self) -> i32

Number of days in this year, 365 or 366.

pub fn in_leap_year(&self) -> bool

Whether this year is a leap year in the ISO 8601 calendar.

pub fn months_in_year(&self) -> i32

Number of months in this year — always 12 for ISO 8601.

impl Display for ZonedDateTime

fn fmt(&self, f: &mut Formatter)

impl DisplayAlt for ZonedDateTime

fn fmt_alt(&self, f: &mut Formatter)

impl Serialize for ZonedDateTime

fn serialize<S: Serializer>(&self, s: &mut S) -> Result<(), SerializeError>

impl Deserialize for ZonedDateTime

fn deserialize<D: Deserializer>(d: &mut D) -> Result<ZonedDateTime, DeserializeError>