Reputation: 18471
I´ve wrote the following function that receives a time point and return a ISO string with milliseconds:
std::string TimePointToISOString(const std::chrono::time_point<std::chrono::system_clock>& time)
{
std::time_t rawTime = std::chrono::system_clock::to_time_t(time);
struct tm timeinfo;
localtime_s(&timeinfo, &rawTime);
std::chrono::milliseconds ms = std::chrono::duration_cast<std::chrono::milliseconds>(time.time_since_epoch());
std::size_t frac_seconds = ms.count() % 1000;
char buffer[32];
std::strftime(buffer, 32, "%FT%TZ", &timeinfo);
std::string bufferStr(buffer);
std::stringstream ss;
ss << bufferStr.substr(0, 19) << "." << std::setw(3) << std::setfill ('0') << frac_seconds << bufferStr.substr(20);
return ss.str();
}
I´ve used to run that on Linux with the localtime()
function with no problems at all. Now I am migrating to Windows/VS2012 and the compiler suggests to change localtime()
for a safer localtime_s()
, done immediately.
After the conversion the strftime
call does crash at runtime with the following error:
Expression: ("Invalid format directive", 0)
Compilation runs fine. Help appreciated to find out what´s going on here. I guess something simple that I can´t notice...
Upvotes: 2
Views: 2721
Reputation: 490338
The %F
and %T
conversions were added in C99, and VS 2012 only supports C89.
Although it still doesn't try to support all of C99, I believe VS 2015 adds enough more (especially the C99 library functions, which are also part of C++11) that this should work fine with it.
If you need to continue using the older compiler/library, %F and %T are both basically "shortcuts" for some formats that were already supported in C89. If I'm reading the requirements correctly, the following should be equivalent:
std::strftime(buffer, 32, "%Y-%m-%dT%H:%M:%SZ", &timeinfo);
As an aside, if your compiler supports C++11, it's generally easier and cleaner to use std::put_time
instead of strftime
.
Upvotes: 5
Reputation: 219185
This is a good question (upvoted). And Jerry Coffin's answer is a good answer (also upvoted). This answer is about adding further information about an alternative open-source solution which is known to work on VS-2013 and later, gcc, and clang. This alternative will produce identical output to TimePointToISOString
. In order to differentiate it, I've given it another name: to_local_string
:
std::string
to_local_string(const std::chrono::system_clock::time_point& time)
{
using namespace date;
auto zone = current_zone();
auto local = floor<std::chrono::milliseconds>(zone->to_local(time).first);
auto dp = floor<days>(local);
year_month_day ymd = dp;
std::stringstream ss;
ss << ymd << 'T' << make_time(local-dp);
return ss.str();
}
This uses the time zone database parser found here:
https://github.com/HowardHinnant/date
and documented here:
http://howardhinnant.github.io/tz.html
This is a parser of the IANA Timezone database, and is used here to simply find the UTC offset for the local timezone. Once the local time_point is found (stored as a std::chrono::system_clock::time_point
), it is broken up into field types year/month/day/hour/minute/second/millisecond using this library whose implementation is found at the same github site.
I just ran both TimePointToISOString
and to_local_string
on the same time_point and the output was:
2016-03-23T22:54:52.626
2016-03-23T22:54:52.626
Upvotes: 2