0

I am using in my projects some time_point<steady_clock> variables in order to do operations at a specific interval. I want to serialize/deserialize in a file those values.

But it seems that the time_since_epoch from a steady_clock is not reliable, although time_since_epoch from a system_clock is quite ok, it always calculates the time from 1970/1/1 (UNIX time)

What's the best solution for me? It seems that I have to convert somehow from steady_clock to system_clock but I don't think this is achievable.

P.S. I already read the topic here: Persisting std::chrono time_point instances

Taw
  • 479
  • 3
  • 15
  • Why do you want to serialise/deserialize it? `steady_clock` only ensures monotonicity and there is nothing non reliable in `time_since_epoch` you just need to be aware what it measures. For instance, instead you could measure time since program's start - just measure steady clock at startup and store it in a global variable. – ALX23z Jun 03 '21 at 11:09
  • @ALX23z so my program does a polling to a server every hour to update some internal fields and I want to save some data to the disk cache, including this. For example if the polling is done at 12:00, I save this data to disk, I restart the program at 12:50 and I want to do the next polling at 13:00 – Taw Jun 03 '21 at 11:33
  • another scenario is that I have a memory cache with TTL and those have `expireAt` timepoints, which I want also to store to disk. – Taw Jun 03 '21 at 11:34
  • For such purposes you should inherently use system clock. Steady clock is used for measuring time differences - primarily small intervals. – ALX23z Jun 03 '21 at 12:54
  • I know that for measuring small time differences there is `high_resolution_clock`, `steady_clock` is used when you don't to be affected by PC/OS clock https://stackoverflow.com/a/13263328/1134813, which is exactly what I want. If someone changes OS clock, I don't my internal calculation to be messed up... – Taw Jun 03 '21 at 13:46
  • You've already read the correct answer to your question. There's not another correct answer. Your computer has two clocks. One of them is a stop watch. The other is responsible for tracking UTC. And on any unsecured computer someone can mess with either of those clocks. – Howard Hinnant Jun 03 '21 at 14:03

1 Answers1

2

On the cppreference page for std::chrono::steady_clock, it says:

This clock is not related to wall clock time (for example, it can be time since last reboot), and is most suitable for measuring intervals.

The page for std::chrono::system_clock says that most implementations use UTC as an epoch:

The epoch of system_clock is unspecified, but most implementations use Unix Time (i.e., time since 00:00:00 Coordinated Universal Time (UTC), Thursday, 1 January 1970, not counting leap seconds).

If you're trying to compare times across machines or hoping to correlate the recorded times to real world events (i.e. at 3pm today there was an issue), then you'll want to switch your code over to using the system clock. Anytime you reboot the steady clock will reset and it doesn't relate to wall time at all.

Edit: if you wanted to do an approximate conversion between steady and system timestamps you could do something like this:

template <typename To, typename FromTimePoint>
typename To::time_point approximate_conversion(const FromTimePoint& from) {
    const auto to_now = To::now().time_since_epoch();
    const auto from_now = FromTimePoint::clock::now().time_since_epoch();

    // compute an approximate offset between the clocks and apply that to the input timestamp
    const auto approx_offset = to_now - from_now;
    return typename To::time_point{from.time_since_epoch() + approx_offset};
}

int main() {
    auto steady = std::chrono::steady_clock::now();
    auto system = approximate_conversion<std::chrono::system_clock>(steady);
}

This assumes the clocks don't drift apart very quickly, and that there are no large discontinuities in either clock (both of which are false assumptions over long periods of time).

mattlangford
  • 1,260
  • 1
  • 8
  • 12
  • thanks for the answer, yes, that's what I am trying to do. internally in the code I need only precise time intervals, so I can do a polling to to a server each 1h, so `steady_clock` is perfect. But now I want to serialize this data into a file. So next time I open the application, I read the file and get the `last_polling_time`. But now the 2 clocks, `steady` and `system` mix one with the other it it won't work... – Taw Jun 03 '21 at 15:23
  • You could keep track of the last polling time (as a `system` time) and then save that when you serialize. When you reopen you'd check the current `system` time and then do the next poll accordingly. That way you wouldn't need to save the `steady` timestamps but instead would only need to keep track of a single `system` time. – mattlangford Jun 03 '21 at 15:30
  • yes, but I know that `steady` is more safe and more reliable when calculating intervals, because it does not depend on "outside world" (ex: sync with a time server). Moving to `system` time would help indeed. – Taw Jun 03 '21 at 15:38
  • 1
    I added an edit that shows how you could convert between clocks if you wanted to do that. It's worth noting that this sort of conversion isn't very safe to do over long periods of time. – mattlangford Jun 03 '21 at 16:06
  • thanks, unfortunately it does not compile on windows – Taw Jun 06 '21 at 16:55
  • Interesting, if you want to reply with errors I could try to help? – mattlangford Jun 07 '21 at 13:45
  • very strange, I also added the compiler explorer link, but it seems that SO deleted it: https://godbolt.org/z/vcEKxW5Md – Taw Jun 07 '21 at 13:50
  • xample.cpp (10): error C2440: '': cannot convert from 'initializer list' to 'std::chrono::system_clock::time_point' (10): note: No constructor could take the source type, or constructor overload resolution was ambiguous (15): note: see reference to function template instantiation 'std::chrono::system_clock::time_point approximate_conversion(const FromTimePoint &)' being compiled with [ FromTimePoint=std::chrono::steady_clock::time_point ] – Taw Jun 07 '21 at 13:51
  • 1
    Try changing the return line to: `return To::time_point{std::chrono::duration_cast(from.time_since_epoch() + approx_offset)};` – mattlangford Jun 07 '21 at 14:06
  • Thank you again, it works, I wonder if system time epoch can be smaller than steady time epoch in any case, because the standard does not specify anything about this. – Taw Jun 17 '21 at 08:56
  • 1
    Good question, I think the solution I’ve outlined should work regardless though since durations can be negative – mattlangford Jun 17 '21 at 13:51