std::get_time и другие функции локали некорректно работают в Windows

Пытаясь заставить libc++ и его тесты работать в Windows, я столкнулся с проблемой, которую никак не могу понять. Следующий код взят из тестового кода libc++ и проходит на Mac (и, возможно, FreeBSD), но не с MinGW-w64 и MSVC 2010 SP1.

#include <iomanip>
#include <iostream>
#include <cassert>

template <class CharT>
struct testbuf
    : public std::basic_streambuf<CharT>
{
    typedef std::basic_string<CharT> string_type;
    typedef std::basic_streambuf<CharT> base;
private:
    string_type str_;
public:

    testbuf() {}
    testbuf(const string_type& str)
        : str_(str)
    {
        base::setg(const_cast<CharT*>(str_.data()),
                   const_cast<CharT*>(str_.data()),
                   const_cast<CharT*>(str_.data()) + str_.size());
    }
};
#if _WIN32
#define LOCALE_en_US_UTF_8 "English_USA.1252"
#else
#define LOCALE_en_US_UTF_8 "en_US.UTF-8"
#endif
int main()
{
    testbuf<char> sb("  Sat Dec 31 23:55:59 2061");
    std::istream is(&sb);
    is.imbue(std::locale(LOCALE_en_US_UTF_8));
    std::tm t = {0};
    is >> std::get_time(&t, "%c");
    std::cout << t.tm_sec << "\n";
    std::cout << t.tm_min << "\n";
    std::cout << t.tm_hour << "\n";
    std::cout << t.tm_mday << "\n";
    std::cout << t.tm_mon << "\n";
    std::cout << t.tm_year << "\n";
    std::cout << t.tm_wday << "\n";
    assert(t.tm_sec == 59);
    assert(t.tm_min == 55);
    assert(t.tm_hour == 23);
    assert(t.tm_mday == 31);
    assert(t.tm_mon == 11);
    assert(t.tm_year == 161);
    assert(t.tm_wday == 6);
}

Тест пройден для Mac/FreeBSD, но все разные элементы равны 0 для Windows. Это верно для MinGW-w64+libc++ и MSVC10+Microsoft STL.

Это просто дерьмовая поддержка локали в Windows или здесь действует неправильное предположение, зависящее от реализации (формат ввода), которое я могу исправить или обойти?


person rubenvb    schedule 02.10.2011    source источник
comment
Клэр: все до единого: все элементы просто 0 вместо значений, ожидаемых в утверждениях.   -  person rubenvb    schedule 02.10.2011
comment
Хм, да, я понимаю, что вы имеете в виду. В Visual C++ 2010 Express я тоже получаю все нули — с консольным приложением C++, созданным по умолчанию.   -  person Clare Macrae    schedule 02.10.2011
comment
Являются ли они тем типом и форматом локалей, которые принимает Windows? Windows живет в мире UTF-16, поэтому я сомневаюсь, что они поддерживают множество локалей UTF-8.   -  person Nicol Bolas    schedule 02.10.2011
comment
Никол Болас: Обратите внимание, что я сжульничал и использовал кодовую страницу Windows в определении UTF_8. Особенно для простого английского (en_US) этот простой пример должен работать. istream::imbue не выходит из строя. Я убедился в этом.   -  person rubenvb    schedule 02.10.2011


Ответы (2)


Я не уверен, что у вас правильная строка локали. Похоже, они очень плохо задокументированы, хотя есть хороший список здесь.

Возможно, "American_us.1252"? (Конечно, все эти строки определяются реализацией.)

person Alan Stokes    schedule 02.10.2011
comment
См. здесь и здесь, но это, кажется, предлагает другое имя... Я запутался :( - person rubenvb; 02.10.2011

Проблема, представленная здесь, заключается не в неправильных именах локалей, а в неправильных значениях std::tm.

Но именно в том, что спецификатор формата %c не делает того, что ожидается здесь в Windows. Это легко исправить, указав формат полностью. В этом случае я использовал:

"%a %b %d %H" ":" "%M" ":" "%S %Y"

У него все еще есть проблемы с частью %Y (tm_year все еще равно нулю)...

person rubenvb    schedule 03.10.2011