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\$