[libc++][format] Fixes year formatter on Windows.

Windows' libc, like some other libc implementations do not work as
specified for %Y and %y. This uses the fixes used for other libc
implementations.

The work was part of D150593.

Reviewed By: #libc, ldionne

Differential Revision: https://reviews.llvm.org/D151612
This commit is contained in:
Mark de Wever 2023-05-27 16:52:37 +02:00
parent 66c7388c83
commit 0ee73debf7
3 changed files with 40 additions and 41 deletions

View File

@ -270,20 +270,19 @@ _LIBCPP_HIDE_FROM_ABI void __format_chrono_using_chrono_specs(
//
// TODO FMT evaluate the comment above.
# if defined(__GLIBC__) || defined(_AIX)
# if defined(__GLIBC__) || defined(_AIX) || defined(_WIN32)
case _CharT('y'):
// Glibc fails for negative values, AIX for positive values too.
__sstr << std::format(_LIBCPP_STATICALLY_WIDEN(_CharT, "{:02}"), (std::abs(__t.tm_year + 1900)) % 100);
break;
# endif // defined(__GLIBC__) || defined(_AIX)
# endif // defined(__GLIBC__) || defined(_AIX) || defined(_WIN32)
case _CharT('Y'): {
int __year = __t.tm_year + 1900;
if (__year < 1000)
__formatter::__format_year(__year, __sstr);
else
__facet.put({__sstr}, __sstr, _CharT(' '), std::addressof(__t), std::to_address(__s), std::to_address(__it + 1));
} break;
case _CharT('Y'):
// Depending on the platform's libc the range of supported years is
// limited. Intead of of testing all conditions use the internal
// implementation unconditionally.
__formatter::__format_year(__t.tm_year + 1900, __sstr);
break;
case _CharT('F'): {
int __year = __t.tm_year + 1900;

View File

@ -9,9 +9,6 @@
// UNSUPPORTED: c++03, c++11, c++14, c++17
// UNSUPPORTED: no-localization
// TODO FMT Investigate Windows issues.
// UNSUPPORTED: msvc, target={{.+}}-windows-gnu
// TODO FMT Fix this test using GCC, it currently crashes.
// UNSUPPORTED: gcc-12
@ -35,9 +32,16 @@
#include "make_string.h"
#include "platform_support.h" // locale name macros
#include "test_macros.h"
#include "assert_macros.h"
#include "concat_macros.h"
#define SV(S) MAKE_STRING_VIEW(CharT, S)
#define TEST_EQUAL(OUT, EXPECTED) \
TEST_REQUIRE(OUT == EXPECTED, \
TEST_WRITE_CONCATENATED( \
"\nExpression ", #OUT, "\nExpected output ", EXPECTED, "\nActual output ", OUT, '\n'));
template <class CharT>
static std::basic_string<CharT> stream_c_locale(std::chrono::year year) {
std::basic_stringstream<CharT> sstr;
@ -65,23 +69,23 @@ static std::basic_string<CharT> stream_ja_JP_locale(std::chrono::year year) {
template <class CharT>
static void test() {
assert(stream_c_locale<CharT>(std::chrono::year{-32'768}) == SV("-32768 is not a valid year"));
assert(stream_c_locale<CharT>(std::chrono::year{-32'767}) == SV("-32767"));
assert(stream_c_locale<CharT>(std::chrono::year{0}) == SV("0000"));
assert(stream_c_locale<CharT>(std::chrono::year{1970}) == SV("1970"));
assert(stream_c_locale<CharT>(std::chrono::year{32'767}) == SV("32767"));
TEST_EQUAL(stream_c_locale<CharT>(std::chrono::year{-32'768}), SV("-32768 is not a valid year"));
TEST_EQUAL(stream_c_locale<CharT>(std::chrono::year{-32'767}), SV("-32767"));
TEST_EQUAL(stream_c_locale<CharT>(std::chrono::year{0}), SV("0000"));
TEST_EQUAL(stream_c_locale<CharT>(std::chrono::year{1970}), SV("1970"));
TEST_EQUAL(stream_c_locale<CharT>(std::chrono::year{32'767}), SV("32767"));
assert(stream_fr_FR_locale<CharT>(std::chrono::year{-32'768}) == SV("-32768 is not a valid year"));
assert(stream_fr_FR_locale<CharT>(std::chrono::year{-32'767}) == SV("-32767"));
assert(stream_fr_FR_locale<CharT>(std::chrono::year{0}) == SV("0000"));
assert(stream_fr_FR_locale<CharT>(std::chrono::year{1970}) == SV("1970"));
assert(stream_fr_FR_locale<CharT>(std::chrono::year{32'767}) == SV("32767"));
TEST_EQUAL(stream_fr_FR_locale<CharT>(std::chrono::year{-32'768}), SV("-32768 is not a valid year"));
TEST_EQUAL(stream_fr_FR_locale<CharT>(std::chrono::year{-32'767}), SV("-32767"));
TEST_EQUAL(stream_fr_FR_locale<CharT>(std::chrono::year{0}), SV("0000"));
TEST_EQUAL(stream_fr_FR_locale<CharT>(std::chrono::year{1970}), SV("1970"));
TEST_EQUAL(stream_fr_FR_locale<CharT>(std::chrono::year{32'767}), SV("32767"));
assert(stream_ja_JP_locale<CharT>(std::chrono::year{-32'768}) == SV("-32768 is not a valid year"));
assert(stream_ja_JP_locale<CharT>(std::chrono::year{-32'767}) == SV("-32767"));
assert(stream_ja_JP_locale<CharT>(std::chrono::year{0}) == SV("0000"));
assert(stream_ja_JP_locale<CharT>(std::chrono::year{1970}) == SV("1970"));
assert(stream_ja_JP_locale<CharT>(std::chrono::year{32'767}) == SV("32767"));
TEST_EQUAL(stream_ja_JP_locale<CharT>(std::chrono::year{-32'768}), SV("-32768 is not a valid year"));
TEST_EQUAL(stream_ja_JP_locale<CharT>(std::chrono::year{-32'767}), SV("-32767"));
TEST_EQUAL(stream_ja_JP_locale<CharT>(std::chrono::year{0}), SV("0000"));
TEST_EQUAL(stream_ja_JP_locale<CharT>(std::chrono::year{1970}), SV("1970"));
TEST_EQUAL(stream_ja_JP_locale<CharT>(std::chrono::year{32'767}), SV("32767"));
}
int main(int, char**) {

View File

@ -10,9 +10,6 @@
// UNSUPPORTED: c++03, c++11, c++14, c++17
// UNSUPPORTED: no-localization
// TODO FMT Investigate Windows issues.
// UNSUPPORTED: msvc, target={{.+}}-windows-gnu
// TODO FMT Fix this test using GCC, it currently crashes.
// UNSUPPORTED: gcc-12
@ -88,7 +85,7 @@ static void test_valid_values() {
// Non localized output using C-locale
check(SV("%C='00'\t"
#if defined(__APPLE__)
#if defined(__APPLE__) || defined(_WIN32)
"%EC='00'\t"
#else
"%EC='0'\t"
@ -97,7 +94,7 @@ static void test_valid_values() {
"%Ey='00'\t"
"%Oy='00'\t"
"%Y='0000'\t"
#if defined(__APPLE__)
#if defined(__APPLE__) || defined(_WIN32)
"%EY='0000'\t"
#elif defined(_AIX)
"%EY=''\t"
@ -132,7 +129,7 @@ static void test_valid_values() {
// Use the global locale (fr_FR)
check(SV("%C='00'\t"
#if defined(__APPLE__)
#if defined(__APPLE__) || defined(_WIN32)
"%EC='00'\t"
#else
"%EC='0'\t"
@ -141,7 +138,7 @@ static void test_valid_values() {
"%Ey='00'\t"
"%Oy='00'\t"
"%Y='0000'\t"
#if defined(__APPLE__)
#if defined(__APPLE__) || defined(_WIN32)
"%EY='0000'\t"
#elif defined(_AIX)
"%EY=''\t"
@ -175,10 +172,10 @@ static void test_valid_values() {
std::chrono::year{2038});
// Use supplied locale (ja_JP). This locale has a different alternate.
#if defined(__APPLE__) || defined(_AIX)
#if defined(__APPLE__) || defined(_AIX) || defined(_WIN32)
check(SV("%C='00'\t"
# if defined(__APPLE__)
# if defined(__APPLE__) || defined(_WIN32)
"%EC='00'\t"
# else
"%EC='0'\t"
@ -218,12 +215,12 @@ static void test_valid_values() {
lfmt,
std::chrono::year{2038});
#else // defined(__APPLE__) || defined(_AIX)
#else // defined(__APPLE__) || defined(_AIX) || defined(_WIN32)
check(loc,
SV("%C='00'\t"
"%EC='紀元前'\t"
"%y='00'\t"
// https://sourceware.org/bugzilla/show_bug.cgi?id=23758
// https://sourceware.org/bugzilla/show_bug.cgi?id=23758
# if defined(__GLIBC__) && __GLIBC__ <= 2 && __GLIBC_MINOR__ < 29
"%Ey='1'\t"
# else
@ -231,7 +228,7 @@ static void test_valid_values() {
# endif
"%Oy=''\t"
"%Y='0000'\t"
// https://sourceware.org/bugzilla/show_bug.cgi?id=23758
// https://sourceware.org/bugzilla/show_bug.cgi?id=23758
# if defined(__GLIBC__) && __GLIBC__ <= 2 && __GLIBC_MINOR__ < 29
"%EY='紀元前1年'\t"
# else
@ -265,7 +262,7 @@ static void test_valid_values() {
"\n"),
lfmt,
std::chrono::year{2038});
#endif // defined(__APPLE__) || defined(_AIX)
#endif // defined(__APPLE__) || defined(_AIX) || defined(_WIN32)
std::locale::global(std::locale::classic());
}
@ -273,7 +270,6 @@ static void test_valid_values() {
template <class CharT>
static void test_padding() {
constexpr std::basic_string_view<CharT> fmt = SV("{:%%C='%C'%t%%y='%y'%t%%Y='%Y'%t%n}");
check(SV("%C='-100'\t%y='99'\t%Y='-9999'\t\n"), fmt, std::chrono::year{-9'999});
check(SV("%C='-10'\t%y='99'\t%Y='-0999'\t\n"), fmt, std::chrono::year{-999});
check(SV("%C='-1'\t%y='99'\t%Y='-0099'\t\n"), fmt, std::chrono::year{-99});