Hello,
chrono::system_clock provides a to_time_t[1] method to
convert the time_point
into the std::time_t for the C interop and it is the only clock
to provide
this functionality [2].
However, there's an issue with the implementation proposed in
the standard:
the signature of the method is
static time_t to_time_t (const time_point& t) noexcept; where the time_point has a concrete type chrono::time_point<chrono::system_clock, chrono::duration<unspecified, unspecified>
On some (most) implementation, this duration has int64_t
representation with nanoseconds as
a tick period. While this allows a very narrow range around the
unix epoch - +/-292 years
it is normally not a problem since one can use different
duration, and C++20 also includes
out of the box std::chrono::sys_seconds and
std::chrono::sys_days.
Where the current implementation breaks is the fact that when
one pass an instance of
std::chrono::sys_seconds to the
std::chrono::system_clock::to_time_t that object gets implicitly
converted to the object of the
std::chrono::system_clock::time_point, which although shares
the same clock and it has a higher precision, it has a much
smaller range. This in turn
leads to the overflow at this intermediate step and the result
of the function is wrong.
Here's the simple example:
date::sys_days date = date::year(1500)/1/1;
time_t unix_time = std::chrono::system_clock::to_time_t(date);
std::cout << "to_time_t: " <<
std::ctime(&unix_time); // Fri Jul 21 01:34:33 2084
While we can't guarantee much for this conversion because
std::time_t is in the end
unspecified, we can use the best effort to minimize the error
case. I propose to replace the
existing signature of the to_time_t with something akin to:
template <typename
TimePoint>
static std::time_t
to_time_t(const TimePoint& __t,
typename std::enable_if<std::is_same<typename
TP::clock, system_clock>::value>::type* = 0) noexcept
Since this doesn't specify the
time_point's concrete type we will avoid the
implicit conversion and we'll simply cast
__t.time_since_epoch() to seconds
in the possible implementation.
I believe this method is
backward compatible and more permissive than the original one,
while still making the same guarantee - it shoudln't allow
passing the time_points measured
on the clock different than the system one (since this clock
is the only that map time points
into the C-style time.
While using the duration_cast
to seconds and count() get the value and making sure that
the system clock is used, I see no reason why we don't improve
the current function since
it's been advertied as the entry point for C-style time
conversion.
Best,
Nemanja
[1] https://en.cppreference.com/w/cpp/chrono/system_clock/to_time_t
[2] https://en.cppreference.com/w/cpp/chrono/system_clock
[3] http://eel.is/c++draft/time.clock.system