Является ли std:: get_time сломанным в g++ и clang++?

Сегодня я работал с некоторыми функциями времени и заметил, что стандартное преобразование с использованием %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';
    }
}

Ответ 1

Как отмечали комментаторы, на самом деле это ошибка (бездействие) в libstdc++. Представлен отчет об ошибке .

Ответ 2

Плохая новость заключается в том, что 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
// ........