How to format std::chrono durations?

leon22 picture leon22 · Feb 9, 2017 · Viewed 7.5k times · Source

Is there a convenient way to format std::chrono::duration to a specified format?

std::chrono::high_resolution_clock::time_point now, then;
then = std::chrono::high_resolution_clock::now();
// ...
now = std::chrono::high_resolution_clock::now();
auto duration = now - then;

// base in microseconds:
auto timeInMicroSec =
      std::chrono::duration_cast<std::chrono::microseconds>(duration);

How can I format timeInMicroSec like ss::ms::us?

Answer

Yakk - Adam Nevraumont picture Yakk - Adam Nevraumont · Feb 9, 2017

This takes an arbitrary chrono duration and breaks it down into other duration quantities:

template<class...Durations, class DurationIn>
std::tuple<Durations...> break_down_durations( DurationIn d ) {
  std::tuple<Durations...> retval;
  using discard=int[];
  (void)discard{0,(void((
    (std::get<Durations>(retval) = std::chrono::duration_cast<Durations>(d)),
    (d -= std::chrono::duration_cast<DurationIn>(std::get<Durations>(retval)))
  )),0)...};
  return retval;
}

Test code:

int main() {
  auto then = std::chrono::high_resolution_clock::now();
  std::this_thread::sleep_for( std::chrono::seconds(3) );
  auto now = std::chrono::high_resolution_clock::now();
  auto duration = now - then;

  auto clean_duration = break_down_durations<std::chrono::seconds, std::chrono::milliseconds, std::chrono::microseconds>( duration );
  std::cout << std::get<0>(clean_duration).count() << "::" << std::get<1>(clean_duration).count() << "::" << std::get<2>(clean_duration).count() << "\n";
}

The formatting code can be cleaned up and put into a function.

Live example.

It would be amusing to write an auto-formatters for such a tuple of (increasing precision) durations.

You'd write the outermost duration, then ::. After that, you'd convert one unit of the previous duration to the next, take its log based 10, and do a setw, and output the next duration. Repeat until you run out of durations.

I'd probably round-trip this through arrays of std::size_t for both .count() and for the ratios.

Like this:

template<class...Durations>
std::string format_durations( std::tuple<Durations...> d ) {
  std::size_t values[]={(std::size_t)std::get<Durations>(d).count()...};
  auto ratios = get_ratios<Durations...>();

  std::stringstream ss << std::setfill('0');
  ss << values[0];

  for (std::size_t const& v:values) {
    std::size_t i = &v-values;
    if (i==0) continue;
    ss << "::" << std::setw( log_10_round_up(ratios[i-1]) ) << values[i];
  }
  return ss.str();
}

with log_10_round_up and get_ratios to be written.

That lets you take a duration, and format it as hh:mm:ss or whatever else you want.