Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert std::chrono::system_clock::time_point to struct timeval and back

Tags:

c++

c

c++11

I´m writing a C++ code that needs to access an old C library that uses timeval as a representation of the current time.

In the old package to get the current date/time we used:

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

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

Now I need to use C++ chrono, something as:

    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);

Later in code I need the way back, building a time_point variable from the returned struct timeval:

    struct timeval dateTime;
    function(&dateTime);

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

I´m using C++11.

like image 353
Mendes Avatar asked Sep 09 '16 23:09

Mendes


2 Answers

Here is how to do the conversion both without using manual conversion factors, or depending upon the unspecified rounding mode of 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 takes care to round the tp down (in case it is negative). The POSIX spec is a bit vague on this but I'm assuming that timeval represents time points prior to the epoch with negative tv_sec values, and then positive tv_usec values. Then it is a simple operation to find the microseconds since the last second.

If I'm incorrect about my assumption (and a more precise POSIX spec can be found), <chrono> has the power to model whatever the heck it does.

The reverse conversion, assuming the conventions above, is incredibly readable. It requires no comment.

This can all be tested like this:

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}});
}

This is all predicated on the assumption that the epoch for timeval and system_clock are identical. This is not specified, but is true for all existing implementations. With any luck we can standardize this existing practice in the near future.

Be aware that in POSIX timeval is used both as a time_point and a duration. So to_time_point could result in a run time error if the timeval is currently representing a time duration. And to_timeval could result in a run time error if the client interprets the result as a time duration.

like image 77
Howard Hinnant Avatar answered Nov 06 '22 09:11

Howard Hinnant


[Edited to use time_val instead of free vars]

Assuming you trust your system_clock with milliseconds accuracy, you can go like this:

  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;

Use std::chrono::microseconds in duration_cast and adjust your (div/mod) code accordingly for the higher precision - take care on how much you trust the accuracy of the values you obtain.

The conversion back is:

  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)
  ;
like image 7
Adrian Colomitchi Avatar answered Nov 06 '22 10:11

Adrian Colomitchi