How to format std::chrono durations?

前端 未结 4 1764
名媛妹妹
名媛妹妹 2021-01-11 12:49

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

std::chrono::high_resolution_clock::time_point now, then;
then          


        
相关标签:
4条回答
  • 2021-01-11 13:06

    One can use something like:

    #include <iomanip>
    #include <sstream>
    
    //...
    
    auto c(timeInMicroSec.count());
    std::ostringstream oss;
    oss << std::setfill('0')          // set field fill character to '0'
        << (c % 1000000000) / 1000000 // format seconds
        << "::"
        << std::setw(3)               // set width of milliseconds field
        << (c % 1000000) / 1000       // format milliseconds
        << "::"
        << std::setw(3)               // set width of microseconds field
        << c % 1000;                  // format microseconds
    auto formatted(oss.str());
    
    0 讨论(0)
  • 2021-01-11 13:11

    Here is a possible solution using a variadic template.
    The if constexpr () makes it c++17 only but replacing with a regular if still works and is c++14 compliant.

    template<class DurationIn, class FirstDuration, class...RestDurations>
    std::string formatDuration(DurationIn d)
    {   
        auto val = std::chrono::duration_cast<FirstDuration>(d);
    
        string out = std::to_string(val.count());
    
        if constexpr(sizeof...(RestDurations) > 0) {
            out += "::" + formatDuration<DurationIn, RestDurations...>(d - val);
        }
    
        return out;
    }
    
    template<class DurationIn>
    std::string formatDuration(DurationIn) { return {}; } // recursion termination
    

    testing in main, outputs "77::600::42"

    auto formattedStr = formatDuration<
        std::chrono::microseconds,
        std::chrono::seconds,
        std::chrono::milliseconds,
        std::chrono::microseconds>(77'600'042us);
    
    0 讨论(0)
  • 2021-01-11 13:15

    Here's the same solution as suggested by @jotik but using the fmt library which results in much more compact code:

    #include <fmt/format.h>
    
    auto c = timeInMicroSec.count();
    auto formatted = fmt::format("{}::{:03}::{:03}",
        (c % 1'000'000'000) / 1'000'000, (c % 1'000'000) / 1'000, c % 1'000);
    
    0 讨论(0)
  • 2021-01-11 13:23

    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.

    0 讨论(0)
提交回复
热议问题