Consider these functions that allow to convert std::chrono::time_point
to/from std::string
with a predefined date-time format.
constexpr size_t log10(size_t xx) { return xx == 1 ? 0 : 1 + log10(xx/10); }
template < typename Double, size_t Precision = std::numeric_limits<Double>::digits10, typename TimePoint >
requires (TimePoint::period::den % 10 != 0) && std::is_floating_point_v<Double> && Precision <= std::numeric_limits<Double>::digits10
inline bool toString(const TimePoint& timePoint, std::string& str)
{
Double seconds = timePoint.time_since_epoch().count();
(seconds *= TimePoint::period::num) /= TimePoint::period::den;
auto zeconds = std::modf(seconds,&seconds);
time_t tt = seconds;
std::ostringstream oss;
oss << std::put_time(std::localtime(&tt), "%Y-%m-%d %H:%M:")
<< std::setw(Precision+3) << std::setfill('0') << std::fixed << std::setprecision(Precision) << (tt%60)+zeconds;
return oss && (str = oss.str(), true);
}
template < typename TimePoint > requires (TimePoint::period::den % 10 == 0)
inline bool toString(const TimePoint& timePoint, std::string& str)
{
uint64_t feconds = timePoint.time_since_epoch().count() * TimePoint::period::num;
time_t tt = feconds / TimePoint::period::den;
std::ostringstream oss;
oss << std::put_time(std::localtime(&tt), "%Y-%m-%d %H:%M:%S.")
<< std::setw(log10(TimePoint::period::den)) << std::setfill('0') << feconds % TimePoint::period::den;
return oss && (str = oss.str(), true);
}
template < typename TimePoint >
bool fromString(TimePoint& timePoint, const std::string& str)
{
std::istringstream iss(str);
std::tm tm{};
if (!(iss >> std::get_time(&tm, "%Y-%m-%d %H:%M:%S")))
return false;
timePoint = {};
timePoint += std::chrono::seconds(std::mktime(&tm));
if (iss.eof())
return true;
if (iss.get() != '.')
return false;
std::string zz;
if (!(iss >> zz))
return false;
static_assert(std::chrono::high_resolution_clock::period::num == 1 && std::chrono::high_resolution_clock::period::den % 10 == 0);
zz.resize(log10(std::chrono::high_resolution_clock::period::den),'0');
size_t zeconds = 0;
try { zeconds = std::stoul(zz); } catch (const std::exception&) { return false; }
timePoint += std::chrono::high_resolution_clock::duration(zeconds);
return true;
}
with usage:
std::string str;
auto now = std::chrono::system_clock::now();
toString(now,str); std::cerr << "==== " << str << std::endl;
using DD = std::chrono::duration<size_t,std::ratio<2,3>>;
using TP = std::chrono::time_point<std::chrono::system_clock,DD>;
toString<double>(TP(DD(0)),str); std::cout << "==== " << str << std::endl;
toString<double>(TP(DD(1)),str); std::cout << "==== " << str << std::endl;
toString<double>(TP(DD(2)),str); std::cout << "==== " << str << std::endl;
toString<double>(TP(DD(3)),str); std::cout << "==== " << str << std::endl;
std::chrono::system_clock::time_point tp;
str = "2017-Mar-01"; fromString(tp,str); toString(tp,str); std::cerr << "---- " << str << std::endl;
str = "1969-Dec-31 19:00:00.666666666666667"; fromString(tp,str); toString(tp,str); std::cerr << "---- " << str << std::endl;
Any suggestions to make them faster, c-functions free or more compact?
As the objective is to do conversions with a predefined date-time format the std::locale
support of std::ostringstream
and std::istringstream
is useless and it is a significant slowdown. But how can I avoid this?
log10()
might not be the best choice of name, given thatstd::log10()
exists. \$\endgroup\$