Title
Explicitly specify effects of precision specification in `duration` formatter
Status
new
Section
[time.format]
Submitter
Liang Jiaming

Created on 2026-03-27.00:00:00 last changed 2 weeks ago

Messages

Date: 2026-04-05.18:31:32

Proposed resolution:

This wording is relative to N5032.

  1. Modify [time.format] as indicated:

    -1- Each formatter ([format.formatter]) specialization in the chrono library ([time.syn]) meets the Formatter requirements ([formatter.requirements]). The `parse` member functions of these formatters interpret the format specification as a chrono-format-spec according to the following syntax:

    […]

    The productions fill-and-align, width, and precision are described in [format.string]. Giving a precision specification in the chrono-format-spec is valid only for types that are specializations of `std::chrono::duration` for which the nested typedef-name `rep` denotes a floating-point type, and the precision specification doesn't take effects unless explicitly specified. For all other types, an exception of type `format_error` is thrown if the chrono-format-spec contains a precision specification. All ordinary multibyte characters represented by literal-char are copied unchanged to the output.

    […]

    -7- Unless otherwise specified, if the chrono-specs is omitted, the chrono object is formatted as if by streaming it to basic_ostringstream<charT> os with the formatting locale imbued and copying `os.str()` through the output iterator of the context with additional padding and adjustments as specified by the format specifiers. The precision specification is applied as if by os << fixed << setprecision(precision).

  2. Modify Table 133 — "Meaning of conversion specifiers" [tab:time.format.spec] as indicated:

    Table 133 — Meaning of conversion specifiers [tab:time.format.spec]
    Specifier Replacement
    […]
    %Q The `duration`'s numeric value (as if extracted via `.count()`).
    The precision specification is applied on that numeric value.
    […]
Date: 2026-03-27.00:00:00

Currently, the formatter for chrono types handles fill-align-width-precision besides chrono specifications. On the one hand, the standard regulates in the second paragraph of [time.format] p1 that:

The productions fill-and-align, width, and precision are described in [format.string].

and in [time.format] p7:

Unless otherwise specified, if the chrono-specs is omitted, the chrono object is formatted as if by streaming it to basic_ostringstream<charT> `os` with the formatting locale imbued and copying `os.str()` through the output iterator of the context with additional padding and adjustments as specified by the format specifiers.

[Example 3:

string s = format("{:=>8}", 42ms);      // value of s is "====42ms"

end example]

which imply that the precision specification takes effects on the final formatted string. On the other hand, the next two statements in [time.format] p1 say that:

Giving a precision specification in the chrono-format-spec is valid only for types that are specializations of `std::chrono::duration` for which the nested typedef-name `rep` denotes a floating-point type. For all other types, an exception of type `format_error` is thrown if the chrono-format-spec contains a precision specification.

which imply that the precision specification takes effects on the underlying floating-point representation.

These two contradictory intentions make unnecessary confusion and divergence in different implementations. Given code example below:

std::chrono::duration<float> s = 10ms + 10us + 123ns;
std::print("from chrono {:.3} | from count {:.3%Q}\n", s);

It gives four different outputs in libstdc++, libc++, MS-STL and FMT (i.e. fmtlib/fmt; though it doesn't need to be standard-compliant, we list it here for comparison):

Implementation {0:.3} {0:.3%Q}
libc++ "0.0" "0.010010123"
libstdc++ "0.0100101s" "0.010010123"
MS-STL "0.0100101s" "0.0100101"
FMT "0.010s" "0.010"

To be specific, the current implementations are as follows:

  1. No chrono-spec, with precision:

    • libc++: Strictly obey [time.format] p7, so the streamed string is truncated as if by `format("{:.3}", "0.0100101s")` and remains the first three characters.

    • libstdc++: Ignore the precision.

    • MS-STL: Ignore the precision.

    • FMT: Apply the precision on the underlying floating-point representation.

  2. `%Q` spec, with precision:

    • libc++: Ignore the precision and format representation to string. Note that `format` will apply `to_chars` on floating-point numbers, which forms the shortest recoverable string so `0.010010123` is fully output.

    • libstdc++: Ignore the precision and format representation to string.

    • MS-STL: Ignore the precision, output count to stream, and format the streamed string. So it outputs 6 significant digits and gives `0.0100101`.

    • FMT: Apply the precision on the underlying floating-point representation.

To bridge the gap among implementations, the standard should explicitly specify the effects of precision. Since the current wording says that precision is only allowed for `duration` with floating-point representation, an intuitive solution is just applying precision on that representation. Otherwise (i.e. the precision is applied on the streamed string), other chrono types like `time_point` should be capable of handling that too, making current constraint seemingly unnecessary.

History
Date User Action Args
2026-04-04 12:52:07adminsetmessages: + msg16245
2026-03-27 00:00:00admincreate