Reputation: 20130
In Visual Studio 2013 I just used
#include <chrono>
#include <ctime>
#include <iostream>
#include <iomanip>
#include <sstream>
std::string Time_Point_String(const std::chrono::high_resolution_clock::time_point & timePoint)
{
time_t timeNow = std::chrono::system_clock::to_time_t(timePoint);
tm time = *localtime(&timeNow);
std::stringstream timeString;
timeString << std::setfill('0') << 1900 + time.tm_year << "-" << std::setw(2) << time.tm_mon + 1 << "-" << std::setw(2) << time.tm_mday << " " << std::setw(2) << time.tm_hour << ":" << std::setw(2) << time.tm_min << ":" << std::setw(2) << time.tm_sec;
return timeString.str();
}
int main()
{
const std::chrono::high_resolution_clock::time_point & timePoint = std::chrono::high_resolution_clock::now();
std::cout << Time_Point_String(timePoint);
return 0;
}
But with Visual Studio 2017 I get a compiler error:
Error C2664 '__time64_t std::chrono::system_clock::to_time_t(const std::chrono::system_clock::time_point &) noexcept': cannot convert argument 1 from 'const std::chrono::steady_clock::time_point' to 'const std::chrono::system_clock::time_point &'
So it isn't possible anymore to convert a high_resolution_clock::time_point
to a different time_point
like system_clock::time_point
and there is no possibility to convert high_resolution_clock::time_point
to time_t
directly?
How can I handle this situation? Is it possible at all (some SO postings say they are just completely different clocks and conversion doesn't make sense)? As far as I've seen, the function did what I expected it to do in Visual Studio 2013 application, it has provided the right local time for a high_resolution time_point.
Upvotes: 1
Views: 4764
Reputation: 21
It's all just data. The c++ type system is powerful, but not absolute. The reason you can convert between std::chrono::system_clock and a time_t is that they share the same epoch, or "time when the clock began". For time_t this is in seconds since the epoch "Wed Dec 31 19:00:00 1969" or since since 00:00, Jan 1 1970 UTC, although it is not defined. So converting a system clock is as simple as casting the clocks native resolution to the number of seconds since the epoch. A time_point encapsulates it's time since the epoch. For a high_resolution_clock, or any other clock other than system_clock, the epoch is not guaranteed to be the same. It may be when the computer started up, or when the cmos battery was plugged in, when the program began... Who knows? Who cares? The difference in epochs is why they are not compatible. Not necessarily their type. Although..., an alias would ensure the share the same epoch. So the difference between epochs for any given time point, applied as an offset in seconds, will give us our conversion. Without any more warm up here is the code :
#include <iostream>
#include <chrono>
#include <ctime>
namespace joe {
template<typename Test, template<typename...> class Ref>
struct is_specialization : public std::false_type {};
template<template<typename...> class Ref, typename... Args>
struct is_specialization<Ref<Args...>, Ref> : public std::true_type {};
template <class Duration >
using is_duration = is_specialization<Duration, std::chrono::duration>;
template<class, class = void>
struct is_clock : public std::false_type {};
template<class Clock>
struct is_clock<Clock, std::void_t<typename Clock::rep, typename Clock::period, typename Clock::duration,
typename Clock::time_point, decltype(Clock::is_steady), decltype(Clock::now())>> : public std::true_type {};
template<typename Clock, class Duration = typename Clock::duration>
[[nodiscard]] static inline time_t to_time_t(const std::chrono::time_point<Clock, Duration>& time) noexcept {
static_assert(joe::is_clock<Clock>::value, "The given type for Clock does not fulfill the requirements of a clock.");
static_assert(joe::is_duration<Duration>::value, "The given type for Duration is not of std::chrono::duration.");
static const long long offset = (std::is_same< Clock, std::chrono::system_clock>::value) ? 0
: (std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count()
- std::chrono::duration_cast<std::chrono::seconds>(Clock::now().time_since_epoch()).count());
return std::chrono::duration_cast<std::chrono::seconds>(time.time_since_epoch()).count() + offset;
}
template<typename Clock, class Duration = typename Clock::duration>
[[nodiscard]] static inline std::chrono::time_point<Clock, Duration> from_time_t( time_t time) noexcept {
static_assert(joe::is_clock<Clock>::value, "The given type for Clock does not fulfill the requirements of a clock.");
static_assert(joe::is_duration<Duration>::value, "The given type for Duration is not of std::chrono::duration.");
static const long long offset = (std::is_same< Clock, std::chrono::system_clock>::value) ? 0
: (std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count()
- std::chrono::duration_cast<std::chrono::seconds>(Clock::now().time_since_epoch()).count());
return std::chrono::time_point<Clock, Duration>{ std::chrono::duration_cast<Duration>(std::chrono::seconds{ time - offset }) };
}
[[nodiscard]] static inline std::string ctime( time_t result) noexcept
{
char str[26];
ctime_s(str, sizeof str, &result);
return std::string(str);
}
}
void check_system_epoch()
{
const std::chrono::system_clock::time_point timePoint;
time_t time = joe::to_time_t(timePoint);
std::cout << "system clock epoch : " << joe::ctime(time) << std::endl;
}
void check_high_res_epoch()
{
const std::chrono::high_resolution_clock::time_point timePoint;
time_t time = joe::to_time_t(timePoint);
std::cout << "high resolution clock epoch : " << joe::ctime(time) << std::endl;
}
int main()
{
const std::chrono::high_resolution_clock::time_point & timePoint = std::chrono::high_resolution_clock::now();
time_t time = joe::to_time_t(timePoint);
std::cout << joe::ctime( time ) << std::endl;
std::cout << "Press the enter key to continue!\n";
check_system_epoch();
std::cin.ignore();
std::cout << "Press the enter key to continue!\n";
check_high_res_epoch();
std::cin.ignore();
}
` This gives you to_time_t, from_time_t, and a ctime, that will work with any clock.
Upvotes: 2
Reputation: 36379
You can only convert between time_point
s from the same clock. std::chrono::high_resolution_clock
is an alias for another clock. If that happens to be std::chrono::system_clock
then you can convert to time_t.
If std::chrono::high_resolution_clock
is another clock you can approximately convert it to std::chrono::system_clock
by taking the difference between the current time and the input time in std::chrono::high_resolution_clock
and adding that difference to the system time:
#include <iostream>
#include <chrono>
int main()
{
auto input = std::chrono::high_resolution_clock::now();
auto highResNow = std::chrono::high_resolution_clock::now();
auto systemNow = std::chrono::system_clock::now();
auto output = systemNow + (highResNow - input);
std::cout << std::chrono::system_clock::to_time_t( output ) << "\n";
}
Note that the conversion is only approximate, ideally highResNow
and systemNow
need to be calculated at the same time but there will be a small gap between them. The conversion will also be more unreliable the further apart the input time and the current time are.
Upvotes: 2
Reputation: 40060
This comes from the fact that std::chrono::high_resolution_clock
is a type alias for std::chrono::system_clock
or std::chrono::steady_clock
:
Class
std::chrono::high_resolution_clock
represents the clock with the smallest tick period provided by the implementation. It may be an alias ofstd::chrono::system_clock
orstd::chrono::steady_clock
, or a third, independent clock.
This means std::chrono::high_resolution_clock::time_point
can be a type alias for std::chrono::system_clock::time_point
, or it can be an other type. From your question, one can guess it does for MSVC2013, making your code valid, but is not for MSVC2017, making your code invalid.
As a conclusion, the following code might or might not be valid, in an unspecified manner (it depends on the compiler and its target architecture):
std::chrono::high_resolution_clock::time_point timePoint = /* something */;
std::chrono::system_clock::to_time_t(timePoint);
Upvotes: 4