Search code examples
c++stlformattingc++20c++-chrono

"std::format"ing std::chrono seconds without fractional digits


What is the correct way to format seconds in c++20 (using std::format) without also showing fractional digits?


This code:

#include <chrono>
#include <format>
#include <iostream>

int main()
    {
        std::cout << std::format("{:%H:%M:%S}",  std::chrono::system_clock::now()) << std::endl;
        std::cout << std::format("{:%H:%M:%OS}", std::chrono::system_clock::now()) << std::endl;
    }

Produces this output (via trunk GCC (14.0.0 20230425)):

00:18:07.087240403
00:18:07.087622600

I just want the integer part (eg 00:18:07 without the trailing .087240403). It's very easy to remove the remainder from the resulting string after the fact (or manually extract the seconds and print them separately), but I'd be very surprised if that was the intended solution.


The formatting codes are described here. AFAICT there is no %S variant that explicitly rounds or truncates the seconds to the nearest integer.

The explanation (from the link above) for %S (and %OS below) is:

Writes the second as a decimal number. If the number of seconds is less than 10, the result is prefixed with 0.

If the precision of the input cannot be exactly represented with seconds, then the format is a decimal floating-point number with a fixed format and a precision matching that of the precision of the input (or to a microseconds precision if the conversion to floating-point decimal seconds cannot be made within 18 fractional digits). The character for the decimal point is localized according to the locale.

The modified command %OS writes the locale's alternative representation.


GCC/glibc is functioning correctly here, right? (It seems to fit the explanation above).

If so, what's the intended way to achieve this?

  1. Do I use a different format string?
  2. Do I use a different clock?
  3. Do I manually round the value first? (It still shows the fractional part if rounded).
  4. Or do I really have to manually strip the fractional part or manually extract the seconds.

(If this isn't trivially achievable, could this be considered potentially undesirable behavior, perhaps a STL defect?)


Solution

  • You can use time_point_cast to convert its duration to seconds and then format it:

    auto now = std::chrono::time_point_cast<std::chrono::seconds>(
                 std::chrono::system_clock::now()
               );
    std::cout << std::format("{:%H:%M:%S}", now) << std::endl;