Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ How do I convert a std::chrono::time_point to long and back

Tags:

c++

c++11

chrono

I need to convert std::chrono::time_point to and from a long type (integer 64 bits). I´m starting working with std::chrono ...

Here is my code:

int main () {      std::chrono::time_point<std::chrono::system_clock> now = std::chrono::system_clock::now();      auto epoch = now.time_since_epoch();     auto value = std::chrono::duration_cast<std::chrono::milliseconds>(epoch);     long duration = value.count();       std::chrono::duration<long> dur(duration);      std::chrono::time_point<std::chrono::system_clock> dt(dur);      if (dt != now)         std::cout << "Failure." << std::endl;     else         std::cout << "Success." << std::endl; } 

This code compiles, but does not show success.

Why is dt different than now at the end?

What is missing on that code?

like image 454
Mendes Avatar asked Jul 06 '15 20:07

Mendes


People also ask

What is std :: Chrono :: Time_point?

Class template std::chrono::time_point represents a point in time. It is implemented as if it stores a value of type Duration indicating the time interval from the start of the Clock 's epoch. Clock must meet the requirements for Clock or be std::chrono::local_t (since C++20).

What is std :: Chrono :: duration?

Class template std::chrono::duration represents a time interval. It consists of a count of ticks of type Rep and a tick period, where the tick period is a compile-time rational fraction representing the time in seconds from one tick to the next. The only data stored in a duration is a tick count of type Rep .


2 Answers

std::chrono::time_point<std::chrono::system_clock> now = std::chrono::system_clock::now(); 

This is a great place for auto:

auto now = std::chrono::system_clock::now(); 

Since you want to traffic at millisecond precision, it would be good to go ahead and covert to it in the time_point:

auto now_ms = std::chrono::time_point_cast<std::chrono::milliseconds>(now); 

now_ms is a time_point, based on system_clock, but with the precision of milliseconds instead of whatever precision your system_clock has.

auto epoch = now_ms.time_since_epoch(); 

epoch now has type std::chrono::milliseconds. And this next statement becomes essentially a no-op (simply makes a copy and does not make a conversion):

auto value = std::chrono::duration_cast<std::chrono::milliseconds>(epoch); 

Here:

long duration = value.count(); 

In both your and my code, duration holds the number of milliseconds since the epoch of system_clock.

This:

std::chrono::duration<long> dur(duration); 

Creates a duration represented with a long, and a precision of seconds. This effectively reinterpret_casts the milliseconds held in value to seconds. It is a logic error. The correct code would look like:

std::chrono::milliseconds dur(duration); 

This line:

std::chrono::time_point<std::chrono::system_clock> dt(dur); 

creates a time_point based on system_clock, with the capability of holding a precision to the system_clock's native precision (typically finer than milliseconds). However the run-time value will correctly reflect that an integral number of milliseconds are held (assuming my correction on the type of dur).

Even with the correction, this test will (nearly always) fail though:

if (dt != now) 

Because dt holds an integral number of milliseconds, but now holds an integral number of ticks finer than a millisecond (e.g. microseconds or nanoseconds). Thus only on the rare chance that system_clock::now() returned an integral number of milliseconds would the test pass.

But you can instead:

if (dt != now_ms) 

And you will now get your expected result reliably.

Putting it all together:

int main () {     auto now = std::chrono::system_clock::now();     auto now_ms = std::chrono::time_point_cast<std::chrono::milliseconds>(now);      auto value = now_ms.time_since_epoch();     long duration = value.count();      std::chrono::milliseconds dur(duration);      std::chrono::time_point<std::chrono::system_clock> dt(dur);      if (dt != now_ms)         std::cout << "Failure." << std::endl;     else         std::cout << "Success." << std::endl; } 

Personally I find all the std::chrono overly verbose and so I would code it as:

int main () {     using namespace std::chrono;     auto now = system_clock::now();     auto now_ms = time_point_cast<milliseconds>(now);      auto value = now_ms.time_since_epoch();     long duration = value.count();      milliseconds dur(duration);      time_point<system_clock> dt(dur);      if (dt != now_ms)         std::cout << "Failure." << std::endl;     else         std::cout << "Success." << std::endl; } 

Which will reliably output:

Success. 

Finally, I recommend eliminating temporaries to reduce the code converting between time_point and integral type to a minimum. These conversions are dangerous, and so the less code you write manipulating the bare integral type the better:

int main () {     using namespace std::chrono;     // Get current time with precision of milliseconds     auto now = time_point_cast<milliseconds>(system_clock::now());     // sys_milliseconds is type time_point<system_clock, milliseconds>     using sys_milliseconds = decltype(now);     // Convert time_point to signed integral type     auto integral_duration = now.time_since_epoch().count();     // Convert signed integral type to time_point     sys_milliseconds dt{milliseconds{integral_duration}};     // test     if (dt != now)         std::cout << "Failure." << std::endl;     else         std::cout << "Success." << std::endl; } 

The main danger above is not interpreting integral_duration as milliseconds on the way back to a time_point. One possible way to mitigate that risk is to write:

    sys_milliseconds dt{sys_milliseconds::duration{integral_duration}}; 

This reduces risk down to just making sure you use sys_milliseconds on the way out, and in the two places on the way back in.

And one more example: Let's say you want to convert to and from an integral which represents whatever duration system_clock supports (microseconds, 10th of microseconds or nanoseconds). Then you don't have to worry about specifying milliseconds as above. The code simplifies to:

int main () {     using namespace std::chrono;     // Get current time with native precision     auto now = system_clock::now();     // Convert time_point to signed integral type     auto integral_duration = now.time_since_epoch().count();     // Convert signed integral type to time_point     system_clock::time_point dt{system_clock::duration{integral_duration}};     // test     if (dt != now)         std::cout << "Failure." << std::endl;     else         std::cout << "Success." << std::endl; } 

This works, but if you run half the conversion (out to integral) on one platform and the other half (in from integral) on another platform, you run the risk that system_clock::duration will have different precisions for the two conversions.

like image 56
Howard Hinnant Avatar answered Sep 24 '22 13:09

Howard Hinnant


I would also note there are two ways to get the number of ms in the time point. I'm not sure which one is better, I've benchmarked them and they both have the same performance, so I guess it's a matter of preference. Perhaps Howard could chime in:

auto now = system_clock::now();  //Cast the time point to ms, then get its duration, then get the duration's count. auto ms = time_point_cast<milliseconds>(now).time_since_epoch().count();  //Get the time point's duration, then cast to ms, then get its count. auto ms = duration_cast<milliseconds>(tpBid.time_since_epoch()).count(); 

The first one reads more clearly in my mind going from left to right.

like image 21
Feem Avatar answered Sep 23 '22 13:09

Feem