# Time and Timers

### Time and Timers

Programs often need to work with time.

A program may need to measure how long something takes. It may need to wait for a short duration. It may need to store timestamps in logs. It may need to compare two moments.

Zig’s standard library provides time utilities in:

```zig
std.time
```

For beginners, the most important use is measuring elapsed time.

#### Getting a Timestamp

A timestamp is a number that represents a point in time.

One simple function is:

```zig
std.time.nanoTimestamp()
```

It returns a timestamp in nanoseconds.

A nanosecond is one billionth of a second.

```zig
const std = @import("std");

pub fn main() void {
    const now = std.time.nanoTimestamp();

    std.debug.print("now = {}\n", .{now});
}
```

The exact number is not usually meaningful by itself. It is useful when you compare it with another timestamp.

#### Measuring Elapsed Time

To measure elapsed time, take one timestamp before the work and one timestamp after the work.

```zig
const std = @import("std");

pub fn main() void {
    const start = std.time.nanoTimestamp();

    var sum: u64 = 0;
    for (0..1_000_000) |i| {
        sum += i;
    }

    const end = std.time.nanoTimestamp();

    std.debug.print("sum = {}\n", .{sum});
    std.debug.print("elapsed ns = {}\n", .{end - start});
}
```

This pattern is common:

```zig
const start = std.time.nanoTimestamp();

// work

const end = std.time.nanoTimestamp();
const elapsed = end - start;
```

The result is the elapsed time in nanoseconds.

#### Converting Units

Nanoseconds are precise, but they can be hard to read.

Zig defines useful constants such as:

```zig
std.time.ns_per_us
std.time.ns_per_ms
std.time.ns_per_s
```

These mean:

| Constant | Meaning |
|---|---|
| `std.time.ns_per_us` | nanoseconds per microsecond |
| `std.time.ns_per_ms` | nanoseconds per millisecond |
| `std.time.ns_per_s` | nanoseconds per second |

Example:

```zig
const std = @import("std");

pub fn main() void {
    const start = std.time.nanoTimestamp();

    var sum: u64 = 0;
    for (0..10_000_000) |i| {
        sum += i;
    }

    const end = std.time.nanoTimestamp();
    const elapsed_ns = end - start;

    const elapsed_ms = @divTrunc(elapsed_ns, std.time.ns_per_ms);

    std.debug.print("sum = {}\n", .{sum});
    std.debug.print("elapsed ms = {}\n", .{elapsed_ms});
}
```

This converts nanoseconds to milliseconds using integer division.

#### Integer Division Loses the Remainder

This line:

```zig
const elapsed_ms = @divTrunc(elapsed_ns, std.time.ns_per_ms);
```

drops the fractional part.

For example, if the elapsed time is:

```text
1.7 ms
```

integer division gives:

```text
1 ms
```

That may be good enough for simple reporting.

For more detail, print both milliseconds and remaining nanoseconds, or use floating point conversion carefully.

```zig
const elapsed_ms = @as(f64, @floatFromInt(elapsed_ns)) /
    @as(f64, @floatFromInt(std.time.ns_per_ms));

std.debug.print("elapsed ms = {d:.3}\n", .{elapsed_ms});
```

This prints a decimal number with three digits after the decimal point.

#### Sleeping

Sleeping means pausing the current thread for some duration.

A common function is:

```zig
std.time.sleep()
```

It receives a duration in nanoseconds.

```zig
const std = @import("std");

pub fn main() void {
    std.debug.print("before sleep\n", .{});

    std.time.sleep(1 * std.time.ns_per_s);

    std.debug.print("after sleep\n", .{});
}
```

This pauses for about one second.

Use constants to make durations readable:

```zig
std.time.sleep(500 * std.time.ns_per_ms);
```

This sleeps for about 500 milliseconds.

#### Sleep Is Not Exact

A sleep duration is a request to the operating system.

This:

```zig
std.time.sleep(10 * std.time.ns_per_ms);
```

means “do not run this thread for at least about 10 milliseconds.”

It does not guarantee exact timing.

The operating system scheduler, system load, timer resolution, and platform behavior all affect when the thread resumes.

For ordinary delays, `sleep` is fine.

For precise benchmarking, do not use `sleep` as a timing mechanism.

#### Timers

For measuring elapsed time, Zig also provides timer utilities.

The exact timer APIs may vary across versions, but the idea is simple:

Create a timer.

Do work.

Ask the timer how much time elapsed.

Conceptually:

```zig
var timer = try std.time.Timer.start();

// work

const elapsed = timer.read();
```

A timer is often clearer than manually storing two timestamps.

Example shape:

```zig
const std = @import("std");

pub fn main() !void {
    var timer = try std.time.Timer.start();

    var sum: u64 = 0;
    for (0..10_000_000) |i| {
        sum += i;
    }

    const elapsed = timer.read();

    std.debug.print("sum = {}\n", .{sum});
    std.debug.print("elapsed ns = {}\n", .{elapsed});
}
```

Use this pattern when available in your installed Zig version.

#### Wall Time vs Monotonic Time

There are two different ideas of time.

Wall time is calendar time. It answers questions like:

What date is it?

What time is it now?

What timestamp should I put in a log?

Monotonic time is measuring time. It answers questions like:

How long did this operation take?

Has this timeout expired?

For measuring durations, prefer monotonic time when possible.

Wall time can jump forward or backward because of clock synchronization, manual clock changes, daylight saving changes, or system time corrections.

Monotonic time is designed for elapsed-time measurement.

#### Timing Small Code Is Hard

This kind of benchmark is tempting:

```zig
const start = std.time.nanoTimestamp();

doSomething();

const end = std.time.nanoTimestamp();
std.debug.print("{}\n", .{end - start});
```

It is useful for rough checks, but it is not a serious benchmark.

Small timings are noisy.

The compiler may optimize away unused work.

The CPU may change speed.

The operating system may interrupt your process.

The first run may behave differently from later runs.

For simple learning, manual timing is fine. For serious performance work, run many iterations, prevent unwanted optimization, use release builds, and compare results carefully.

#### A Simple Repeated Timing Program

This example runs work many times and measures the total time.

```zig
const std = @import("std");

fn work() u64 {
    var sum: u64 = 0;

    for (0..1_000_000) |i| {
        sum += i;
    }

    return sum;
}

pub fn main() void {
    const start = std.time.nanoTimestamp();

    var result: u64 = 0;
    for (0..100) |_| {
        result ^= work();
    }

    const end = std.time.nanoTimestamp();
    const elapsed_ns = end - start;

    const elapsed_ms = @as(f64, @floatFromInt(elapsed_ns)) /
        @as(f64, @floatFromInt(std.time.ns_per_ms));

    std.debug.print("result = {}\n", .{result});
    std.debug.print("elapsed ms = {d:.3}\n", .{elapsed_ms});
}
```

The variable `result` matters.

Without it, the compiler might decide that the work has no visible effect and remove some of it in optimized builds.

This example still does not replace a real benchmark, but it teaches the right suspicion: timing code must be written carefully.

#### Timeouts

A timeout means “stop waiting after this much time.”

For example, a network program might wait for data, but only for 5 seconds.

The general idea is:

```zig
const deadline = start_time + timeout;

// later
if (now >= deadline) {
    return error.Timeout;
}
```

A simple loop can use this pattern:

```zig
const std = @import("std");

pub fn main() !void {
    const timeout_ns = 2 * std.time.ns_per_s;
    const start = std.time.nanoTimestamp();

    while (true) {
        const now = std.time.nanoTimestamp();

        if (now - start >= timeout_ns) {
            std.debug.print("timeout\n", .{});
            return;
        }

        std.debug.print("working...\n", .{});
        std.time.sleep(500 * std.time.ns_per_ms);
    }
}
```

This prints `working...` a few times, then prints `timeout`.

#### Logging with Timestamps

Many programs include timestamps in logs.

A simple debug log might print a raw timestamp:

```zig
const std = @import("std");

pub fn main() void {
    const ts = std.time.nanoTimestamp();

    std.debug.print("[{}] program started\n", .{ts});
}
```

This is not human-friendly, but it is useful for ordering events.

Human-readable date and time formatting is a separate topic. It involves calendars, time zones, leap years, and formatting rules. Keep it separate from basic elapsed-time measurement.

#### Common Mistakes

Do not assume sleep is exact.

Do not use wall-clock time for precise elapsed-time measurement when monotonic time is available.

Do not trust one timing result.

Do not benchmark debug builds and assume the result represents optimized performance.

Do not let the compiler remove the work you are trying to measure.

Do not confuse nanoseconds, microseconds, milliseconds, and seconds.

#### The Core Pattern

For elapsed time:

```zig
const start = std.time.nanoTimestamp();

// work

const end = std.time.nanoTimestamp();
const elapsed = end - start;
```

For sleeping:

```zig
std.time.sleep(500 * std.time.ns_per_ms);
```

For readable conversion:

```zig
const ms = @as(f64, @floatFromInt(ns)) /
    @as(f64, @floatFromInt(std.time.ns_per_ms));
```

For repeated timing:

```zig
var result: u64 = 0;

const start = std.time.nanoTimestamp();

for (0..100) |_| {
    result ^= work();
}

const end = std.time.nanoTimestamp();
```

#### What You Should Remember

`std.time` contains time-related utilities.

Use timestamps to measure elapsed time.

Subtract start time from end time.

Use constants like `std.time.ns_per_ms` and `std.time.ns_per_s`.

Use `std.time.sleep` for simple delays.

Sleep is approximate.

Timing small code is noisy.

Use release builds for performance measurements.

For serious benchmarking, measure many iterations and make sure the work cannot be optimized away.

