将std::duration转换为易读的时间格式

16

是否有标准实现可以将std::duration打印为人类可读的持续时间?

steady_clock::time_point start = steady_clock::now();
doSomeFoo();
steady_clock::time_point end = steady_clock::now();
std::cout << "Operation took "
          << may_be_std::theMagic(start-end) << std::endl;

应该输出类似于以下内容:

"Operation took 10d:15h:12m:14:s"

或类似的东西。


这个链接 http://en.cppreference.com/w/cpp/chrono/duration 应该会有所帮助 + sprintf - Maciej Lichoń
@MaciejLichoń 不行。它不能用于转换持续时间。例如,121秒应该是2分1秒。虽然duration_cast只能将其转换为2分钟,对于更大的值则超过了60分钟。 - sorush-r
似乎没有这样的东西。 - jrok
你可能想要查看 std::time_put。不幸的是,它使用了表示方式 std::tm - iavr
5个回答

14

同意目前没有标准实现。以下是如何编写自己的实现:

#include <iostream>
#include <iomanip>
#include <chrono>

std::ostream&
display(std::ostream& os, std::chrono::nanoseconds ns)
{
    using namespace std;
    using namespace std::chrono;
    typedef duration<int, ratio<86400>> days;
    char fill = os.fill();
    os.fill('0');
    auto d = duration_cast<days>(ns);
    ns -= d;
    auto h = duration_cast<hours>(ns);
    ns -= h;
    auto m = duration_cast<minutes>(ns);
    ns -= m;
    auto s = duration_cast<seconds>(ns);
    os << setw(2) << d.count() << "d:"
       << setw(2) << h.count() << "h:"
       << setw(2) << m.count() << "m:"
       << setw(2) << s.count() << 's';
    os.fill(fill);
    return os;
};

int
main()
{
    std::cout << "Operation took ";
    display(std::cout, std::chrono::microseconds(918734000000));
    std::cout << '\n';
}

Operation took 10d:15h:12m:14s

刚刚用余数运算符写了一个类似的代码:os << setw(2) << h.count()%60 <<...。我认为你的代码更高效。 - sorush-r

8
基于 Howard 的 answer,我写了这段代码来确保只打印出相关的数据,因此 120 秒将变成 2m00s 而不是 00d:00h:02m00s,并确保剥离前导零,所以它仍然是 2m00s 而不是 02m00s
使用方法很简单:
std::chrono::seconds seconds{60*60*24 + 61};

std::string pretty_seconds = beautify_duration(seconds);
printf("seconds: %s", pretty_seconds.c_str());
>>seconds: 1d00h01m01s

代码:

std::string beautify_duration(std::chrono::seconds input_seconds)
{
    using namespace std::chrono;
    typedef duration<int, std::ratio<86400>> days;
    auto d = duration_cast<days>(input_seconds);
    input_seconds -= d;
    auto h = duration_cast<hours>(input_seconds);
    input_seconds -= h;
    auto m = duration_cast<minutes>(input_seconds);
    input_seconds -= m;
    auto s = duration_cast<seconds>(input_seconds);

    auto dc = d.count();
    auto hc = h.count();
    auto mc = m.count();
    auto sc = s.count();

    std::stringstream ss;
    ss.fill('0');
    if (dc) {
        ss << d.count() << "d";
    }
    if (dc || hc) {
        if (dc) { ss << std::setw(2); } //pad if second set of numbers
        ss << h.count() << "h";
    }
    if (dc || hc || mc) {
        if (dc || hc) { ss << std::setw(2); }
        ss << m.count() << "m";
    }
    if (dc || hc || mc || sc) {
        if (dc || hc || mc) { ss << std::setw(2); }
        ss << s.count() << 's';
    }

    return ss.str();
}

这很棒。我会在返回 ss.str() 之前添加以下代码。else if (!dc && !hc && !mc && !sc) { ss << "0s"; } - swinefeaster

3
这里是一个模板版本,适用于chrono中的所有时间单位。
感谢我之前的回答者们。
template<typename T>
inline std::string format(T timeunit) {
  nanoseconds ns = duration_cast<nanoseconds>(timeunit);
  std::ostringstream os;
  bool foundNonZero  = false;
  os.fill('0');
  typedef duration<int, std::ratio<86400*365>> years;
  const auto y = duration_cast<years>(ns);
  if (y.count()) {
    foundNonZero = true;
    os << y.count() << "y:";
    ns -= y;
  }
  typedef duration<int, std::ratio<86400>> days;
  const auto d = duration_cast<days>(ns);
  if (d.count()) {
    foundNonZero = true;
    os << d.count() << "d:";
    ns -= d;
  }
  const auto h = duration_cast<hours>(ns);
  if (h.count() || foundNonZero) {
    foundNonZero = true;
    os << h.count() << "h:";
    ns -= h;
  }
  const auto m = duration_cast<minutes>(ns);
  if (m.count() || foundNonZero) {
    foundNonZero = true;
    os << m.count() << "m:";
    ns -= m;
  }
  const auto s = duration_cast<seconds>(ns);
  if (s.count() || foundNonZero) {
    foundNonZero = true;
    os << s.count() << "s:";
    ns -= s;
  }
  const auto ms = duration_cast<milliseconds>(ns);
  if (ms.count() || foundNonZero) {
    if (foundNonZero) {
      os << std::setw(3);
    }
    os << ms.count() << ".";
    ns -= ms;
    foundNonZero = true;
  }
  const auto us = duration_cast<microseconds>(ns);
  if (us.count() || foundNonZero) {
    if (foundNonZero) {
      os << std::setw(3);
    }
    os << us.count() << ".";
    ns -= us;
  }
  os << std::setw(3) << ns.count() << "ns" ;
  return os.str();
}

输出:

59y:325d:20h:33m:19s:008.800.999ns
20d:13h:53m:19s:008.800.999ns
33m:19s:008.800.999ns
1s:000.000.999ns
1.000.099ns
10.000ns
100ns

3

这里有一个版本,允许您使用operator<<内联持续时间。它仅打印必要的内容,并允许设置所需的精度:

#include <chrono>
#include <iomanip>
#include <optional>
#include <ostream>

std::ostream& operator<<(std::ostream& os, std::chrono::nanoseconds ns)
{
    using namespace std::chrono;
    using days = duration<int, std::ratio<86400>>;
    auto d = duration_cast<days>(ns);
    ns -= d;
    auto h = duration_cast<hours>(ns);
    ns -= h;
    auto m = duration_cast<minutes>(ns);
    ns -= m;
    auto s = duration_cast<seconds>(ns);
    ns -= s;

    std::optional<int> fs_count;
    switch (os.precision()) {
    case 9: fs_count = ns.count();
        break;
    case 6: fs_count = duration_cast<microseconds>(ns).count();
        break;
    case 3: fs_count = duration_cast<milliseconds>(ns).count();
        break;
    }

    char fill = os.fill('0');
    if (d.count())
        os << d.count() << "d ";
    if (d.count() || h.count())
        os << std::setw(2) << h.count() << ":";
    if (d.count() || h.count() || m.count())
        os << std::setw(d.count() || h.count() ? 2 : 1) << m.count() << ":";
    os << std::setw(d.count() || h.count() || m.count() ? 2 : 1) << s.count();
    if (fs_count.has_value())
        os << "." << std::setw(os.precision()) << fs_count.value();
    if (!d.count() && !h.count() && !m.count())
        os << "s";

    os.fill(fill);
    return os;
}

以下是一些使用示例:
#include <iostream>
#include <chrono>

using namespace std;
using namespace std::chrono_literals;
int main()
{
    cout << 918734032564785ns << "\n";
    cout << setprecision(3) << 918734032564785ns << "\n";
    cout << setprecision(9) << 918734032564785ns << "\n";
    cout << setprecision(0) << 918734032564785ns << "\n";
    cout << setprecision(3) << 432034ms << "\n";
    cout << 14h + 32min + 37s + 645ms << "\n";
    cout << 86472s << "\n";
    cout << 4324ms << "\n";

    return 0;
}

输出:

10d 15:12:14.032564
10d 15:12:14.032
10d 15:12:14.032564785
10d 15:12:14
7:12.034
14:32:37.645
1d 00:01:12.000
4.324s

1

没有标准的实现。你可以通过 std::put_time 获得一个 std::chrono::time_point 的可读版本,但不能获得 std::chrono::duration。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接