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 betweensys_time
and calendar types (27.8).
[Example:
sys_seconds{sys_days{1970y/January/1}}.time_since_epoch()
is0s
.
sys_seconds{sys_days{2000y/January/1}}.time_since_epoch()
is
946’684’800s
, which is10’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_point
s 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_point
s 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 typesys_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 betweensys_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()
is946'684'800s
, which is10'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_point
s 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 onsystem_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 oftime_t
, though using Unix Time is common practice, and specified by POSIX.Though unspecified,
time_t
typically has a precision ofseconds
.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 samestd::ratio
assystem_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 twotime_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 twotime_point
's does not compile. But you can add atime_point
and anyduration
. The logical algebra oftime_point
s andduration
s 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). Withsystem_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 oftime_point
s, with its own convenience template type alias calledutc_time<Duration>
. And you can convert between them usingstd::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
Using Declaration in Variadic Template
Printing Double Without Losing Precision
Does the Restrict Keyword Provide Significant Benefits in Gcc/G++
Is There a Standard Way of Moving a Range into a Vector
_Attribute_((Weak)) and Static Libraries
Why Doesn't This Reinterpret_Cast Compile
Image Edge Smoothing with Opencv
Cmath VS Math.H (And Similar C-Prefixed VS .H Extension Headers)
Are There Binary Memory Streams in C++
How to Create an Array of Pointers
Calling a Constructor to Re-Initialize Object
How to Use Multiple Versions of Gcc
Clang C++ Cross Compiler - Generating Windows Executable from MAC Os X
Add External Libraries to Cmakelist.Txt C++
How Are User-Level Threads Scheduled/Created, and How Are Kernel Level Threads Created