Skip to main content

Module temporal

Module temporal 

Source
Expand description

Exact calendar primitives — the bedrock of the time tower.

Everything here is exact integer arithmetic over a single absolute coordinate: the day count since the Unix epoch (1970-01-01). Two calendars are projected onto that coordinate — the proleptic Gregorian calendar (the civil default) and the proleptic Julian calendar (for historical dates and the Gregorian/Julian divergence) — so a date in either calendar is just a different lens on the same day number, and converting between them is lossless.

The Gregorian conversions are Howard Hinnant’s branch-light days_from_civil/civil_from_days (valid for any year, no lookup tables). The Julian conversions go through the Julian Day Number (JDN); the epoch 1970-01-01 is JDN 2440588. ISO-8601 week dates are derived from the day number directly. Weekday is 0 = Sunday … 6 = Saturday; ISO weekday is 1 = Monday … 7 = Sunday.

Structs§

CivilDateTime
A civil (wall-clock) date-time, calendar + time-of-day, with no zone — the human-readable decomposition of a SmoothUTC instant (nanoseconds since the Unix epoch, leap-second-free).
DstRule
A POSIX-style DST transition rule: the week-th weekday of month, at time_seconds local. week is 1..=5 (5 = the last occurrence); weekday is 0 = Sunday … 6 = Saturday. This is the Mm.w.d form a TZ string uses (e.g. M3.2.0 = the 2nd Sunday of March), and it generates one transition per year — enough to express any zone with a regular DST schedule.
EstimateInterval
A bound on a remote node’s current time: it lies somewhere in [lower_nanos, upper_nanos]. The width is the irreducible uncertainty — across a light-delay you can never pin a remote clock tighter than the physics allows. The intersect of two valid bounds is a lattice meet (it never widens), which is what makes a SyncClock conflict-free.
Hlc
A Hybrid Logical Clock timestamp: a physical instant (nanoseconds) plus a logical counter that breaks ties and preserves causality when the physical clock stalls, jumps backward, or two events share a nanosecond. The total order is lexicographic (physical_nanos, logical) — so the derived Ord is the HLC order. This is the causally-consistent timestamp the SyncClock CRDT keys on: it stays monotonic and orders happens-before correctly even across machines whose wall clocks disagree (the bounded-skew / interplanetary case the campaign targets).
SyncClock
A light-cone-aware CRDT clock: a Hybrid Logical Clock (causal order) plus per-node knowledge intervals (light-cone-bounded estimates of each peer’s current time). Its merge is a join over a lattice — max on the HLC, interval intersection per node — so replicas separated by bounded (even interplanetary) delay converge conflict-free, regardless of message order. This realises “spacelike separation = CRDT concurrency”: events with no shared light cone need no total order, only this commutative/associative/idempotent merge.
ZoneSpec
A named time zone: its standard UTC offset, and an optional DST schedule (dst_offset, spring rule, fall rule). A zone with dst = None is a fixed offset (UTC, IST, JST). DST transitions are generated per year from the rules, so a ZoneSpec is tiny — no transition table.
ZoneTransition
A UTC-offset transition: from at_unix_seconds (a UTC instant) onward, the zone’s local time is UTC + offset_seconds. A timezone is a list of these, sorted ascending — the shape every IANA zone reduces to. DST is just two alternating offsets with transitions twice a year.

Enums§

Fold
How to resolve a local civil time that maps ambiguously to UTC: in a DST fold (the hour repeated when clocks fall back, so a local time occurs twice) or a gap (the hour skipped when clocks spring forward, so a local time never occurs). Earlier picks the earlier instant, Later the later one.

Constants§

NANOS_PER_DAY
Nanoseconds in one civil day.
NANOS_PER_SECOND
Nanoseconds in one SI second.
SECONDS_PER_DAY
Seconds in one civil day (SmoothUTC: no leap seconds, so every day is exactly 86 400 s).
TT_MINUS_TAI_NANOS
The fixed TT − TAI offset: Terrestrial Time runs 32.184 s ahead of atomic time, exactly.
UNIX_EPOCH_JDN
The Julian Day Number of the Unix epoch (1970-01-01, Gregorian).

Functions§

add_business_days
Advance a day number by n business days (skipping weekends): n > 0 moves forward, n < 0 backward. Landing rule is “next business day in the direction of travel”, so add_business_days(friday, 1) is the following Monday and add_business_days(monday, -1) is the preceding Friday.
add_span
Add a calendar span (months then days) to a civil date-time — the civil (wall-clock) operation, distinct from adding a physical Duration. Months are applied first with end-of-month clamping (Jan 31 + 1 month = Feb 28/29), then whole days roll over exactly through the day number. The time-of-day is preserved. (Span arithmetic on a zoned value is where it diverges from a physical duration across a DST boundary; in UTC the two agree.)
business_days_between
The count of business days (Mon–Fri) in the half-open interval between two day numbers. Signed by direction (b < a yields a negative count); [a, b) so adjacent days differ by 1.
civil_from_days
The inverse of days_from_civil: the Gregorian (year, month, day) of a day number.
civil_from_unix_nanos
Decompose a SmoothUTC instant (nanoseconds since 1970-01-01T00:00:00) into its civil date-time. Floor division makes pre-epoch instants correct (e.g. -1 ns is 1969-12-31T23:59:59.999999999).
day_of_year
The 1-based day of the Gregorian year (1..=366).
days_from_civil
Days since 1970-01-01 for a Gregorian civil date. Howard Hinnant’s algorithm — exact for any year, month in 1..=12, day in 1..=last_day_of_month.
days_from_iso_week
The inverse of iso_week_from_days: the day number of an ISO-8601 week date (iso_year, week, iso_weekday) (week 1..=53, weekday 1..=7 Monday … Sunday).
days_from_julian
Days since 1970-01-01 for a Julian-calendar date.
dst_transitions_for_year
The two UTC transitions for year of a zone with a regular DST schedule: spring-forward (std_offset → dst_offset) at start, fall-back (dst_offset → std_offset) at end. Each rule’s time_seconds is wall-clock local in the offset in effect just before the transition.
format_duration
Format a physical duration (nanoseconds) as a compact human string — 1h30m, 500ms, 1.5s, 2d, -45m, 0s. Largest unit first; zero components omitted; sub-second fractions trimmed. The inverse of parse_duration. (This is a physical elapsed time, distinct from a calendar Span: it has no months — those aren’t a fixed number of nanoseconds.)
format_rfc3339
Format a SmoothUTC instant as an RFC 3339 / ISO 8601 UTC timestamp (Z zone), e.g. 1970-01-01T00:00:00Z or 2024-03-10T07:30:00.123456789Z. Sub-second precision appears only when nonzero, with trailing zeros trimmed.
format_time_of_day
Format a time-of-day (nanoseconds from midnight) as HH:MM:SS, with a trailing sub-second fraction only when nonzero (trailing zeros trimmed) — e.g. 07:30:45, 16:00:00, 07:30:45.5. The clock face is lossless to the nanosecond; the input is normalised modulo a day so a negative or overflowing value still maps onto [00:00:00, 24:00:00).
format_zoned
Format a UTC instant as the local wall-clock time in a named zone, with its offset — 2024-07-01T08:00:00-04:00 (the timezone-aware “relative read” of an instant). None if the zone is unknown. The space-aware/zoned generalisation of format_rfc3339.
from_local
The UTC instant of a local civil date-time in a zone. A local time normally maps to exactly one instant; in a DST fold it maps to two (resolved by fold); in a DST gap it maps to none (the boundary instants are returned, fold choosing which). This is the offset-dependent inverse of to_local, solved by testing each candidate offset for self-consistency.
is_gregorian_leap
True if year is a leap year in the proleptic Gregorian calendar (every 4th year, except centuries, except every 400th).
is_julian_leap
True if year is a leap year in the proleptic Julian calendar (every 4th year, no century exception).
is_weekend
True if a day number falls on a weekend (Saturday or Sunday).
iso_week_from_days
The ISO-8601 week date of a day number: (iso_year, week, iso_weekday), with week in 1..=53 and iso_weekday in 1..=7 (Monday … Sunday). The ISO year can differ from the civil year near January 1 / December 31 (e.g. 2021-01-01 is ISO 2020-W53-5).
iso_weekday_from_days
The ISO-8601 weekday of a day number: 1 = Monday … 7 = Sunday.
jdn_from_julian
The Julian Day Number of a Julian-calendar date (proleptic, month in 1..=12).
julian_from_days
The inverse: the Julian-calendar (year, month, day) of a day number.
julian_from_jdn
The inverse of jdn_from_julian: the Julian-calendar (year, month, day) of a JDN.
knowable_horizon
The freshest remote time you can possibly know: an event happening at a node light_delay_nanos away cannot be observed before now − light_delay. This is the light-cone horizon — the reason a distributed clock’s knowledge is principled-uncertain rather than sloppy.
last_day_of_month
The last day (28–31) of a Gregorian month.
local_instant_nanos
The local-as-UTC instant of a UTC instant in a named zone: the nanoseconds that, read back with the plain UTC calendar functions, yield the zone’s local wall-clock components. This is the lowering target for zoned component reads (the hour of m in "Asia/Tokyo"): a local clock face encoded as an instant, so every UTC extractor composes onto it unchanged. None if the zone is unknown. (Not a real instant — it is the wall clock; do not re-format it with a Z.)
months_between
The number of complete calendar months from instant a to instant b (signed; negative when b precedes a). A month is complete only when b reaches at least a + k months under the same end-of-month-clamping, time-of-day-preserving rule as add_span — so Jan 15 → Mar 14 is 1 month (not 2), Jan 31 → Feb 28 (non-leap) is a full month, and one hour short of a month is 0.
nth_weekday_of_month
The day-of-month of the week-th weekday of (year, month) — e.g. the 2nd Sunday of March, or (with week = 5) the last Sunday of October. week is 1..=5, weekday 0 = Sun … 6 = Sat.
offset_at
The UTC offset (seconds) a zone has at instant_ns. transitions must be sorted ascending by at_unix_seconds; base_offset applies before the first transition.
parse_duration
Parse a physical duration string into nanoseconds — 1h30m, 90m, 1.5s, 500ms, 2d, -1h, 1d2h3m4s. Units: d h m s ms us/µs ns. None on malformed input or i64 overflow.
parse_rfc3339
Parse an RFC 3339 / ISO 8601 timestamp into a SmoothUTC instant (nanoseconds since epoch). Accepts a Z zone or a numeric ±HH:MM offset (normalized to UTC); None on malformed input.
tai_minus_utc
TAI − UTC (whole seconds) in effect at a SmoothUTC/Unix instant — the count of leap seconds (plus the 1972 base of 10) inserted on/before it. Constant between leap seconds; 10 before 1972.
tai_nanos_from_unix_nanos
Convert a SmoothUTC/Unix instant in nanoseconds to TAI nanoseconds (leap-second exact).
tai_to_unix_seconds
The inverse: TAI seconds back to a SmoothUTC/Unix instant. The offset is monotone in UTC, so a first guess (tai − offset(tai)) corrected against offset(guess) converges in one step.
to_local
The local civil date-time of a UTC instant in a zone (apply the offset in effect).
tt_nanos_from_unix_nanos
Convert a SmoothUTC/Unix instant in nanoseconds to Terrestrial Time (TT) nanoseconds: TT = TAI + 32.184 s — the continuous astronomical scale ephemerides are tabulated against.
unix_nanos_from_civil
The inverse: the SmoothUTC instant (nanoseconds since the epoch) of a civil date-time.
unix_nanos_from_tt_nanos
The inverse: TT nanoseconds back to a SmoothUTC/Unix instant in nanoseconds.
unix_to_tai_seconds
Convert a SmoothUTC/Unix instant (seconds) to TAI (atomic time) seconds: TAI = UTC + (TAI−UTC).
weekday_from_days
The weekday of a day number: 0 = Sunday … 6 = Saturday. (1970-01-01 was a Thursday = 4.)
years_between
The number of complete calendar years from a to b (signed) — complete 12-month periods, so 2020-06-01 → 2024-03-01 is 3 (the fourth year, ending 2024-06-01, has not arrived).
zone_by_name
Look up a ZoneSpec by IANA name (e.g. "America/New_York", "UTC", "Australia/Sydney"). None for an unknown zone. A growable registry seeded with common zones (POSIX-rule based); the full IANA set ingests later.