When Is Std::Chrono Epoch

When is std::chrono epoch?

It is a function of both the specific clock the time_point refers to, and the implementation of that clock. The standard specifies three different clocks:

  • system_clock
  • steady_clock
  • high_resolution_clock

And the standard does not specify the epoch for any of these clocks.

Programmers (you) can also author their own clocks, which may or may not specify an epoch.

There is a de-facto (unofficial) standard that std::chrono::system_clock::time_point has an epoch consistent with Unix Time. This is defined as the time duration that has elapsed since 00:00:00 Coordinated Universal Time (UTC), Thursday, 1 January 1970, not counting leap seconds.

Fwiw, here is a date/time library which takes advantage of this de-facto standard.

There is no de-facto standard for the other two std-specified clocks. Additionally high_resolution_clock is permitted to be a type alias for either system_clock or steady_clock.

On OS X, high_resolution_clock is a type alias for steady_clock, and steady_clock is a count of nanoseconds since the computer booted (no relationship whatsoever to UTC).

Update

The draft C++2a spec now says for system_clock:

Objects of type sys_time<Duration> measure time since (and before)
1970-01-01 00:00:00 UTC excluding leap seconds. This measure is
commonly referred to as Unix time. This measure facilitates an
efficient mapping between sys_timeand calendar types (27.8).
[Example:
sys_seconds{sys_days{1970y/January/1}}.time_since_epoch() is 0s.
sys_seconds{sys_days{2000y/January/1}}.time_since_epoch() is
946’684’800s, which is 10’957 * 86’400s. —end example]

Additionally, C++2a introduces utc_clock, tai_clock, gps_clock and file_clock. These clocks also have well-defined epochs as one can clock_cast time_points from one clock to another among these and system_clock.

The file_clock epoch will not be portable, but you will still be able to relate its time_points to the civil calendar.

utc_clock is like system_clock, except that it does not ignore leap seconds. For example:

#include <chrono>
#include <iostream>

int
main()
{
using namespace std::chrono;
auto s1 = sys_days{December/31/2016} + 23h + 59min + 59s;
auto s2 = sys_days{January/1/2017};
auto u1 = clock_cast<utc_clock>(s1);
auto u2 = clock_cast<utc_clock>(s2);
std::cout << s2 - s1 << '\n';
std::cout << u2 - u1 << '\n';
}

Outputs:

1s
2s

Update

Link to the now specified (C++20) system_clock epoch: http://eel.is/c++draft/time.clock.system#overview-1

Objects of type system_­clock represent wall clock time from the
system-wide realtime clock. Objects of type sys_­time<Duration>
measure time since 1970-01-01 00:00:00 UTC excluding leap seconds.
This measure is commonly referred to as Unix time. This measure
facilitates an efficient mapping between sys_­time and calendar
types ([time.cal]). [ Example:
sys_­seconds{sys_­days{1970y/January/1}}.time_­since_­epoch() is
0s. sys_­seconds{sys_­days{2000y/January/1}}.time_­since_­epoch()
is 946'684'800s, which is 10'957 * 86'400s.
— end example ]

std::chrono: Set clock's epoch to 1/1/0000

I would avoid std::time_t altogether. Using days_from_civil from chrono-Compatible Low-Level Date Algorithms, you can immediately compute any difference between std::chrono::system_clock::time_point, and any date in the proleptic Gregorian calendar1.

In addition to days_from_civil which takes a year/month/day triple and converts it into a count of days before/since 1970-01-01 (a chrono-compatible epoch), it is also convenient to create a custom chrono::duration to represent 24 hours:

typedef std::chrono::duration
<
int,
std::ratio_multiply<std::ratio<24>, std::chrono::hours::period>
> days;

Now you can create any epoch you want with just:

constexpr days epoch = days(days_from_civil(0, 1, 1));  // 0000-01-01

In C++1y this is even a compile-time computation!

And you can subtract this std::chrono::duration from any other std::chrono::duration:

auto delta = std::chrono::system_clock::now().time_since_epoch() - epoch;

delta is now a std::chrono::duration representing the amount of time between now, and 0000-01-01. You can then print that out however you want, or otherwise manipulate it. For example here is an entire working demo:

#include "../date_performance/date_algorithms"
#include <iostream>
#include <chrono>

typedef std::chrono::duration
<
int,
std::ratio_multiply<std::ratio<24>, std::chrono::hours::period>
> days;

int
main()
{
constexpr days epoch = days(days_from_civil(0, 1, 1));
auto delta = std::chrono::system_clock::now().time_since_epoch() - epoch;
days d = std::chrono::duration_cast<days>(delta);
std::cout << "It has been " << d.count() << " days, ";
delta -= d;
auto h = std::chrono::duration_cast<std::chrono::hours>(delta);
std::cout << h.count() << " hours, ";
delta -= h;
auto m = std::chrono::duration_cast<std::chrono::minutes>(delta);
std::cout << m.count() << " minutes, ";
delta -= m;
auto s = std::chrono::duration_cast<std::chrono::seconds>(delta);
std::cout << s.count() << " seconds ";
std::cout << " since 0000-01-01\n";
}

Which for me output:

It has been 735602 days, 19 hours, 14 minutes, 32 seconds  since 0000-01-01

A word of warning about overflow:

The std::chrono::system_clock::time_point::duration is not guaranteed to have a range large enough to do this. It turns out that on my system it does. It is microseconds in a signed long long which will span +/- 292,000 years. If you need to avoid an overflow problem, you could truncate your std::chrono::system_clock::time_point::duration to courser units (e.g. seconds or days) to extend the range prior to subtracting 0000-01-01.

I got to thinking

And that usually leads to a disaster. However in this case I decided I should add to this post anyway. This:

constexpr days epoch = days(days_from_civil(0, 1, 1));

has type days, which is a duration. But it really isn't a duration. It is a point in time. It is a date. It is a time_point with a coarse precision. By introducing a new typedef, the code in this post can be cleaned up just a little bit more:

typedef std::chrono::time_point<std::chrono::system_clock, days> date_point;

Now instead of writing:

constexpr days epoch = days(days_from_civil(0, 1, 1));

One can write:

constexpr date_point epoch{days(days_from_civil(0, 1, 1))};

But even more importantly, instead of:

auto delta = std::chrono::system_clock::now().time_since_epoch() - epoch;

we can now write:

auto delta = std::chrono::system_clock::now() - epoch;

This delta still has exactly the same type and value as it did previously, and everything else in the demo still proceeds as exactly as it did before.

This is both a small change, and a big change. By treating epoch as a time_point instead of a duration, the algebra of time_point's and duration's works for us, both simplifying and type-checking our expressions to help us write cleaner code with fewer mistakes.

For example one can add two duration's together. But it doesn't make any sense at all to:

epoch + epoch

By using time_point instead of duration for the type of epoch, the compiler catches such non-sensical expressions at compile time.

1The proleptic Gregorian calendar has a year 0. In the year 0 it is 2 days behind the Julian calendar. Using a year 0 is also consistent with ISO 8601. As long as all parties involved know what calendar you are using, then everything is fine. Conversion between non-positive years and "BC years" is trivial if desired.

std::chrono default duration for time_since_epoch

The type of the duration is std::chrono::high_resolution_clock::duration. You can inspect a duration's tick period with: std::chrono::high_resolution_clock::duration::period::num and std::chrono::high_resolution_clock::duration::period::den. This is the numerator and denominator of a fraction representing the amount of seconds per tick (e.g. 1/1000000000 for nanoseconds).

The epoch is unspecified, but for you is 1389375799048790227 ticks from when you got that result.

What does time_since_epoch() actually represent in a std::chrono::local_time?

The value in a local_time is the exact same value it would have in a sys_time. For example:

auto lt = local_days{June/3/2021} + 18h + 30min;

lt is a local time with the indicated value. All one has to do change this to a sys_time is change local_days to sys_days:

auto st = sys_days{June/3/2021} + 18h + 30min;

I.e. one can now assert that st.time_since_epoch() == lt.time_since_epoch(). The only difference between lt and st is semantics.

So you can tell clients to consume this number as if it is Unix Time, which it can then derive year, month, day, time-of-day information, but then treat that information as a local time in (presumably) their local time zone.

In doing that "reinterpret cast", it is quite possible that the local indicated time may not exist, or may be ambiguous because there are two of them. One can up the odds of not hitting such a situation by avoiding times of day in the range 00:00:00 - 04:00:00. If one does hit this situation, there is no one right answer on how you handle it. You'll just have to state a policy along with the rest of your documentation.

...

Or maybe they just write their parser in C++20... :-)

How to create a time point from milliseconds since Unix epoch?

This is easier/simpler:

std::chrono::system_clock::time_point tp{std::chrono::milliseconds{m}};

The above has a precision of system_clock::precision (microseconds on macOS, nanoseconds on Linux systems, and 1/10 microseconds on Windows). You could also create a time_point with a precision of milliseconds if desired:

std::chrono::time_point<std::chrono::system_clock, std::chrono::milliseconds>
tp{std::chrono::milliseconds{m}};

In C++20 this can be simplified to:

std::chrono::sys_time tp{std::chrono::milliseconds{m}};

sys_time is just a template type alias for the system_clock family of time_points at any precision. I.e. the above is the exact same type as the milliseconds precision time_point created before.

Also, is std::chrono::time_point even the recommended way to represent "instants" in time? Or should std::time_t be preferred?

I recommend std::chrono::system_clock::time_point over std::time_t:

  • time_point's based on system_clock have a well-defined epoch (in C++20) which is also a de-facto standard in C++17: It counts time since 1970-01-01 00::00:00 UTC, excluding leap seconds. This is also known as Unix Time. In contrast no C or C++ standard specifies the epoch of time_t, though using Unix Time is common practice, and specified by POSIX.

  • Though unspecified, time_t typically has a precision of seconds. system_clock::time_point typically has a precision that is millions or billions finer than that. The exact precision is not specified, but it is documented within the API so you can discover it at compile-time or run-time. system_clock::period is the same std::ratio as system_clock::time_point::period and represents a compile-time fraction of a second from one tick to the next.

  • time_t is typically just a 32 or 64 bit signed integral. This has no type safety in generic code. For example you can add two time_t's and it compiles. However adding two points in time is not logical (while subtracting them is). The chrono library catches such logic bugs at compile-time. Adding two time_point's does not compile. But you can add a time_point and any duration. The logical algebra of time_points and durations is checked for you at compile-time.

  • If you need to cover leap seconds, it is unspecified but common that time_t does not (typically it is Unix Time). With system_clock, Unix Time is specified (you know you aren't counting leap seconds). However in C++20 there is another chrono clock that does include leap seconds in its count: std::chrono::utc_clock. Like all chrono clocks this clock has it's own type-safe family of time_points, with its own convenience template type alias called utc_time<Duration>. And you can convert between them using std::chrono::clock_cast.

like this:

auto tp_sys = clock_cast<system_clock>(tp_utc);

std::chrono::system_clock and C time

If you assume that std::chrono::system_clock has the same epoch as time_t (it usually does) then to_time_t and from_time_t are essentially just a std::duration_cast.

For example this code should print the same values twice (note that whether to_time_t rounds or truncates is unspecified and duration_cast always truncates so there might be a 1 second difference):

const auto now = std::chrono::system_clock::now();
time_t time = std::chrono::system_clock::to_time_t(now);
std::cout << time << "\n";
std::cout << std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count() << "\n";
std::cout << std::chrono::system_clock::from_time_t(time).time_since_epoch().count() << "\n";
std::cout << std::chrono::system_clock::time_point(std::chrono::seconds(time)).time_since_epoch().count() << "\n";

You can therefore write your own to/from_time_t functions fairly easily:

constexpr time_point  my_point_from_time_t(time_t value)
{
return time_point(std::chrono::seconds(value));
}

constexpr time_t my_point_to_time_t(const time_point & value)
{
return std::chrono::duration_cast<std::chrono::seconds>(value.time_since_epoch()).count();
}

If system_clock doesn't use the same epoch as time_t then you can simply adjust the values:

const time_t system_clock_epoch = std::chrono::system_clock::to_time_t({});

time_point my_point_from_time_t(time_t value)
{
return time_point(std::chrono::seconds(value - system_clock_epoch));
}

time_t my_point_to_time_t(const time_point & value)
{
return std::chrono::duration_cast<std::chrono::seconds>(value.time_since_epoch()).count() + system_clock_epoch;
}

NTP timestamps using std::chrono

Your tick period: 1/268'435'455 is unfortunately both extremely fine and also doesn't lend itself to much of a reduced fraction when your desired conversions are used (i.e. between system_clock::duration and NTPClock::duration. This is leading to internal overflow of your unsigned long long NTPClock::rep.

For example, on Windows the system_clock tick period is 1/10,000,000 seconds. The current value of now() is around 1.6 x 1016. To convert this to NTPClock::duration you have to compute 1.6 x 1016 times 53,687,091/2,000,000. The first step in that is the value times the numerator of the conversion factor which is about 8 x 1023, which overflows unsigned long long.

There's a couple of ways to overcome this overflow, and both involve using at least an intermediate representation with a larger range. One could use a 128 bit integral type, but I don't believe that is available on Windows, except perhaps by a 3rd party library. long double is another option. This might look like:

static time_point now() noexcept
{
using imd = std::chrono::duration<long double, period>;
return time_point
(
std::chrono::duration_cast<duration>(imd{std::chrono::system_clock::now().time_since_epoch()
+ std::chrono::hours(24*25567)})
);
};

That is, perform the offset shift with no conversion (system_clock::duration units), then convert that to the intermediate representation imd which has a long double rep, and the same period as NTPClock. This will use long double to compute 1.6 x 1016 times 53,687,091/2,000,000. Then finally duration_cast that to NTPClock::duration. This final duration_cast will end up doing nothing but casting long double to unsigned long long as the conversion factor is simply 1/1.

Another way to accomplish the same thing is:

static time_point now() noexcept
{
return time_point
(
std::chrono::duration_cast<duration>(std::chrono::system_clock::now().time_since_epoch()
+ std::chrono::hours(24*25567)*1.0L)
);
};

This takes advantage of the fact that you can multiply any duration by 1, but with alternate units and the result will have a rep with the common_type of the two arguments, but otherwise have the same value. I.e. std::chrono::hours(24*25567)*1.0L is a long double-based hours. And that long double carries through the rest of the computation until the duration_cast brings it back to NTPClock::duration.

This second way is simpler to write, but code reviewers may not understand the significance of the *1.0L, at least until it becomes a more common idiom.

Does the value of std::chrono::system_clock.now().time_since_epoch().count() increase monotonically?

system_clock tracks Unix Time. Unix Time has no UTC offset adjustments (daylight saving). It is just a linear count of non-leap seconds. It is possible that an implementation could jump backwards during a leap second insertion. Though in practice the leap second is "smeared" with many tiny adjustments over a span of hours.

In theory it is possible for a system_clock to be monotonic. In practice, no clock keeps perfect time, and must be adjusted, (potentially backwards) to stay in sync with Unix Time.

In C++11/14/17, the Unix Time measure is not specified for system_clock, but it is the existing practice. In C++20, it will be specified.



Related Topics



Leave a reply



Submit