Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Persisting std::chrono time_point instances

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?

like image 667
Zamfir Kerlukson Avatar asked Mar 10 '14 03:03

Zamfir Kerlukson


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 does std :: Chrono :: System_clock :: now return?

std::chrono::system_clock::now Returns a time point representing with the current point in time.

What is std :: Chrono :: Steady_clock?

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.


2 Answers

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.

like image 85
Howard Hinnant Avatar answered Oct 13 '22 21:10

Howard Hinnant


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.

like image 23
Cheers and hth. - Alf Avatar answered Oct 13 '22 19:10

Cheers and hth. - Alf