How to set width or precision for seconds with fmt library and chrono

Viewed 483

I'm trying to format a duration with fmt's chrono impl and specify the precision for the seconds part.

I'm reading the documentation but think i don't understand how to do it; whatever i try, it either doesn't format as expected or the compiler reports an error.

Here's what i tried so far:

#include <chrono>
#include <ratio>
#define FMT_HEADER_ONLY
#include <fmt/chrono.h>
#include <iostream>

int main() {
    std::chrono::duration<std::int64_t, std::nano> const d{3601123456789};
    std::cout << fmt::format("1a {:%H:%M:%S}\n", d);
    std::cout << fmt::format("1_ {: >15%S} <-- width works; precision won't compile\n", d);
    // The fmt docs say that "Precision is valid only for std::chrono::duration types with a floating-point representation type."
    // The following format doesn't compile with "d" whose ticks are represented as integer.
    // It does with "dd" whose ticks are represented as double, but the result is still not as expected.
    std::cout << fmt::format("2_ {:.2%S} <-- this is confusing; precision doesn't work\n", dd);
    std::cout << fmt::format("2_ {: >15.2%S} <-- width works; precision doesn't\n", dd);
}

(godbolt link)

Edit: Just checked with std::format on msvc; the results are the same.

1 Answers

To control the width, include an alignment and width specification as the first thing after the ':'. For example "{:>20..." to say align right with width 20.

To control the precision, alter the units of the input duration. For example to have 3 decimal digits, floor<milliseconds>(d):

std::cout << fmt::format("d: {:>20%H:%M:%S}\n", std::chrono::floor<std::chrono::milliseconds>(d));

Output:

d:         00:02:03.456

If you prefer, you can use {:>20%T} in place of {:>20%H:%M:%S}.

For two fractional digits it would be convenient to create a centiseconds duration:

using centiseconds = std::chrono::duration<std::int64_t, std::centi>;
std::cout << fmt::format("d: {:>20%T}\n", std::chrono::floor<centiseconds>(d));

Getting a little winded from chasing the goalposts here ... ;-)

using centiseconds = chrono::duration<int64_t, centi>;
auto d = 3601123456us;
auto m = floor<minutes>(d);
d -= m;
cout << fmt::format("{}:{:%S}", m/1min, floor<centiseconds>(d)) << '\n';

Output:

60:01.12

I got this working for specifying the precision in the format string, but this exits the chrono-type system completely prior to formatting:

auto d = 3601123456.us;
auto m = floor<minutes>(d);
d -= m;
cout << fmt::format("{}:{:0>5.2f}", m/1min, d/1s) << '\n';

Output:

60:01.12
Related