What is the correct way to persist std::chrono time_point instances and then read them back into another instance of the same type?
typedef std::chrono::time_point<std::chrono::high_resolution_clock> time_point_t;
time_point_t tp = std::chrono::high_resolution_clock::now();
serializer.write(tp);
.
.
.
time_point_t another_tp;
serializer.read(another_tp);
The calls to write/read, assume that the instance of type time_point_t, can be somehow converted to a byte representation, which can then be written to or read from a disk or a socket etc.
A possible solution suggested by Alf is as follows:
std::chrono::high_resolution_clock::time_point t0 = std::chrono::high_resolution_clock::now();
//Generate POD to write to disk
unsigned long long ns0 = t0.time_since_epoch().count();
//Read POD from disk and attempt to instantiate time_point
std::chrono::high_resolution_clock::duration d(ns0)
std::chrono::high_resolution_clock::time_point t1(d);
unsigned long long ns1 = t1.time_since_epoch().count();
if ((t0 != t1) || (ns0 != ns1))
{
std::cout << "Error time points don't match!\n";
}
Note: The above code has a bug as the final instantiated time point does not match the original.
In the case of of the old style time_t, one typically just writes the entire entity to disk based on its sizeof and then reads it back the same way - In short what would be the equivalent for the new std::chrono types?
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).
std::chrono::system_clock::now Returns a time point representing with the current point in time.
Class std::chrono::steady_clock represents a monotonic clock. The time points of this clock cannot decrease as physical time moves forward and the time between ticks of this clock is constant.
Reading from a disk or socket implies that you might be reading in an instance of the application that did not do the write. And in this case, serializing the duration alone is not sufficient.
A time_point
is a duration
amount of time since an unspecified epoch. The epoch could be anything. On my computer the epoch of std::chrono::high_resolution_clock
is whenever the computer booted. I.e. this clock reports the number of nanoseconds since boot.
If one application writes the time_since_epoch().count(), the computer is rebooted, and then another (or even the same) application reads it back in, the read in value has no meaning whatsoever, unless you happen to somehow know the amount of time between boots.
To reliably serialize a time_point
one has to arrange for the writer and the reader to agree upon some epoch, and then ensure that the time_point
written and read is with respect to that epoch. For example one might arrange to use the POSIX epoch: New Years 1970 UTC.
As it turns out, every std::chrono::system_clock
implementation I'm aware of uses Unix time, a close approximation of UTC measured from New Years 1970. However I know of no common epoch for std::chrono::high_resolution_clock
.
Only if you can somehow ensure that the reader and writer clocks agree upon a common epoch, can you serialize a time_point as a duration.
the time_point
constructor takes a duration
, and you can get a duration
from member time_since_epoch
. thus the question reduces to serialize a duration
value. and duration
has a constructor that takes a number of ticks, and a member function count
that produces the number of ticks.
all this just by googling std::chrono::time_point
and looking at the cppreference documentation google landed me on.
it's often a good idea to read the documentation.
Addendum: an example.
#include <chrono>
#include <iostream>
#include <typeinfo>
using namespace std;
auto main() -> int
{
using Clock = chrono::high_resolution_clock;
using Time_point = Clock::time_point;
using Duration = Clock::duration;
Time_point const t0 = Clock::now();
//Generate POD to write to disk
Duration::rep const ns0 = t0.time_since_epoch().count();
//Read POD from disk and attempt to instantiate time_point
Duration const d(ns0);
Time_point const t1(d);
cout << "Basic number type is " << typeid( ns0 ).name() << "." << endl;
if( t0 != t1 )
{
cout << "Error time points don't match!" << endl;
}
else
{
cout << "Reconstituted time is OK." << endl;
}
}
With Visual C++ 12.0 the reported basic type is __int64
, i.e. long long
, while with g++ 4.8.2 in Windows the reported type is x
, which presumably means the same.
With both compilers the reconstituted time is identical to the original.
Addendum: As noted by Dina in the comments, as of C++14 the C++ standard doesn't specify the epoch, and so to make this work across machines or with different clocks it's necessary to add additional steps that normalize the epoch for the serialized data, e.g. and most naturally to Posix time, i.e. time since since 00:00:00 Coordinated Universal Time (UTC), Thursday, 1 January 1970.
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