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?
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).
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 .
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_cast
s 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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With