Chrono and Formatting

Chrono and Formatting

Measure time correctly and produce readable output with modern formatting APIs.

Chrono and Formatting

Pick the right clock

Use std::chrono::steady_clock for elapsed time and timeouts. Use std::chrono::system_clock for timestamps that correspond to calendar time.

using namespace std::chrono_literals;

auto start = std::chrono::steady_clock::now();
do_work();
auto end = std::chrono::steady_clock::now();

auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);

steady_clock will not jump backwards if the system time changes.

That makes it the default choice for benchmarks, retries, and timeouts.

Timeouts and literals

auto retry_delay = 250ms;
auto max_wait = 5s;

Duration literals remove the guesswork around raw integers.

using namespace std::chrono_literals;

if (elapsed > 2s) {
    retry();
}

Formatting output cleanly

auto message = std::format("processed {} items in {} ms", count, elapsed.count());
std::cout << message << '\n';

This scales better than long stream chains once formatting becomes more structured.

If your standard library supports it, std::print can write directly:

std::print("processed {} items in {} ms\n", count, elapsed.count());

Practical example

void benchmark_sort(std::vector<int> values) {
    auto start = std::chrono::steady_clock::now();
    std::ranges::sort(values);
    auto end = std::chrono::steady_clock::now();

    auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
    std::print("sorted {} values in {} ms\n", values.size(), ms.count());
}

Stopwatch-style helper

class Stopwatch {
public:
    void start() {
        running_ = true;
        started_at_ = std::chrono::steady_clock::now();
    }

    void stop() {
        if (running_) {
            elapsed_ += std::chrono::steady_clock::now() - started_at_;
            running_ = false;
        }
    }

    void reset() {
        elapsed_ = std::chrono::steady_clock::duration::zero();
        running_ = false;
    }

    auto elapsed_ms() const {
        return std::chrono::duration_cast<std::chrono::milliseconds>(elapsed_);
    }

private:
    std::chrono::steady_clock::time_point started_at_{};
    std::chrono::steady_clock::duration elapsed_{};
    bool running_ = false;
};

This kind of helper is where chrono types become practical instead of abstract: you accumulate durations safely without falling back to raw integers.

Calendar-style formatting

auto now = std::chrono::system_clock::now();
auto stamp = std::format("{:%Y-%m-%d %H:%M}", now);

Use this when the output is meant for people rather than just internal timing.

Time-point arithmetic

using namespace std::chrono_literals;

auto deadline = std::chrono::steady_clock::now() + 250ms;
if (std::chrono::steady_clock::now() > deadline) {
    retry();
}

Time points represent "when". Durations represent "how long". Most chrono code is cleaner once you keep those roles separate.

Common mistakes

Exercises

Example in practice

#include <format>
#include <string>

int main() {
    std::string text = std::format("value = {}", 42);
    return static_cast<int>(text.size());
}

Try this variation

Swap one older pattern for a newer standard facility, then compare clarity. This keeps modern C++ grounded in practical tradeoffs instead of feature tourism.

#include <format>
#include <string>

int main() {
    std::string line = std::format("{} items ready", 3);
    return static_cast<int>(line.size());
}