zig-skills/references/std-time.md

3.6 KiB

Time and Timing (Zig 0.16.0)

Primary release-note source: https://ziglang.org/download/0.16.0/release-notes.html

Zig 0.16 moves time APIs that depend on the runtime behind std.Io. Use std.Io.Timestamp, std.Io.Duration, and std.Io.Clock.

Migration Summary

Release-note map:

  • std.time.Instant -> std.Io.Timestamp
  • std.time.Timer -> std.Io.Timestamp
  • std.time.timestamp -> std.Io.Timestamp.now
  • {D} duration formatting -> format std.Io.Duration with {f}

std.time still provides constants and calendar helpers such as ns_per_ms, ns_per_s, and std.time.epoch.

Clocks

std.Io.Clock values:

  • .real: wall-clock Unix/POSIX time, affected by clock changes.
  • .awake: monotonic-style clock intended to exclude suspend time where possible.
  • .boot: monotonic-style clock intended to include suspend time where possible.
  • .cpu_process: CPU time used by current process.
  • .cpu_thread: CPU time used by current thread.

Current Timestamp

const wall = std.Io.Timestamp.now(io, .real);
const boot = std.Io.Timestamp.now(io, .boot);
const awake = std.Io.Timestamp.now(io, .awake);

Clock-specific wrapper:

const start = std.Io.Clock.Timestamp.now(io, .boot);

Duration

const d1 = std.Io.Duration.fromMilliseconds(16);
const d2 = std.Io.Duration.fromSeconds(1);
const ns = d1.toNanoseconds();
_ = .{ d2, ns };

Formatting:

try writer.print("{f}", .{std.Io.Duration.fromMilliseconds(250)});

Do not use the removed {D} format specifier.

Elapsed Time

const start = std.Io.Timestamp.now(io, .boot);

// work

const end = std.Io.Timestamp.now(io, .boot);
const elapsed = start.durationTo(end);

Or with clock-tagged timestamps:

const start = std.Io.Clock.Timestamp.now(io, .boot);

// work

const elapsed = start.untilNow(io);

Sleeping

Use clock-aware durations/timestamps rather than std.Thread.sleep when the code should cooperate with the selected std.Io backend.

try std.Io.Clock.Duration{
    .raw = std.Io.Duration.fromMilliseconds(10),
    .clock = .boot,
}.sleep(io);

For low-level OS-thread code that deliberately blocks a thread and is not part of I/O task scheduling, std.Thread.sleep is still available.

Resolution

Clock resolution may fail or return zero for unsupported clocks.

const clock: std.Io.Clock = .boot;
const resolution = try clock.resolution(io);
if (resolution.nanoseconds == 0) {
    return error.ClockUnavailable;
}

Epoch and Calendar Helpers

Use std.time.epoch for calendar conversion. Timestamps can be converted to seconds:

const now = std.Io.Timestamp.now(io, .real);
const seconds: u64 = @intCast(now.toSeconds());
const epoch_seconds = std.time.epoch.EpochSeconds{ .secs = seconds };

Application Guidance

  • Put common wall-clock timestamp reads behind a shared application helper when consistent clock selection matters.
  • Store/pass std.Io on timer systems that sample time repeatedly.
  • Avoid direct std.time.microTimestamp-style callsites in new code; route through the shared helper.
  • Use .boot or .awake for elapsed-time measurement; use .real for timestamps intended to correspond to wall-clock time.

Review Checklist

  • Is the code using std.Io.Timestamp.now(io, clock) rather than old std.time.timestamp helpers?
  • Is the clock choice documented by usage (.real for wall time, monotonic clocks for elapsed time)?
  • Is duration formatting using {f} with std.Io.Duration?
  • Does the API receive or store io rather than constructing a fallback locally?
  • Is std.Thread.sleep only used for deliberate OS-thread blocking?