Skip to main content

Chrono and time formatting

std::chrono types carry their units in the type system — a std::chrono::milliseconds knows it's milliseconds — and fmt/chrono.h is what turns that into readable output without a round-trip through std::tm and strftime.

Durations

#include <fmt/chrono.h>

fmt::print("{}\n", std::chrono::milliseconds(1500)); // 1500ms
fmt::print("{}\n", std::chrono::seconds(90)); // 90s
DurationSuffix
std::chrono::nanosecondsns
std::chrono::microsecondsus
std::chrono::millisecondsms
std::chrono::secondss
std::chrono::minutesmin
std::chrono::hoursh

strftime-style specs

A strftime-derived spec after the colon controls the presentation, and it applies to durations, time points, and calendar types alike.

timestamp.cpp
auto now = std::chrono::system_clock::now();
fmt::print("{:%Y-%m-%d %H:%M:%S}\n", now); // 2026-08-07 14:32:05
fmt::print("{:%H:%M}\n", now); // 14:32

The flag table

FlagMeaning
%Y4-digit year
%mMonth, zero-padded (01-12)
%dDay of month, zero-padded (01-31)
%HHour, 24h, zero-padded
%MMinute, zero-padded
%SSecond, zero-padded (with sub-second precision if requested)
%FEquivalent to %Y-%m-%d
%TEquivalent to %H:%M:%S
%zUTC offset
%ZTime zone abbreviation
%jDay of year, zero-padded
%pAM/PM designator

Sub-second precision

%S on a duration whose period is finer than a second includes the fractional part automatically; .N after the spec controls how many fractional digits are shown, the same as float precision elsewhere in the mini-language.

fmt::print("{:%S}\n", std::chrono::milliseconds(1234)); // 01.234

Time zones

system_clock::time_point formats as UTC — there is no implicit local-time conversion

fmt::format("{:%H:%M}", std::chrono::system_clock::now()) prints the UTC wall-clock time, not the local one. Nothing about a bare time_point implies a time zone; fmt formats the value it was given.

For local or named-zone output, convert to a C++20 std::chrono::zoned_time first — fmt formats zoned_time with the zone's offset and abbreviation applied, using the same spec letters.

Cost

Chrono formatting is markedly heavier than integer or float formatting: it involves calendar arithmetic, not just digit conversion.

In a hot logging path, format the timestamp once per line, not once per field

If a log line has a timestamp plus several other chrono-derived fields, compute and format the timestamp string once and reuse it, rather than re-deriving it (and re-paying the calendar arithmetic) per field. See Performance characteristics for where this cost sits relative to the rest of fmt.

See also