Ответ 1
Как отмечали комментаторы, на самом деле это ошибка (бездействие) в libstdc++
. Представлен отчет об ошибке .
Сегодня я работал с некоторыми функциями времени и заметил, что стандартное преобразование с использованием %r
(или %p
), похоже, не работает для ввода через std::get_time()
в g++ или clang++. Смотрите эту версию живого кода для g++ и clang++. Кажется, он работает как ожидается в Windows с VС++ (см. Этот близкий вопрос). Также обратите внимание, что эффект тот же, независимо от того, включена ли строка imbue
. Локаль на моей машине Linux установлен на "en_US.UTF-8"
, если это имеет значение.
#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';
}
}
Как отмечали комментаторы, на самом деле это ошибка (бездействие) в libstdc++
. Представлен отчет об ошибке .
Плохая новость заключается в том, что get_time()
более сломан, чем OP и думали комментаторы. Он определенно не работает со спецификатором формата "%F"
(даты в формате ISO-8601, например, "2019-01-13"
). Я немного изменил код OP, чтобы продемонстрировать это. Скомпилировано с g++
версии 7.3.0 под 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';
}
Это вывод:
Conversion failed
1900-01-00
Незначительное замечание: настоятельно рекомендуется std::tm
объект std::tm
перед попыткой какой-либо обработки.
Обновление: вот обходной путь, который основан на функции strptime
UNIX (обратите внимание, что это не стандартная функция C или C++!). Я показываю пример кода с разбора даты ISO8601.
Во-первых, давайте обнаружим библиотеку GNU stdlibC++
и stdlibC++
заголовок <time.h>
. (На некоторых платформах заголовок может быть <sys/time.h>
):
#include <cstddef>
#if defined(__GLIBCXX__) || defined(__GLIBCPP__)
#define GET_TIME_WORKAROUND
#include <time.h>
#endif
Тогда разбери:
#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
// ........