I have some arbitrary epoch, like July 13, 1988. Essentially I want to measure the time relative to this. I was thinking of writing a custom clock class, so that I could write code like this:
using std::chrono;
time_point<My_Clock> tp;
std::cout << duration_cast<seconds>(tp.time_since_epoch()).count() << std::endl;
Is this possible? If not, what's the cleanest way to accomplish this?
The hard part of writing this custom clock is figuring out how to write its now()
function. In the example below I base the now()
off of system_clock
's now()
. First I do some detective work to discover that my system_clock
has an epoch of New Years 1970, neglecting leap seconds. This is known as unix time. As it turns out, every implementation I'm aware of (and I think I've checked them all) have this very same epoch (but this is unspecified by the C++11 standard).
Next I compute that 1988-07-13 is 6768 days after 1970-01-01. Using these two facts, the rest is easy:
#include <chrono>
struct My_Clock
{
typedef std::chrono::seconds duration;
typedef duration::rep rep;
typedef duration::period period;
typedef std::chrono::time_point<My_Clock> time_point;
static const bool is_steady = false;
static time_point now() noexcept
{
using namespace std::chrono;
return time_point
(
duration_cast<duration>(system_clock::now().time_since_epoch()) -
hours(6768*24)
);
}
};
MyClock
needs nested typedefs to describe its duration
, rep
, period
, and time_point
. Based on your question, I've chosen seconds
as the duration
, but you can choose anything you want.
For the now()
function I just call the system_clock::now()
and subtract off the epoch in units of seconds. I got just a little clever with this computation by writing everything in terms of MyClock::duration
so that I can more easily change duration
. Note that I was able to subtract off the epoch in terms of hours
, which implicitly converts to duration
(which is seconds
). Alternatively I could have built myself a custom duration
of days:
typedef std::chrono::duration<int, std::ratio_multiply<std::chrono::hours::period,
std::ratio<24>>> days;
And then the return of now()
could have been written:
return time_point
(
duration_cast<duration>(system_clock::now().time_since_epoch()) -
days(6768)
);
At any rate, now you can use this like:
#include <iostream>
int
main()
{
using namespace std::chrono;
time_point<My_Clock> tp = My_Clock::now();
std::cout << tp.time_since_epoch().count() << '\n';
}
Which for me just printed out:
786664963
Which demonstrates that today (2013-06-16) is approximately 24.9 years after 1988-07-13.
In C++20 you can use the updated <chrono>
calendrical services to eliminate the "magic number" from the implementation:
#include <chrono>
struct My_Clock
{
typedef std::chrono::seconds duration;
typedef duration::rep rep;
typedef duration::period period;
typedef std::chrono::time_point<My_Clock> time_point;
static const bool is_steady = false;
static time_point now() noexcept
{
using namespace std::chrono;
return time_point
{
duration_cast<duration>(system_clock::now() - sys_days{July/13/1988})
};
}
};
There is no run-time expense for this convenience. The sub-expression sys_days{July/13/1988}
is optimized at compile-time into a count of days since the system_clock
epoch, and then further converted to system_clock::duration
(still at compile-time) before being subtracted from system_clock::now()
.
Also new in C++20 there is a std::chrono::clock_cast
facility that allows you to cast time_point
s of one clock to another as long as there is a defined relationship between the two clock's epochs.
For example:
auto tp = clock_cast<gps_clock>(system_clock::now());
tp
is a time_point
based on std::chrono::gps_clock
which has an epoch of the first Sunday of January, 1980.
clock_cast
can be used to convert among all of these chrono-supplied clocks:
system_clock
utc_clock
tai_clock
gps_clock
file_clock
If desired, My_Clock
can also be hooked into the clock_cast
utility. By defining conversions to and from system_clock
's time_point
s, clock_cast
can then be used to convert among all of the chrono-supplied clocks above plus any other user-defined clocks that participate in the clock_cast
infrastructure.
This is done like this:
struct My_Clock;
template <class Duration>
using My_time = std::chrono::time_point<My_Clock, Duration>;
struct My_Clock
{
typedef std::chrono::seconds duration;
typedef duration::rep rep;
typedef duration::period period;
typedef std::chrono::time_point<My_Clock> time_point;
static const bool is_steady = false;
static
time_point
now() noexcept;
template <class Duration>
static
auto
from_sys(std::chrono::sys_time<Duration> const& tp) noexcept;
template <class Duration>
static
auto
to_sys(My_time<Duration> const& tp) noexcept;
};
template <class Duration>
auto
My_Clock::from_sys(std::chrono::sys_time<Duration> const& tp) noexcept
{
using namespace std::chrono;
return My_time{tp - sys_days{July/13/1988}};
}
template <class Duration>
auto
My_Clock::to_sys(My_time<Duration> const& tp) noexcept
{
using namespace std::chrono;
return sys_time{tp - clock_cast<My_Clock>(sys_days{})};
}
My_Clock::time_point
My_Clock::now() noexcept
{
using namespace std::chrono;
return floor<duration>(clock_cast<My_Clock>(system_clock::now()));
}
to_sys
and from_sys
define conversions to and from system_clock
-based time_point
s. The clock_cast
facility finds these functions and uses them to implement conversions among all clocks that convert to/from either system_clock
or utc_clock
.
And by clever use of the clock_cast
facility within the implementation of these functions, the epoch of My_Clock
needs to be mentioned only once.