Skip to content

std.time

std.time provides small time values. Duration is a span used by async timers and deadlines. Instant is a clock reading used to measure elapsed time. The module also exposes the two underlying clocks: now() reads the monotonic clock, and now_realtime() reads wall-clock time since the Unix epoch.

Contract

Duration

Duration is an opaque copyable value that stores a non-negative nanosecond count. Unit constructors make conversion explicit. ns cannot overflow because the input is already nanoseconds; us, ms, and sec check multiplication and throw TimeError.Overflow when the requested duration cannot fit in u64 nanoseconds.

Duration does not allocate and does not own external resources. Timer accuracy, monotonic clock source, wake ordering, and scheduler fairness belong to std.async, not to the value type.

Clocks

Zynx exposes two clocks with different guarantees:

  • The monotonic clock, read by now(), is steady and never decreases. Its zero point is unspecified, so an Instant is only meaningful relative to another Instant. Use it to measure elapsed time.
  • The realtime clock, read by now_realtime(), reports wall-clock time as nanoseconds since the Unix epoch (1970-01-01 UTC). It is not steady: the system can adjust it forward or backward, so successive reads can jump or even move backward. Use it for timestamps, not for measuring intervals.

Instant is a Copy value that wraps a monotonic nanosecond reading. It does not allocate and does not own external resources. The difference between two Instant values is a Duration. Because Duration holds a non-negative nanosecond count, duration_since saturates to zero when earlier is later than the receiver. A correct monotonic clock never produces that ordering, but the saturation keeps the result well defined.

Duration

Duration is an opaque value. Build it with unit constructors and pass it to APIs such as async.sleep and async.timeout:

Zynx
import std.io;
import std.async;
import std.time;
import std.mem.{ AllocError };

fn main() throws(AllocError) {
	await sleep(try time.ms(0));
	let value = (await timeout(ready(7), try time.sec(1))) catch { _ => 0 };
	io.println(try ^"{value}");
}

Functions

NameSignatureDescription
nsns(amount: u64) -> DurationBuilds a nanosecond duration.
usus(amount: u64) throws(TimeError) -> DurationBuilds a microsecond duration or returns TimeError.Overflow.
msms(amount: u64) throws(TimeError) -> DurationBuilds a millisecond duration or returns TimeError.Overflow.
secsec(amount: u64) throws(TimeError) -> DurationBuilds a second duration or returns TimeError.Overflow.
to_nanosecondsto_nanoseconds(duration: Duration) -> u64Returns the raw nanosecond count.

Clock Readings

To measure how long work takes, read the monotonic clock with now() before and after the work, then subtract the two Instant values with duration_since. Assert relationships between readings, never an exact wall-clock value:

Zynx
import std.time;

fn main() {
	let start = time.now();

	// Work whose duration we want to measure.
	var total = 0 as u64;
	var i = 0 as u64;
	while i < 2000000 as u64 {
		total = total + i;
		i = i + 1 as u64;
	}

	let finish = time.now();
	let span = finish.duration_since(start);

	// The monotonic clock never decreases, so the later reading is never
	// earlier than the start, and the span equals the difference of the two
	// readings.
	assert finish.nanos() >= start.nanos();
	assert time.to_nanoseconds(span) == finish.nanos() - start.nanos();
	assert total == 1999999000000 as u64;
}

Instant.elapsed() is a convenience for the common case of measuring from a single starting Instant to the present. It re-reads the monotonic clock each call, so repeated readings from the same start never decrease:

Zynx
import std.time;

fn main() {
	let start = time.now();
	let first = start.elapsed();
	let second = start.elapsed();

	// elapsed() re-reads the monotonic clock, which never decreases.
	assert time.to_nanoseconds(second) >= time.to_nanoseconds(first);
}

now_realtime() returns a Duration measuring wall-clock nanoseconds since the Unix epoch. Use it for timestamps. Do not use it to measure intervals, because the system clock can move backward between reads:

Zynx
import std.time;

fn main() {
	let wall = time.now_realtime();

	// Realtime is nanoseconds since the Unix epoch (1970-01-01 UTC), so a live
	// reading is well past the start of 2001.
	let year_2001 = (978307200 as u64) * (1000000000 as u64);
	assert time.to_nanoseconds(wall) > year_2001;
}

Functions

NameSignatureDescription
nownow() -> InstantReads the monotonic clock; use it to measure elapsed time.
now_realtimenow_realtime() -> DurationWall-clock time since the Unix epoch (1970-01-01 UTC), in nanoseconds.

Instant Methods

MethodSignatureDescription
nanosnanos() -> u64Raw monotonic nanosecond reading; the zero point is unspecified.
duration_sinceduration_since(earlier: Instant) -> DurationTime elapsed from earlier to this Instant, saturating to zero if earlier is later.
elapsedelapsed() -> DurationTime from this Instant to a fresh monotonic reading; equivalent to now().duration_since(self).

Use std.async for sleep, timeout, and scheduler behavior. Use std.arith when duration-like calculations need explicit overflow policy before conversion.

Status

std.time is the current public time API: Duration for spans, Instant and now() for monotonic elapsed-time measurement, and now_realtime() for wall-clock timestamps. Calendar, time-zone, and formatted-date conversions are not part of this module. For the full exported surface, see Standard Library Inventory.

Released under the MIT License.