I was working with some time functions today and noticed that the standard conversion using %r
(or %p
) does not seem to work for input via std::get_time()
on g++ or clang++. See this live code version for g++ and clang++. It does seem to work as expected under Windows with VC++ (see this closely related question). Also note that the effect is the same whether or not the imbue
line is included. The locale on my Linux machine is set to "en_US.UTF-8"
if it matters.
#include <iostream>
#include <iomanip>
#include <sstream>
#include <string>
#include <ctime>
int main(){
std::tm t{};
std::stringstream ss{"1:23:45 PM"};
ss.imbue(std::locale(std::cin.getloc(),
new std::time_get_byname<char>("en_US")));
ss >> std::get_time(&t, "%r");
if (ss.fail()) {
std::cout << "Conversion failed\n" << std::put_time(&t, "%r") << '\n';
} else {
std::cout << std::put_time(&t, "%r") << '\n';
}
}
As the commenters have pointed out, this is actually a bug (an omission) in libstdc++
. A bug report has been submitted.
The bad news is that get_time()
is more broken than the OP and the commenters thought. It definitely does not work with the "%F"
format specifier (dates in ISO-8601 format, e.g. "2019-01-13"
) either. I modified the OP's code slightly to demonstrate this. Compiled with g++
version 7.3.0, under Ubuntu 18.04.1:
#include <iostream>
#include <iomanip>
#include <sstream>
#include <string>
#include <ctime>
int main(){
std::tm tdate{0,0,0,0,0,0,0,0,-1}; // was t{};
std::istringstream ins{"2019-01-13"};
ss.imbue(std::locale(std::cin.getloc(),
new std::time_get_byname<char>("en_US")));
ss >> std::get_time(&tdate, "%F");
if (ss.fail()) {
std::cout << "Conversion failed\n" << std::put_time(&tdate, "%F") << '\n';
} else {
std::cout << std::put_time(&tdate, "%F") << '\n';
}
This is the output:
Conversion failed
1900-01-00
A minor remark: it is highly advisable to zero out the std::tm
object before any processing is attempted.
Update: here is a workaround which relies on the UNIX function strptime
(note that this is not a C or C++ standard function!). I show the code example with the ISO8601 date parsing.
First, let's detect the GNU stdlibc++
library and pull in the <time.h>
header. (On some platforms the header may be <sys/time.h>
):
#include <cstddef>
#if defined(__GLIBCXX__) || defined(__GLIBCPP__)
#define GET_TIME_WORKAROUND
#include <time.h>
#endif
Then parse:
#ifdef GET_TIME_WORKAROUND
char *result = strptime(datestr.c_str(), "%F", &_cal);
if (result == nullptr) {
throw std::invalid_argument{ "Date(" + datestr + "): ISO-8601 date expected" };
}
#else
// proper C++11
// ........
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With