RFC 058: std.datetime — temporal values, intervals, and runtime timing¶
- Status: Implemented
- Created: 2026-04-14
- Author(s): Danny Meijer (@dannymeijer)
- Related:
- RFC 022 (namespaced stdlib modules and compiler handoff)
- RFC 023 (compilable stdlib and Rust module binding)
- RFC 055 (
std.fspath-centric filesystem APIs)
- Issue: https://github.com/dannys-code-corner/incan/issues/292
- RFC PR: —
- Written against: v0.2
- Shipped in: v0.3
Summary¶
This RFC proposes std.datetime as Incan's standard library home for deterministic temporal values. The module covers runtime timing (Duration, Instant, SystemTime), civil and analytics-facing temporal values (Date, Time, DateTime), fixed UTC offsets (FixedOffset, DateTimeOffset), and first-class interval types (TimeDelta, YearMonthInterval, DateTimeInterval). The design intentionally combines a Rust std::time-backed runtime timing model with a more analytics- and Substrait-shaped civil time model so Incan has one coherent temporal vocabulary for scheduling, timestamps, temporal arithmetic, and data-facing date or time work without requiring application code to drop into Rust or treat time as raw strings and integers.
Motivation¶
Time is a foundational hole in the current stdlib surface. Programs need to measure elapsed work, represent timestamps, parse and format calendar values, and perform date arithmetic in analytics pipelines. Today, that story is fragmented: elapsed-time measurement pushes users toward Rust interop, while calendar-style dates, datetimes, and intervals have no clear standard home at all.
This matters across a broad slice of Incan code:
- CLIs, services, and workflow systems need durations, deadlines, and retry intervals.
- Analytics and ETL work need dates, datetimes, and interval arithmetic that are honest about month- and year-based semantics.
- Filesystem and process APIs eventually need a shared temporal vocabulary for metadata, timeouts, and scheduling.
- Tests need stable construction, parsing, and comparison helpers.
std.datetime should therefore give Incan one coherent temporal vocabulary instead of a mix of strings, integers, and Rust escape hatches.
Goals¶
- Provide a standard runtime timing story with
Duration,Instant, andSystemTime. - Provide a standard civil and analytics-facing temporal story with
Date,Time, andDateTime. - Provide fixed UTC offset values and fixed-offset datetimes for ISO
%z/Zdata. - Provide first-class interval types for analytics and temporal arithmetic rather than flattening everything into one duration-shaped abstraction.
- Support arithmetic, comparison, parsing, and formatting for the committed temporal types.
- Provide UTC-only civil host-clock factories while leaving local and named-zone host-clock semantics to packages.
- Keep the user-facing surface Incan-first while using Rust
std::timefor platform-owned runtime timing primitives. - Keep named timezone rule databases out of the core standard library so richer timezone support can live in separately versioned packages.
Non-Goals¶
- Defining cron-like recurrence rules or workflow scheduler DSLs in this RFC.
- Standardizing named IANA timezone-database support in the core standard library.
- Providing locale-sensitive formatting as the primary formatting model.
- Replacing domain-specific time libraries that may later exist for finance, calendars, or recurrence.
Guide-level explanation¶
Authors should be able to use std.datetime for both elapsed-time measurement and data-facing temporal work.
from std.datetime import Duration, Instant
start = Instant.now()
run_job()?
elapsed = start.elapsed()
if elapsed > Duration.seconds(5):
println("job was slow")
from std.datetime import DateTimeOffset, FixedOffset
offset = FixedOffset.hours(1)?
created_at = DateTimeOffset.fromisoformat("2026-04-14T12:34:56+01:00")?
parsed = DateTimeOffset.strptime("2026-04-14T12:34:56.123456789+01:00", "%FT%T.%f%:z")?
if parsed < created_at:
println(parsed.isoformat())
Named timezones remain a package-level concern. A later package could expose a surface shaped like:
from pub::timezones @ 0.1 import TimeZone
zone = TimeZone.named("Europe/Amsterdam")?
from std.datetime import Date, TimeDelta, YearMonthInterval
anchor = Date.strptime("2026-04-14", "%Y-%m-%d")?
next_week = anchor + TimeDelta.days(7)
quarter_end = anchor + YearMonthInterval.months(3)
The mental model should be simple:
- use
Duration,Instant, andSystemTimefor runtime timing and deadlines; - use
Date,Time, andDateTimefor civil timestamp work; - use
FixedOffsetandDateTimeOffsetfor concreteZ,+HHMM, and+HH:MMoffset data; - use interval types for analytics-facing temporal arithmetic.
Named timezone rule lookup belongs in separately versioned packages layered on this deterministic core.
Reference-level explanation¶
Module scope¶
std.datetime is the standard library home for Incan temporal values. The committed family includes:
DurationInstantSystemTimeDateTimeDateTimeFixedOffsetDateTimeOffsetTimeDeltaYearMonthIntervalDateTimeInterval
There is no separate std.time module in this design. Runtime timing and calendar-facing time belong in one coherent namespace because users need them together.
Semantic families¶
The module distinguishes three semantic families:
- runtime timing values for elapsed measurement and machine-clock work:
Duration,Instant,SystemTime - civil values for dates, times, and naive timestamps:
Date,Time,DateTime - analytics-facing interval values for temporal arithmetic:
TimeDelta,YearMonthInterval,DateTimeInterval
That split is normative. The public contract must not blur fixed elapsed time, civil timestamps, and calendar-relative intervals into one mushy abstraction.
Timestamp model¶
The timestamp split is explicit:
DateTimeis a naive datetime with no timezone or offset attached.FixedOffsetis a deterministic UTC offset value, restricted to whole-minute offsets within one day.DateTimeOffsetis a naive datetime paired with a fixed offset. It represents concrete offset data from ISO text or%z; it is not a named timezone.- Named IANA timezone support is not part of
std.datetime.
That richer timezone story is expected to live in separately versioned packages. A future package could look like:
from pub::timezones @ 0.1 import TimeZone
A named timezone does not "belong to" one permanent offset. It resolves to an offset for a specific instant or local civil time because daylight-saving and historical rules change.
Interval model¶
The interval model is also explicit:
Durationis the runtime elapsed-time type.TimeDeltais the Python-friendly day/time-style interval.YearMonthIntervalis the month/year-style interval.DateTimeIntervalis the compound interval type for analytics-facing temporal arithmetic.
This split is intentional. Duration is not the main analytics interval abstraction. Month- and year-based arithmetic must not be smuggled through fake fixed-length durations.
Arithmetic boundary¶
The arithmetic boundary is normative:
Instantcomposes withDuration.SystemTimecomposes withDuration.DateandDateTimecompose withTimeDelta,YearMonthInterval, andDateTimeInterval.Instantmust not compose withTimeDelta,YearMonthInterval, orDateTimeInterval.
That keeps runtime timing and analytics-facing interval arithmetic distinct.
Parsing and formatting¶
The parsing and formatting story is complete and Python-shaped:
- canonical ISO helpers such as
isoformat()andfromisoformat(...)belong in the contract; - general
strftime(...)andstrptime(...)-style formatting and parsing belong in the contract as well. - fractional-second parsing and formatting must support up to 9 digits of precision.
%zand%:zbelong toDateTimeOffset;%Zand named timezone parsing are outsidestd.datetime.
However, Incan should standardize the supported format directives itself. The surface should look like Python, but it must not inherit Python's host-libc variability as part of the public contract. The native parser/formatter can be inspired by the directive tables in Rust's chrono and time crates, but the implementation should remain Incan source so stdlib behavior is inspectable and portable.
Constructor and factory surface¶
The constructor and factory surface should be broad but consistent:
Durationuses unit-based factories such asweeks(...),days(...),hours(...),minutes(...),seconds(...),milliseconds(...),microseconds(...), andnanoseconds(...).Instantexposesnow()andelapsed()and composes withDuration.SystemTimeexposesnow(), fallible Unix timestamp factories, and falliblechecked_add(...)/checked_sub(...)offset helpers.Date,Time, andDateTimesupport direct structural construction plusfromisoformat(...)andstrptime(...).FixedOffsetexposes checkedseconds(...),minutes(...),hours(...), andutc()factories.DateTimeOffsetsupports fixed-offset ISO parsing/formatting and%z/%:zparsing/formatting.Dateexposesutc_today().DateTimeexposesutc_now().TimeDeltauses unit-based factories analogous toDuration, including nanosecond precision.YearMonthIntervaluses explicityears(...)andmonths(...)factories.DateTimeIntervaluses an explicit composite constructor with keyword-style fields such asyears,months,days,hours,minutes,seconds, and fractional-second parts.
Python's constructor and parse/format surface is the right DX reference here, but not its precision ceiling. The north-star precision for Time, DateTime, Duration, and TimeDelta is nanoseconds rather than microseconds.
TimeDelta and interval naming¶
The Python-shaped TimeDelta name remains the canonical public spelling because it is familiar and clear to users coming from Python. However, the module may also expose DayTimeInterval as an alias because that name aligns better with analytics and Substrait-style vocabulary.
That dual naming is justified here because both mental models are important in Incan:
- Python-style application and data users will look for
TimeDelta; - analytics-minded users will recognize
DayTimeIntervalimmediately.
The public contract should not generalize this into alias proliferation across the entire module. It is a targeted bridge for one type whose semantics are stable and already clearly defined.
DateTimeInterval semantics¶
DateTimeInterval is a single public compound interval type with structured internal components. It is not a scalar duration.
Its semantics should be:
- one public value containing year/month, day/time, and fractional-second components;
- safe normalization within each compatible bucket;
- no collapsing across the calendar-relative versus fixed-time boundary.
Normalization should include examples such as:
1500 milliseconds -> 1 second 500 milliseconds1500 microseconds -> 1 millisecond 500 microseconds1500 nanoseconds -> 1 microsecond 500 nanoseconds90 seconds -> 1 minute 30 seconds25 hours -> 1 day 1 hour15 months -> 1 year 3 months
But the module must not normalize:
1 month -> 30 days1 year -> 365 days
Comparison and equality are also intentionally constrained:
DateTimeIntervalmust not define a total ordering with<,<=,>, or>=;- normalized structural equality is valid;
- equality is fieldwise after normalization, not "same effect on every possible anchor date."
So examples such as the following should hold:
DateTimeInterval(months=15) == DateTimeInterval(years=1, months=3)DateTimeInterval(days=1, hours=24) == DateTimeInterval(days=2)DateTimeInterval(months=1) != DateTimeInterval(days=30)
When a DateTimeInterval is applied to Date or DateTime, the order of application must be fixed and documented:
- year/month portion first
- day/time/fractional portion second
Core calendar surface¶
The core contract should include more than raw field access, but it should stop short of becoming a full calendaring framework.
Included in the module contract:
- field accessors such as year, month, day, hour, minute, second, and nanosecond;
- arithmetic and comparison where meaningful;
- parsing and formatting;
weekday();iso_week();day_of_year();quarter();- ISO calendar conversion helpers such as
fromisocalendar(...)-style construction where appropriate.
Explicitly outside the module contract:
- locale-sensitive naming as a core semantic feature;
- non-Gregorian calendar systems;
- holiday or business-calendar logic;
- humanized relative-time phrases such as "3 days ago."
Design details¶
Why std.datetime instead of std.time¶
The module is broader than runtime timing. It covers dates, times, naive datetimes, and multiple interval families in addition to Duration and Instant. Calling that whole surface std.time would undersell the civil and analytics half of the design. std.datetime is the more honest name.
Why runtime timing and calendar values live together¶
Real programs move between both worlds constantly: they read timestamps, compare them, add intervals, and also measure elapsed work or set deadlines. One module keeps the mental model coherent and prevents the temporal surface from fragmenting across multiple partially overlapping namespaces.
Python-shaped DX, Rust-shaped runtime, analytics-shaped intervals¶
This RFC deliberately blends three influences:
- Python for public parsing and formatting ergonomics and familiar names such as
TimeDelta; - Rust for runtime timing concepts such as
Duration,Instant, andSystemTime; - analytics and Substrait-style thinking for the interval taxonomy and the explicit split between runtime timing, civil values, and calendar-aware intervals.
The goal is not to mirror any one of those ecosystems mechanically. The goal is to produce a better Incan temporal model than any single source provides by itself.
Why named timezone rules stay out of core stdlib¶
Named timezone-aware datetimes are useful, but they bring API, data-update, and distribution questions that do not need to be tied to core standard library releases. Named timezone databases are the obvious example: Europe/Amsterdam is a rule set, not a single offset, and those rules can vary by date because of daylight-saving and historical changes.
The core contract therefore includes only deterministic fixed offsets. FixedOffset and DateTimeOffset cover Z, +HHMM, and +HH:MM data without shipping a timezone database. Named zones, rule lookup, ambiguous local-time handling, and timezone-data updates belong in separately versioned packages. That plays well with Incan's package model and avoids tying timezone-data updates to core language releases.
Interaction with existing and future features¶
std.fsmay eventually expose timestamps through metadata surfaces that should reusestd.datetimetypes.- Process or workflow RFCs will likely depend on
Durationfor timeouts and scheduling. - Analytics and data RFCs should use the interval taxonomy here rather than inventing their own competing time vocabulary.
- Rust interop remains the escape hatch for host-specific or high-precision temporal behavior not standardized here.
Compatibility / migration¶
This feature is additive. Existing code using raw integers or strings for time-like data keeps working, but new stdlib APIs should converge on this temporal vocabulary once std.datetime exists.
Alternatives considered¶
- Only runtime timing in stdlib
-
Too small. It leaves calendar, analytics, and timestamp work fragmented.
-
Only
datetime-style calendar values -
Too incomplete. It leaves elapsed-time measurement and timeout work without a standard story.
-
One generic
Intervaltype -
Too vague. It would blur fixed elapsed time, day/time intervals, and year/month intervals that behave differently in real analytics and scheduling work.
-
Timezone support in core stdlib
-
Viable for fixed offsets, but not for named timezone rules. Fixed offsets are deterministic values; named zones couple the core temporal API to timezone-database churn, distribution policy, and ambiguous local-time policy.
-
Rust interop only
- Too implementation-shaped for ordinary Incan code and examples.
Drawbacks¶
- Time and interval semantics are notoriously easy to get subtly wrong.
- A broad temporal surface increases the design space substantially compared with narrower utility modules.
- Keeping named timezone support out of core stdlib means users need an extra package for IANA-zone timestamp semantics.
Layers affected¶
- Stdlib / runtime: must provide the temporal types, interval types, and documented arithmetic and formatting semantics.
- Language surface: the module, types, constructors, operators, and methods must be available as specified.
- Execution handoff: implementations must preserve arithmetic, comparison, parsing, and formatting behavior without leaking backend quirks.
- Docs / examples: should standardize how Incan code measures elapsed time, works with timestamps, and performs interval arithmetic.
Implementation Plan¶
Phase 1: Runtime and deterministic value layer¶
- Register
std.datetimeas a standard library namespace. - Add Rust
std::time-backed runtime timing values for durations, instants, and system timestamps. - Add source-defined Incan temporal values for dates, times, naive datetimes, fixed-offset datetimes, and intervals.
- Implement deterministic normalization, comparison, arithmetic, ISO-style parsing/formatting, and Python-shaped
strftime/strptimefor the civil, fixed-offset, and interval surface. - Add focused tests that prove the module imports, typechecks, runs, uses Rust only in
std.datetime.runtime, and keepsstd.datetime.civilsource-defined Incan.
Phase 2: Documentation and release integration¶
- Add user-facing
std.datetimereference docs. - Update stdlib navigation, release notes, and the active dev version.
- Keep the runtime/civil boundary explicit in docs so Rust
std::timeinterop is visible and civil calendar behavior remains source-defined Incan.
Phase 3: UTC civil clock boundary¶
- Keep
Duration,Instant, andSystemTimeon the explicit Ruststd::timeinterop boundary. - Define the civil-clock boundary as UTC-only factories in core stdlib:
Date.utc_today()andDateTime.utc_now(). - Keep timezone-aware
today/nowhelpers in named-zone packages. - Extend tests to cover UTC civil live-clock reads.
Implementation Log¶
Spec / design¶
- Preserve the RFC 058 semantic split between runtime timing values, civil values, and interval values.
- Keep runtime timing on the explicit Rust
std::timeinterop boundary. - Keep fixed-offset support in stdlib while leaving named timezone rule lookup to packages.
- Define the UTC-only civil-clock boundary for calendar-clock factories.
Stdlib / runtime¶
- Register
std.datetimein the stdlib namespace registry. - Add Rust
std::time-backedDuration,Instant, andSystemTime. - Add source-defined Incan
Date,Time,DateTime,FixedOffset,DateTimeOffset,TimeDelta,YearMonthInterval, andDateTimeInterval. - Implement pure Incan normalization and deterministic arithmetic for the civil and interval surface.
- Implement ISO-style parsing and formatting for the first stdlib surface.
- Implement Incan-native
strftime/strptime, including nanosecond%fand fixed-offset%z/%:z. - Implement live host-clock factories for runtime timing through Rust
std::time. - Implement UTC civil live-clock factories through
SystemTime.
Tests¶
- Add a valid
std.datetimefixture covering imports, arithmetic, interval normalization, parsing, formatting, nanoseconds, fixed offsets, and invalid named timezone directives. - Add a regression guard that confines Rust imports to
std.datetime.runtime. - Add live-clock tests for runtime timing.
- Add live-clock tests for UTC civil factories.
Docs¶
- Add
std.datetimereference docs. - Update stdlib navigation.
- Add release notes.
Design Decisions¶
- The module is
std.datetime; there is no separatestd.timenamespace. std.datetimeincludes runtime timing types, civil timestamp types, and analytics-facing interval types in one module.- The runtime timing family is
Duration,Instant, andSystemTime. - The civil timestamp family is
Date,Time, andDateTime. DateTimeis naive; fixed-offset timestamp data usesDateTimeOffset.- The fixed-offset family is
FixedOffsetandDateTimeOffset. - The interval family is
TimeDelta,YearMonthInterval, andDateTimeInterval. Durationis the runtime elapsed-time type, not the main analytics interval abstraction.Instantcomposes withDurationonly; analytics interval types do not apply toInstant.- Named timezone support, including IANA rule lookup and ambiguous local-time policy, is intentionally outside the core standard library and belongs in separately versioned packages.
- Parsing and formatting follow Python's overall
isoformat/fromisoformatandstrftime/strptimemodel, but the supported directives are standardized by Incan rather than inherited from host libc behavior. The implementation is Incan-native and may cite inspiration from Rustchrono/timedirective tables where relevant. - The constructor and factory surface is broad and explicit: direct constructors for structural values, UTC
utc_now()/utc_today()factories where appropriate,fromisoformat(...),strptime(...), and unit-based interval factories including nanoseconds. - Nanosecond precision is part of the north-star contract for
Duration,TimeDelta,Time, andDateTime. TimeDeltaremains the canonical public name, withDayTimeIntervalallowed as an alias.DateTimeIntervalis a single compound public type with structured components, safe normalization within compatible buckets, structural equality after normalization, and no total ordering.- Applying a
DateTimeIntervalto civil temporal values uses a fixed order: year/month first, then day/time/fractional components. - The core calendar surface includes
weekday,iso_week,day_of_year,quarter, and ISO calendar conversion helpers, while excluding locale calendars, business calendars, and humanized relative-time strings.