How to convert std::chrono::time_point to string?
For example: "201601161125".
6 Answers
Update for C++20:
This can now easily be done in C++20:
#include <chrono>
#include <format>
#include <iostream>
#include <string>
int
main()
{
using namespace std::chrono_literals;
std::chrono::time_point tp = std::chrono::sys_days{2016y/1/16} + 11h + 25min;
std::string s = std::format("{:%Y%m%d%H%M}", tp);
std::cout << s << '\n';
}
Output:
201601161125
Original Answer:
Howard Hinnant's free, open source, header-only, portable date/time library is a modern way to do this that doesn't traffic through the old C API, and doesn't require that you discard all of your sub-second information. This library is also being proposed for standardization.
There is a lot of flexibility in formatting. The easiest way is to just stream out:
#include "date.h"
#include <iostream>
int
main()
{
using namespace date;
std::cout << std::chrono::system_clock::now() << '\n';
}
This just output for me:
2017-09-15 13:11:34.356648
The using namespace date; is required in order to find the streaming operator for the system_clock::time_point (it isn't legal for my lib to insert it into namespace std::chrono). No information is lost in this format: the full precision of your system_clock::time_point will be output (microseconds where I ran this on macOS).
The full suite of strftime-like formatting flags is available for other formats, with minor extensions to handle things like fractional seconds. Here is another example that outputs with millisecond precision:
#include "date.h"
#include <iostream>
int
main()
{
using namespace date;
using namespace std::chrono;
std::cout << format("%D %T %Z\n", floor<milliseconds>(system_clock::now()));
}
which just output for me:
09/15/17 13:17:40.466 UTC
5 Comments
using namespace whatever, so that it's clearer which functions and types come from which namespace.floor was in namespace date, but starting with C++17 is in namespace std::chrono. By C++20, everything will be in std::chrono.., if necessary) in sub-second part of string produced by date::format("%FT%TZ", p) where p is a system_clock::time_point?format, such as the truncation to milliseconds in the example above. There is no automatic way to detect trailing zeroes at run-time. the only way to do so is to do custom post-processing on the generated string (reverse searching for zeroes).std::format("{:%Y%m%d%H%M%S}", tp) (in my use case the seconds were actually the most important part).The most flexible way to do so is to convert it to struct tm and then use strftime (it's like sprintf for time). Something like:
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
std::time_t now_c = std::chrono::system_clock::to_time_t(now);
std::tm now_tm = *std::localtime(&now_c);
/// now you can format the string as you like with `strftime`
Look up the documentation for strftime here.
If you have localtime_s or localtime_r available you should use either in preference to localtime.
There are many other ways to do this, but, while mostly easier to use, these result in some predefined string representation. You could just "hide" all of the above in a function for ease of use.
5 Comments
strftime() has been a part of C standard for a long time. It seems that in C++20 Mr. Hinnant's library will be a part of the C++ standard - when that happens, it will be hard to make a firm preference (it would depend on your needs and experience).struct tm only has seconds, it does not include any smaller time unit measure.Code solution
The following function converts from chrono time_point to string (serialization).
#include <chrono>
#include <iomanip>
#include <sstream>
using time_point = std::chrono::system_clock::time_point;
std::string serializeTimePoint( const time_point& time, const std::string& format)
{
std::time_t tt = std::chrono::system_clock::to_time_t(time);
std::tm tm = *std::gmtime(&tt); //GMT (UTC)
//std::tm tm = *std::localtime(&tt); //Locale time-zone, usually UTC by default.
std::stringstream ss;
ss << std::put_time( &tm, format.c_str() );
return ss.str();
}
// example
int main()
{
time_point input = std::chrono::system_clock::now();
std::cout << serializeTimePoint(input, "UTC: %Y-%m-%d %H:%M:%S") << std::endl;
}
Time zone
The time_point data-type has no internal representation for the time-zone, in consequence, the time-zone is aggregated by the conversion to std::tm (by the functions gmtime or localtime). It is not recommended to add/substract the time-zone from the input, because you would get an incorrect time-zone displayed with %Z, thus, it is better to set the correct local time (OS dependent) and use localtime().
Technical vs User-friendly serialization
For technical usage, hard-coded time format is a good solution. However, to display to users, one should use a locale to retrieve the user preference and show time-stamp in that format.
C++20
Since C++20, we have nice serialization and parsing functions for time_point and duration.
std::chrono::to_streamstd::chrono::format
3 Comments
std::string_view, otherwise, const char* is also a possibility.A lot like Adrian's answer, but with (optional) milliseconds and GMT/localtime switch.
#include <chrono>
#include <iostream>
#include <iomanip>
#include <sstream>
using Clock = std::chrono::high_resolution_clock;
static std::string timePointToString(const Clock::time_point &tp, const std::string &format, bool withMs = true, bool utc = true)
{
const Clock::time_point::duration tt = tp.time_since_epoch();
const time_t durS = std::chrono::duration_cast<std::chrono::seconds>(tt).count();
std::ostringstream ss;
if (const std::tm *tm = (utc ? std::gmtime(&durS) : std::localtime(&durS))) {
ss << std::put_time(tm, format.c_str());
if (withMs) {
const long long durMs = std::chrono::duration_cast<std::chrono::milliseconds>(tt).count();
ss << std::setw(3) << std::setfill('0') << int(durMs - durS * 1000);
}
}
// gmtime/localtime() returned null ?
else {
ss << "<FORMAT ERROR>";
}
return ss.str();
}
int main() {
const auto tp = Clock::now();
std::cout << timePointToString(tp, "%Z %Y-%m-%d %H:%M:%S.") << std::endl;
return 0;
}
> GMT 2022-04-30 13:44:09.720
Test: https://www.mycompiler.io/view/43wsMbrMcmx
Granted it only appends the milliseconds and they're not part of the template... that would cost extra!
Comments
It will become easier with C++20 (as mentioned in Adrian Maire's answer.)
#include <chrono>
#include <format>
#include <string>
#include <iostream>
int main()
{
auto t = std::chrono::system_clock::now();
std::string s = std::format("{0:%F %R %Z}", t);
std::cout << s << '\n';
return 0;
}
Note, As of now (February 2023) std::format is not implemented yet in most compiler releases, but hopefully will be soon in new releases. (You can check with preprocessor macros __has_include(<format>) and __cpp_lib_format.)
Docs on std::format: https://en.cppreference.com/w/cpp/utility/format/format
The format syntax ("%F %T %Z") is similar to strftime() or std::put_time() (haven't checked what the differences are, though) and documented at https://en.cppreference.com/w/cpp/chrono/system_clock/formatter.
Comments
std::chrono::system_clock::time_point to string and back including hundreds of nanoseconds (the natural resolution of system_clock::time_point):
I couldn't find answer for c++17, so I wrote it myself. Sorry, if I re-inventing the wheel, but it's really ugly 100 nanoseconds here. Took too much of my time, hope my answer will be helpful.
The format "%Y-%m-%d %H:%M:%S.7sybmols_of_hundreds_ns"
And, yeah, you gonna look how to split the string
std::string Utils::From(const std::chrono::system_clock::time_point& datetime) {
auto t = std::chrono::system_clock::to_time_t(datetime);
auto datetime_se = datetime.time_since_epoch();
auto datetime_se_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(datetime_se);
auto hundreds_ns = (datetime_se_ns % 1000000000)/100; // 1000000000 - ns, std::chrono::system_clock 100 ns
std::tm tm = {};
gmtime_s(&tm, &t);
std::stringstream ss;
ss << std::put_time(&tm, "%Y-%m-%d %H:%M:%S");
ss << '.' << std::setfill('0') << std::setw(7) << hundreds_ns.count();
return ss.str();
}
std::chrono::system_clock::time_point Utils::ToTimePoint(const std::string& datetime) {
auto tokens = Utils::SplitString(datetime, ".");
auto time_t = tokens[0];
auto hundreds_ns_str = tokens[1];
std::tm tm = {};
std::istringstream ss(time_t);
ss >> std::get_time(&tm, "%Y-%m-%d %H:%M:%S");
auto t = _mkgmtime(&tm);
std::chrono::system_clock::time_point tp = std::chrono::system_clock::from_time_t(t);
std::istringstream ss_h_ns(hundreds_ns_str);
int hundreds_ns;
ss_h_ns >> hundreds_ns;
tp += std::chrono::duration_cast<std::chrono::system_clock::duration>(
std::chrono::nanoseconds(hundreds_ns*100));
return tp ;
}
std::stringstreamand then converting it to string using stringstream's member function.str()?