How to convert std::chrono::time_point
to string?
For example: "201601161125"
.
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
using namespace whatever
, so that it's clearer which functions and types come from which namespace. –
Premier floor
was in namespace date
, but starting with C++17 is in namespace std::chrono
. By C++20, everything will be in std::chrono
. –
Fivestar .
, if necessary) in sub-second part of string produced by date::format("%FT%TZ", p)
where p
is a system_clock::time_point
? –
Snowbound 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). –
Fivestar 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.
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). –
Worley struct tm
only has seconds, it does not include any smaller time unit measure. –
Worley 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_stream
std::chrono::format
std::string_view
, otherwise, const char*
is also a possibility. –
Absently 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!
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.
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 ;
}
© 2022 - 2024 — McMap. All rights reserved.
std::stringstream
and then converting it to string using stringstream's member function.str()
? – Chaoan