9

My goal is to store timestamps in the range of nanoseconds. Let's say that I have two machines that needs to communicate, at precisely what time a task is done.

Let's just for the sake of the question, say that they a synchronized clocks.
Machine A stores its local time when it's done. Machine B transmits to A, at which time it is done. Machine A then calculates the difference.

My problem is, that the timedifference can actually be up to 60 seconds, but also down to .25ms. So I need to store the time in such a manner, that I can handle both, and calculate the timedifference.

Since time since epoch in nanoseconds is such a big number, I don't think I can store that in any variable that I know of.

How would you tackle this situation? My current though is, that I probably (somehow) could scale the time since epoch in ns down to the bits representing 1 minute, though I have no idea how to do that.

Hope you can help.

Best regards

Benjamin Larsen
  • 560
  • 1
  • 7
  • 21

2 Answers2

22

Just use a 64-bit integer, signed (int64_t) or unsigned (uint64_t), and you'll be all fine.

The 64-bit space is really huge. It is not only enough to store time differences, but even to store absolute time values, which are usually measured as time passed since 1970-01-01 midnight.

For example, right now the number of seconds since 1970-01-01 is roughly:

1492432621.123456789

In nano-seconds, this is:

1492432621123456789

For comparison, the largest signed 64-bit integer value is:

9223372036854775807

This corresponds roughly to 2262-04-12, a date at which you almost certainly won't be alive anymore.

However, if you are still concerned by the possibility that your software runs for more than two centuries, won't be improved during that time, and will eventually crash, then you should either use unsigned 64-bit integers, which will give it some more centuries, or you should use 128-bit integers, which are almost certainly safe until the universe dies. Note that 128-bit integers are supported by all modern 64-bit architectures (__int128 or unsigned __int128), so you can safely use them, unless, of course, you need to support legacy 32-bit systems. Or 16-bit systems. 8-bit, anyone?

vog
  • 23,517
  • 11
  • 59
  • 75
  • 1
    A little more than 584 years fit inside an unsigned 64-bit integer that has nanoseconds precision. So yeah, I think the OP will be fine. – rwols Apr 17 '17 at 12:39
  • I look forward to this software dying of a Y2K262 problem in 245 years (yes, we'll still use the K, even when it doesn't actually make the string shorter). :-) – ShadowRanger Apr 17 '17 at 12:48
  • My guess is that my software won't last to the end of the universe. I'll take it! Thanks! – Benjamin Larsen Apr 17 '17 at 13:09
  • At 2262-04-13 the maintainers will curse him, wouldn't it be better to store milliseconds and the fractional part separately? – Gizmo Apr 17 '17 at 13:13
  • You're not considering applications that may need to represent long durations, or to represent time points far in the past or in the future; it is not a requirement that the OP be alive (or that the program be running) in 2262, for that to be a possibility. – Lightness Races in Orbit Apr 17 '17 at 13:20
  • `128-bit integers are supported by all modern 64-bit architectures` [except MSVC](http://stackoverflow.com/q/6759592/995714) – phuclv Apr 17 '17 at 15:27
  • @LưuVĩnhPhúc: That might be a good opportunity to switch to GCC, e.g. through mingw64. ;-) – vog May 08 '17 at 12:19
6

I recommend using std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds>. You can make this much prettier with type aliases. For example:

template <class Duration>
    using sys_time = std::chrono::time_point<std::chrono::system_clock, Duration>;
using sys_nanoseconds = sys_time<std::chrono::nanoseconds>;

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

This gets you the same range as the currently accepted answer (int64_t), but adds type-safety. An int64_t might mean a time point, or a time duration. sys_nanoseconds can mean only a time_point, and will result in a compile time error if used like a time duration.

For example it is a compile-time error to add two sys_nanoseconds together. But you can subtract two sys_nanoseconds and the resultant type is std::chrono::nanoseconds.

Your range (before overflow/underflow) will be:

1677-09-21 00:12:43.145224192 to 2262-04-11 23:47:16.854775807

To get a larger range with nanosecond precision is of questionable value, but can be done like this on platforms that support 128 bit types:

using nano128 = std::chrono::duration<__int128_t, std::nano>;
using sys_nano = sys_time<nano128>;

Now your range is far greater than +/- the age of the universe.

Note that on some platforms, std::chrono::system_clock::now() will not report with an accuracy of nanoseconds. So storing nanosecond timestamps may be overkill. However it is trivial to take the above code and change it to whatever units you want.

On macOS, system_clock::now() reports microseconds, which will have 1000 times the range of nanoseconds. On Windows system_clock::now() reports in units of 1/10 of a microseconds:

using win_sys_duration = std::chrono::duration<std::int64_t,
                                               std::ratio<1, 10'000'000>>;
using win_sys_time_point = sys_time<win_sys_duration>;

And on gcc, system_clock::now() reports nanoseconds.

Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577