将std::chrono::system_clock::time_point转换为struct timeval,然后再转回去

13

我正在编写一个需要访问旧的C库的C++代码,该库使用timeval表示当前时间。

在旧包中获取当前日期/时间的方法如下:

struct timeval dateTime;
gettimeofday(&dateTime, NULL);

function(dateTime); // The function will do its task

现在我需要使用C++ chrono,类似于:

    system_clock::time_point now = system_clock::now();
    struct timeval dateTime;

    dateTime.tv_sec = ???? // Help appreaciated here
    dateTime.tv_usec = ???? // Help appreaciated here

    function(dateTime);

接下来在代码中我需要回溯,从返回的 struct timeval 构建一个 time_point 变量:

    struct timeval dateTime;
    function(&dateTime);

    system_clock::time_point returnedDateTime = ?? // Help appreacited

我正在使用C++11。

3个回答

7

[改用time_val而不是自由变量进行编辑]

假设您信任您的system_clock具有毫秒级的准确性,您可以按照以下方式操作:

  struct timeval dest;
  auto now=std::chrono::system_clock::now();
  auto millisecs=
    std::chrono::duration_cast<std::chrono::milliseconds>(
        now.time_since_epoch()
    );;
  dest.tv_sec=millisecs.count()/1000;
  dest.tv_usec=(millisecs.count()%1000)*1000;

  std::cout << "s:" << dest.tv_sec << " usec:" << dest.tv_usec << std::endl;

使用std::chrono::microsecondsduration_cast中,并相应地调整你的(div/mod)代码以获得更高的精度 - 注意你对所获得值的准确性有多少信任。

转换回来的方法是:

  timeval src;

  // again, trusting the value with only milliseconds accuracy
  using dest_timepoint_type=std::chrono::time_point<
    std::chrono::system_clock, std::chrono::milliseconds
  >;
  dest_timepoint_type converted{
    std::chrono::milliseconds{
      src.tv_sec*1000+src.tv_usec/1000
    }
  };

  // this is to make sure the converted timepoint is indistinguishable by one
  // issued by the system_clock
  std::chrono::system_clock::time_point recovered =
      std::chrono::time_point_cast<std::chrono::system_clock::duration>(converted)
  ;

应该将值分配给 dateTime.tv_secdateTime.tv_usec,因为目标是得到一个有效的 timeval 结构。 - Kyle Strand
millisecs.count()/1000l;dest.tv_usec=millisecs.count()%1000l; 中的 l 明显是不必要的。这只是一个小问题。 - chux - Reinstate Monica

5
以下是如何进行转换,既不使用手动转换因子,也不依赖于time_t的未指定舍入模式:
timeval
to_timeval(std::chrono::system_clock::time_point tp)
{
    using namespace std::chrono;
    auto s = time_point_cast<seconds>(tp);
    if (s > tp)
        s = s - seconds{1};
    auto us = duration_cast<microseconds>(tp - s);
    timeval tv;
    tv.tv_sec = s.time_since_epoch().count();
    tv.tv_usec = us.count();
    return tv;
}

std::chrono::system_clock::time_point
to_time_point(timeval tv)
{
    using namespace std::chrono;
    return system_clock::time_point{seconds{tv.tv_sec} + microseconds{tv.tv_usec}};
}

to_timeval会确保向下取整tp(如果它是负数)。POSIX规范在这方面有点含糊,但我认为timeval表示先于纪元的时间点,其tv_sec值为负,然后tv_usec值为正。然后可以简单地找到自上一个second以来的microseconds

如果我的假设不正确(并且可以找到更精确的POSIX规范),<chrono>有能力模拟任何它想要的东西。

反向转换非常易读,做了不需要注释。

这一切都可以像这样进行测试:

timeval
make_timeval(time_t s, long us)
{
    timeval tv;
    tv.tv_sec = s;
    tv.tv_usec = us;
    return tv;
}

bool
operator==(timeval x, timeval y)
{
    return x.tv_sec == y.tv_sec && x.tv_usec == y.tv_usec;
}

int
main()
{
    using namespace std::chrono;
    assert(make_timeval(0, 0) == to_timeval(system_clock::time_point{}));
    assert(make_timeval(1, 0) == to_timeval(system_clock::time_point{seconds{1}}));
    assert(make_timeval(1, 400000) == to_timeval(system_clock::time_point{seconds{1} + microseconds{400000}}));
    assert(make_timeval(-1, 400000) == to_timeval(system_clock::time_point{seconds{-1} + microseconds{400000}}));

    assert(to_time_point(make_timeval(0, 0)) == system_clock::time_point{});
    assert(to_time_point(make_timeval(1, 0)) == system_clock::time_point{seconds{1}});
    assert(to_time_point(make_timeval(1, 400000)) == system_clock::time_point{seconds{1} + microseconds{400000}});
    assert(to_time_point(make_timeval(-1, 400000)) == system_clock::time_point{seconds{-1} + microseconds{400000}});
}

这一切都基于这样一个假设: timevalsystem_clock 的时代是相同的。虽然没有规定,但对于所有现有的实现都是正确的。希望我们能在不久的将来统一这个现有的惯例。

请注意,在 POSIX 中,timeval 既用作time_point,也用作duration。因此,如果timeval当前表示时间间隔,则to_time_point可能会导致运行时错误。而如果客户端将结果解释为时间间隔,则to_timeval可能会导致运行时错误。


time_point_cast 仅对时刻值 tp 在纪元之后的值向下舍入。对于纪元之前的值,time_point_cast 则向上舍入。在使用 C++17 或更高版本时,可以用以下简单代码替换 time_point_cast 和此 if 的组合:auto s = floor<seconds>(tp); - Howard Hinnant

4
请看std::chrono::system_clock::to_time_t(),它将time_point转换为time_t,成为您的tv_sec。您不会得到tv_usec,您可以将其设置为0;或者您可以通过使用duration_cast来提取time_point中的秒级小数部分等方法来调整。 from_time_t() 则反过来进行转换。请注意保留HTML标记。

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