# How to Safely Format C++ std::chrono Durations with fmtlib

> Safely format C++ std::chrono durations with fmtlib using format specifiers and FMT_SAFE_DURATION_CAST for automatic overflow checking.

- Repository: [Hello World Foundation/fmt](https://github.com/fmtlib/fmt)
- Tags: how-to-guide
- Published: 2026-09-11

---

**Use `fmt::format` with chrono format specifiers on `std::chrono::duration` objects to leverage automatic overflow checking, as fmtlib converts durations safely via the `FMT_SAFE_DURATION_CAST` mechanism defined in [`include/fmt/chrono.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/chrono.h) before rendering.**

Formatting C++ chrono durations safely with fmtlib prevents undefined behavior from integer overflow or precision loss during time unit conversions. The library implements strict validation checks in the chrono formatter header that intercept potentially dangerous casts before they reach the output stream. Understanding these internal safety mechanisms allows you to format nanoseconds to seconds—or any duration conversion—without risking wraparound errors.

## How fmtlib Implements Safe Duration Casts

The safety system resides in **[`include/fmt/chrono.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/chrono.h)** and operates through compile-time flags and runtime validation functions.

### The FMT_SAFE_DURATION_CAST Macro

By default, fmtlib defines **`FMT_SAFE_DURATION_CAST`** to **1** at line 27 of [`include/fmt/chrono.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/chrono.h). This macro controls whether the library uses its own overflow-checked conversion routines instead of standard `std::chrono::duration_cast`.

When this macro evaluates to true, the formatter invokes `safe_duration_cast` rather than the unconstrained standard cast. You can override this behavior globally by defining the macro to **0** before including the header, though this disables all overflow protections.

### The safe_duration_cast Implementation

The core validation logic appears in the `safe_duration_cast` function template around lines 56-105. This utility performs **checked conversions** using compile-time ratio arithmetic combined with a runtime error code parameter:

```cpp
// Conceptual implementation based on include/fmt/chrono.h
template <typename To, typename FromDuration>
auto safe_duration_cast(FromDuration from, int& error_code) -> To {
    // Compile-time ratio arithmetic validates representability
    if (would_overflow_or_underflow) {
        error_code = 1;
        return To{}; // Returns empty value on error
    }
    return static_cast<To>(from);
}

```

If the conversion would overflow or underflow the target representation, the function sets the error code and returns a default-constructed duration. The formatter then translates this error state into a `fmt::format_error` exception, preventing silent data corruption.

## The Chrono Formatter Specialization

The actual formatting logic resides in the **`formatter<std::chrono::duration<Rep, Period>, Char>`** specialization at line 2033 of [`include/fmt/chrono.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/chrono.h).

When you invoke `fmt::format("{:%S}", duration)`, the formatter executes three steps:

1. **Extraction**: Retrieves the underlying count from the duration object
2. **Safe Conversion**: Passes the count through `detail::duration_cast`, which respects the `FMT_SAFE_DURATION_CAST` setting and invokes `safe_duration_cast` when enabled
3. **Rendering**: Applies the chrono format specifier (such as `%S` for seconds) to the validated value

This pipeline ensures that conversions from high-precision types like `duration<int64_t, std::nano>` to coarser units are validated for representability before formatting proceeds.

## Practical Code Examples

The following examples demonstrate safe formatting patterns and error handling.

### Basic Duration Formatting

Include `<fmt/chrono.h>` to access the chrono formatters. Use standard chrono format specifiers to control output precision:

```cpp
#include <chrono>
#include <fmt/chrono.h>
#include <fmt/core.h>

int main() {
    using namespace std::chrono;

    // Format milliseconds as seconds with fractional part
    auto ms = milliseconds{1234};
    fmt::print("Elapsed: {:%S}\n", ms);  // Output: Elapsed: 1.234

    // Composite formatting with minutes and seconds
    auto time = minutes{5} + seconds{30};
    fmt::print("Time: {:%M:%S}\n", time); // Output: Time: 05:30
}

```

### Handling Overflow Protection

Large duration values that cannot fit in the target type trigger exceptions rather than silent wraparound:

```cpp
#include <chrono>
#include <fmt/chrono.h>
#include <fmt/core.h>
#include <stdexcept>

int main() {
    // 9 trillion nanoseconds exceeds 32-bit integer range for seconds
    auto huge = std::chrono::duration<int64_t, std::nano>{9'000'000'000'000LL};

    try {
        // Attempting to format as seconds (typically int32_t) would overflow
        fmt::print("Seconds: {:%S}\n", huge);
    } catch (const fmt::format_error& e) {
        // Catches the overflow error from safe_duration_cast
        fmt::print("Conversion failed: {}\n", e.what());
    }
}

```

### Disabling Safety Checks

For performance-critical code where you have externally validated inputs, disable safety checks by undefining the macro before inclusion:

```cpp
#define FMT_SAFE_DURATION_CAST 0
#include <fmt/chrono.h>

int main() {
    auto huge = std::chrono::nanoseconds{9'000'000'000'000LL};
    // Performs raw duration_cast without overflow checks
    fmt::print("Unsafe: {:%S}\n", huge);
}

```

## Summary

- **`FMT_SAFE_DURATION_CAST`** defaults to enabled (1) in [`include/fmt/chrono.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/chrono.h) at line 27, activating overflow protection for all duration conversions.
- The **`safe_duration_cast`** function template validates conversions at lines 56-105, returning error codes on overflow rather than allowing undefined behavior.
- The **`formatter<std::chrono::duration<Rep, Period>, Char>`** specialization at line 2033 integrates these checks via `detail::duration_cast` before applying format specifiers like `%S` or `%M`.
- Failed conversions propagate as **`fmt::format_error`** exceptions, making error conditions explicit and catchable.
- Override the safety macro to **0** before including headers to restore standard casting behavior when necessary.

## Frequently Asked Questions

### What happens when a chrono duration conversion overflows in fmtlib?

When `FMT_SAFE_DURATION_CAST` is enabled (the default), the internal `safe_duration_cast` function detects the overflow condition and sets an error code. The formatter translates this into a `fmt::format_error` exception that terminates the formatting operation, preventing silent integer wraparound or undefined behavior.

### Can I use standard chrono format specifiers like %S and %M with fmtlib?

Yes. The `formatter<std::chrono::duration>` specialization in [`include/fmt/chrono.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/chrono.h) supports standard strftime-style specifiers including `%S` for seconds, `%M` for minutes, and `%H` for hours. The library safely converts the underlying duration to match the requested precision before formatting.

### How do I disable the safe duration cast checks for performance reasons?

Define `FMT_SAFE_DURATION_CAST` to **0** before including [`fmt/chrono.h`](https://github.com/fmtlib/fmt/blob/main/fmt/chrono.h). This forces the formatter to use `std::chrono::duration_cast` directly without overflow validation, eliminating the runtime check overhead. Only disable this if you have externally validated that your duration values fit within the target representation.

### Which header file provides chrono formatting support in fmtlib?

The `<fmt/chrono.h>` header (located at [`include/fmt/chrono.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/chrono.h) in the repository) provides all chrono formatting functionality, including the `safe_duration_cast` utilities, safety macros, and the required `formatter` specializations for `std::chrono::duration` and other chrono types.