How to Format std::chrono Time Points with fmtlib: A Complete Guide

You can format any std::chrono time point or duration by including <fmt/chrono.h> and using standard strftime-style format specifiers like %Y-%m-%d %H:%M:%S inside your format string.

The fmtlib/fmt repository provides native support for C++11 and C++20 chrono types through the dedicated header include/fmt/chrono.h. This implementation eliminates the need for manual std::tm conversions or <iomanip> manipulators by offering type-safe formatter specializations that handle system_clock::time_point, utc_time, local_time, and high-resolution durations with compile-time validation.

Include the Chrono Header

To access chrono formatting capabilities, include the specialized header after your core fmt include:

#include <fmt/chrono.h>

This header defines the formatter<std::chrono::duration> and time-point alias specializations that teach fmt::format how to parse and display temporal data.

Format System Clock Time Points

The most common use case is formatting std::chrono::system_clock::time_point. The library treats this as sys_time internally and converts it to a broken-down time representation automatically.

#include <fmt/chrono.h>
#include <chrono>
#include <iostream>

int main() {
    auto now = std::chrono::system_clock::now();
    
    // Default formatting uses strftime-style specifiers
    std::cout << fmt::format("{:%Y-%m-%d %H:%M:%S}\n", now);
}

According to the source code in include/fmt/chrono.h, the conversion path involves detail::to_time_t at line 481 to cast the time point to std::time_t, followed by a gmtime call at line 535 to obtain the std::tm structure for formatting.

Format UTC and Local Time (C++20)

For C++20 code using distinct clock types, the header provides type aliases at lines 262-265 for utc_time and local_time. These enable explicit formatting of UTC-based or local-time-based points using the same specifier syntax.

#include <fmt/chrono.h>
#include <chrono>

int main() {
    using namespace std::chrono;
    
    // UTC time point
    utc_time<seconds> utc_now{system_clock::now()};
    fmt::print("{:%Y-%m-%d %H:%M:%S} UTC\n", utc_now);
    
    // Local time point  
    local_time<seconds> local_now{system_clock::now()};
    fmt::print("{:%Y-%m-%d %H:%M:%S} local\n", local_now);
}

Format Durations

Durations are handled by the same generic formatter<std::chrono::duration> specialization that begins at line 2033. The library interprets format specifiers like %H, %M, and %S as a time-of-day breakdown when formatting durations.

#include <fmt/chrono.h>
#include <chrono>

int main() {
    auto duration = std::chrono::minutes{123} + std::chrono::seconds{45};
    
    // Outputs 02:03:45
    fmt::print("{:%H:%M:%S}\n", duration);
}

Sub-Second Precision

To display fractional seconds, use the %f specifier. The implementation extracts the sub-second component using the fractional part extraction logic found at lines 971-978 of include/fmt/chrono.h.

#include <fmt/chrono.h>
#include <chrono>

int main() {
    auto ms = std::chrono::milliseconds{1234};
    
    // Outputs 01.234 (1 second and 234 milliseconds)
    fmt::print("{:%S.%f}\n", ms);
}

How the Formatter Works Internally

When you pass a chrono type to fmt::format, the library executes a three-phase pipeline defined in include/fmt/chrono.h:

  1. Parsing: The format string is analyzed by parse_chrono_format, starting at line 630, which builds a chrono_format_checker to validate specifiers like %Y, %m, %d, %H, %M, and %S.

  2. Conversion: For time points, the formatter invokes detail::to_time_t to convert the duration-since-epoch to std::time_t, then uses gmtime to create a std::tm representation.

  3. Output: The formatter writes each field according to the parsed specifiers. For durations, it calculates hours, minutes, and seconds directly from the tick count without calendar conversion.

This architecture ensures that formatting std::chrono time points with fmtlib requires no manual temporaries or error-prone pointer handling.

Summary

  • Include <fmt/chrono.h> to enable chrono support alongside the core library.
  • Use strftime-style format specifiers (e.g., %Y-%m-%d %H:%M:%S) inside your format string braces.
  • The formatter specializations begin at line 2033 in include/fmt/chrono.h, handling both durations and time points.
  • C++20 utc_time and local_time aliases are supported via definitions at lines 262-265.
  • Sub-second precision is available through the %f specifier, implemented at lines 971-978.

Frequently Asked Questions

No. Chrono formatting is header-only functionality. Including <fmt/chrono.h> provides all necessary templates and specializations without requiring additional linker flags, as confirmed by the implementation in include/fmt/chrono.h.

What format specifiers are supported for std::chrono types?

The library supports standard strftime specifiers including %Y (year), %m (month), %d (day), %H (hour), %M (minute), %S (second), and %f (fractional seconds). The parse_chrono_format function at line 630 validates these specifiers at runtime.

Does fmtlib require C++20 to format time points?

No. Basic formatting for std::chrono::system_clock::time_point works with C++11. However, explicit utc_time and local_time types require C++20's extended <chrono> library, as these type aliases depend on C++20 clock features defined at lines 262-265.

How does fmtlib handle timezone conversions?

By default, time points are formatted as UTC using the gmtime conversion path at line 535. To display local time, you must explicitly use std::chrono::local_time (C++20), which triggers the local clock formatting path in the formatter specializations.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →