Three types cover Nova's time model: Duration (signed interval), Timestamp (Unix epoch moment), and Monotonic (process-local monotonic clock). All store nanoseconds in a single i64 field. Integer and float extension methods let you write 5.to_seconds(), 1.to_hours(), 500.to_millis() directly.
ro timeout = 30.to_seconds()
ro deadline = 1_700_000_000.to_unix_seconds() + timeout
ro d = 3661.to_seconds() // 1h 1min 1s
assert(d.parts().hours == 1)
Types
Duration
stable since 0.1export type Duration { nanos i64 }
Signed time interval with nanosecond precision. Range: ±292 years (i64::MAX nanoseconds). Supports arithmetic, comparison, and formatting operators.
Timestamp
stable since 0.1export type Timestamp { nanos i64 }
A moment in time stored as nanoseconds since Unix epoch (1970-01-01 UTC). Signed, so it supports dates before 1970. Timestamp - Timestamp yields a Duration. Methods that need the current wall clock require the Time effect.
Monotonic
stable since 0.6export type Monotonic { nanos i64 }
A point on the process-local monotonic clock. Immune to NTP / DST / manual clock adjustments. Use for timer deadlines, profiling intervals, and retry budgets. Cannot be serialized or compared across processes.
Examples
ro start = Monotonic.now()
// ... work ...
ro elapsed = Monotonic.now().elapsed_since(start)
Constants
const ZERO Duration = { nanos: 0 } // zero duration
const SECOND Duration = { nanos: 1_000_000_000 } // 1 second
const MINUTE Duration = { nanos: 60_000_000_000 } // 1 minute
const HOUR Duration = { nanos: 3_600_000_000_000 }
const EPOCH Timestamp = { nanos: 0 } // 1970-01-01 00:00:00 UTC
Duration — constructors
fn[T Ints] T @to_nanos() -> Duration // 1 ns
fn[T Ints] T @to_micros() -> Duration // 1 μs = 1_000 ns
fn[T Ints] T @to_millis() -> Duration // 1 ms = 1_000_000 ns
fn[T Ints] T @to_seconds() -> Duration // 1 s = 1_000_000_000 ns
fn[T Ints] T @to_minutes() -> Duration // 60 s
fn[T Ints] T @to_hours() -> Duration // 3600 s
fn[T Ints] T @to_days() -> Duration // 86400 s
fn[T Ints] T @to_weeks() -> Duration // 7 * 86400 s
fn f64 @to_seconds() -> Duration // e.g. 0.5 → 500ms
Duration — accessors
fn Duration @nanos() -> i64 // raw nanoseconds
fn Duration @micros() -> i64 // truncating
fn Duration @millis() -> i64
fn Duration @seconds() -> i64
fn Duration @minutes() -> i64
fn Duration @hours() -> i64
fn Duration @days() -> i64
fn Duration @seconds_f64() -> f64 // fractional seconds (preserves sub-second)
Examples
assert(2.to_seconds().millis() == 2000)
assert(1500.to_millis().seconds_f64() == 1.5)
Duration — arithmetic
fn Duration @plus(other Duration) -> Duration // d1 + d2
fn Duration @minus(other Duration) -> Duration // d1 - d2 (may be negative)
fn Duration @neg() -> Duration // unary -
fn Duration @abs() -> Duration // |d|
fn Duration @times(n i64) -> Duration // d * n
fn Duration @times(n f64) -> Duration
fn Duration @div(n i64) -> Duration // d / n (truncating)
fn Duration @div(n f64) -> Duration
fn Duration @ratio(other Duration) -> f64 // d1 / d2 dimensionless
Examples
assert(1.to_hours() + 30.to_minutes() == 90.to_minutes())
assert(30.to_seconds().ratio(1.to_minutes()) == 0.5)
Duration — formatting
fn Duration @to_str() -> str // auto-scale: "500ns" / "42ms" / "2.5s" / "5min" / "2h"
fn Duration @parts() -> DurationParts
Examples
ro d = 3661.to_seconds() // 1h 1min 1s
ro p = d.parts()
assert(p.hours == 1 && p.minutes == 1 && p.seconds == 1)
assert(2.to_hours().to_str() == "2h")
assert(500.to_nanos().to_str() == "500ns")
Integer & float extensions
Extension methods on int and f64 allow natural duration literals:
fn[T Ints] T @to_nanos() -> Duration
fn[T Ints] T @to_micros() -> Duration
fn[T Ints] T @to_millis() -> Duration
fn[T Ints] T @to_seconds() -> Duration
fn[T Ints] T @to_minutes() -> Duration
fn[T Ints] T @to_hours() -> Duration
fn[T Ints] T @to_days() -> Duration
fn[T Ints] T @to_weeks() -> Duration
fn f64 @to_seconds() -> Duration
Examples
assert(30.to_seconds().seconds() == 30)
assert(500.to_nanos().nanos() == 500)
assert(0.5.to_seconds() == 500.to_millis())
Timestamp — constructors
fn[T Ints] T @to_unix_seconds() -> Timestamp
fn[T Ints] T @to_unix_millis() -> Timestamp
fn[T Ints] T @to_unix_nanos() -> Timestamp
Timestamp — arithmetic
fn Timestamp @plus(d Duration) -> Timestamp // shift forward
fn Timestamp @minus(d Duration) -> Timestamp // shift back
fn Timestamp @minus(other Timestamp) -> Duration // elapsed (signed)
Examples
ro start = 1000.to_unix_seconds()
ro deadline = start + 30.to_seconds()
assert(deadline.unix_seconds() == 1030)
assert((deadline - start) == 30.to_seconds())
Timestamp — Time-effect queries
These methods require the Time effect from the ambient scope. In tests, inject a deterministic handler:
fn Timestamp @is_past() Time -> bool
fn Timestamp @elapsed() Time -> Duration // positive if in the past
fn Timestamp @time_until() Time -> Duration // positive if in the future
fn deadline_in(d Duration) Time -> Timestamp // now() + d
Examples
with Time = th.fixed_ms(10_000) {
ro deadline = 5.to_unix_seconds()
assert(deadline.is_past())
ro (result, elapsed) = measure(|| 42)
assert(result == 42)
assert(elapsed == Duration.ZERO) // fixed clock doesn't advance
}