| //===----------------------------------------------------------------------===// | 
 | // | 
 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
 | // See https://llvm.org/LICENSE.txt for license information. | 
 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
 | // | 
 | //===----------------------------------------------------------------------===// | 
 |  | 
 | // UNSUPPORTED: c++03, c++11, c++14, c++17 | 
 | // UNSUPPORTED: no-localization | 
 | // UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME | 
 |  | 
 | // TODO FMT This test should not require std::to_chars(floating-point) | 
 | // XFAIL: availability-fp_to_chars-missing | 
 |  | 
 | // REQUIRES: locale.fr_FR.UTF-8 | 
 | // REQUIRES: locale.ja_JP.UTF-8 | 
 |  | 
 | // <chrono> | 
 |  | 
 | // template<class charT> struct formatter<chrono::year, charT>; | 
 |  | 
 | #include <chrono> | 
 | #include <format> | 
 |  | 
 | #include <cassert> | 
 | #include <concepts> | 
 | #include <locale> | 
 | #include <iostream> | 
 | #include <type_traits> | 
 |  | 
 | #include "formatter_tests.h" | 
 | #include "make_string.h" | 
 | #include "platform_support.h" // locale name macros | 
 | #include "string_literal.h" | 
 | #include "test_macros.h" | 
 |  | 
 | template <class CharT> | 
 | static void test_no_chrono_specs() { | 
 |   check(SV("-32767"), SV("{}"), std::chrono::year{-32'767}); | 
 |   check(SV("-1000"), SV("{}"), std::chrono::year{-1000}); | 
 |   check(SV("-0100"), SV("{}"), std::chrono::year{-100}); | 
 |   check(SV("-0010"), SV("{}"), std::chrono::year{-10}); | 
 |   check(SV("-0001"), SV("{}"), std::chrono::year{-1}); | 
 |   check(SV("0000"), SV("{}"), std::chrono::year{0}); | 
 |   check(SV("0001"), SV("{}"), std::chrono::year{1}); | 
 |   check(SV("0010"), SV("{}"), std::chrono::year{10}); | 
 |   check(SV("0100"), SV("{}"), std::chrono::year{100}); | 
 |   check(SV("1000"), SV("{}"), std::chrono::year{1000}); | 
 |   check(SV("32727"), SV("{}"), std::chrono::year{32'727}); | 
 |  | 
 |   // Invalid year | 
 |   check(SV("-32768 is not a valid year"), SV("{}"), std::chrono::year{-32'768}); | 
 |   check(SV("-32768 is not a valid year"), SV("{}"), std::chrono::year{32'768}); | 
 | } | 
 |  | 
 | template <class CharT> | 
 | static void test_valid_values() { | 
 |   constexpr std::basic_string_view<CharT> fmt = SV( | 
 |       "{:" | 
 |       "%%C='%C'%t" | 
 |       "%%EC='%EC'%t" | 
 |       "%%y='%y'%t" | 
 |       "%%Ey='%Ey'%t" | 
 |       "%%Oy='%Oy'%t" | 
 |       "%%Y='%Y'%t" | 
 |       "%%EY='%EY'%t" | 
 |       "%n}"); | 
 |   constexpr std::basic_string_view<CharT> lfmt = SV( | 
 |       "{:L" | 
 |       "%%C='%C'%t" | 
 |       "%%EC='%EC'%t" | 
 |       "%%y='%y'%t" | 
 |       "%%Ey='%Ey'%t" | 
 |       "%%Oy='%Oy'%t" | 
 |       "%%Y='%Y'%t" | 
 |       "%%EY='%EY'%t" | 
 |       "%n}"); | 
 |  | 
 |   const std::locale loc(LOCALE_ja_JP_UTF_8); | 
 |   std::locale::global(std::locale(LOCALE_fr_FR_UTF_8)); | 
 |  | 
 |   // Non localized output using C-locale | 
 |   check(SV("%C='00'\t" | 
 | #if defined(__APPLE__) || defined(_WIN32) || defined(__FreeBSD__) | 
 |            "%EC='00'\t" | 
 | #else | 
 |            "%EC='0'\t" | 
 | #endif | 
 |            "%y='00'\t" | 
 |            "%Ey='00'\t" | 
 |            "%Oy='00'\t" | 
 |            "%Y='0000'\t" | 
 | #if defined(__APPLE__) || defined(_WIN32) || defined(__FreeBSD__) | 
 |            "%EY='0000'\t" | 
 | #elif defined(_AIX) | 
 |            "%EY=''\t" | 
 | #else | 
 |            "%EY='0'\t" | 
 | #endif | 
 |            "\n"), | 
 |         fmt, | 
 |         std::chrono::year{0}); | 
 |  | 
 |   check(SV("%C='19'\t" | 
 |            "%EC='19'\t" | 
 |            "%y='70'\t" | 
 |            "%Ey='70'\t" | 
 |            "%Oy='70'\t" | 
 |            "%Y='1970'\t" | 
 |            "%EY='1970'\t" | 
 |            "\n"), | 
 |         fmt, | 
 |         std::chrono::year{1970}); | 
 |  | 
 |   check(SV("%C='20'\t" | 
 |            "%EC='20'\t" | 
 |            "%y='38'\t" | 
 |            "%Ey='38'\t" | 
 |            "%Oy='38'\t" | 
 |            "%Y='2038'\t" | 
 |            "%EY='2038'\t" | 
 |            "\n"), | 
 |         fmt, | 
 |         std::chrono::year{2038}); | 
 |  | 
 |   // Use the global locale (fr_FR) | 
 |   check(SV("%C='00'\t" | 
 | #if defined(__APPLE__) || defined(_WIN32) || defined(__FreeBSD__) | 
 |            "%EC='00'\t" | 
 | #else | 
 |            "%EC='0'\t" | 
 | #endif | 
 |            "%y='00'\t" | 
 |            "%Ey='00'\t" | 
 |            "%Oy='00'\t" | 
 |            "%Y='0000'\t" | 
 | #if defined(__APPLE__) || defined(_WIN32) || defined(__FreeBSD__) | 
 |            "%EY='0000'\t" | 
 | #elif defined(_AIX) | 
 |            "%EY=''\t" | 
 | #else | 
 |            "%EY='0'\t" | 
 | #endif | 
 |            "\n"), | 
 |         lfmt, | 
 |         std::chrono::year{0}); | 
 |  | 
 |   check(SV("%C='19'\t" | 
 |            "%EC='19'\t" | 
 |            "%y='70'\t" | 
 |            "%Ey='70'\t" | 
 |            "%Oy='70'\t" | 
 |            "%Y='1970'\t" | 
 |            "%EY='1970'\t" | 
 |            "\n"), | 
 |         lfmt, | 
 |         std::chrono::year{1970}); | 
 |  | 
 |   check(SV("%C='20'\t" | 
 |            "%EC='20'\t" | 
 |            "%y='38'\t" | 
 |            "%Ey='38'\t" | 
 |            "%Oy='38'\t" | 
 |            "%Y='2038'\t" | 
 |            "%EY='2038'\t" | 
 |            "\n"), | 
 |         lfmt, | 
 |         std::chrono::year{2038}); | 
 |  | 
 |   // Use supplied locale (ja_JP). This locale has a different alternate. | 
 | #if defined(__APPLE__) || defined(_AIX) || defined(_WIN32) || defined(__FreeBSD__) | 
 |  | 
 |   check(SV("%C='00'\t" | 
 | #  if defined(__APPLE__) || defined(_WIN32) || defined(__FreeBSD__) | 
 |            "%EC='00'\t" | 
 | #  else | 
 |            "%EC='0'\t" | 
 | #  endif | 
 |            "%y='00'\t" | 
 |            "%Ey='00'\t" | 
 |            "%Oy='00'\t" | 
 |            "%Y='0000'\t" | 
 | #  if defined(_AIX) | 
 |            "%EY=''\t" | 
 | #  else | 
 |            "%EY='0000'\t" | 
 | #  endif | 
 |            "\n"), | 
 |         lfmt, | 
 |         std::chrono::year{0}); | 
 |  | 
 |   check(SV("%C='19'\t" | 
 |            "%EC='19'\t" | 
 |            "%y='70'\t" | 
 |            "%Ey='70'\t" | 
 |            "%Oy='70'\t" | 
 |            "%Y='1970'\t" | 
 |            "%EY='1970'\t" | 
 |            "\n"), | 
 |         lfmt, | 
 |         std::chrono::year{1970}); | 
 |  | 
 |   check(SV("%C='20'\t" | 
 |            "%EC='20'\t" | 
 |            "%y='38'\t" | 
 |            "%Ey='38'\t" | 
 |            "%Oy='38'\t" | 
 |            "%Y='2038'\t" | 
 |            "%EY='2038'\t" | 
 |            "\n"), | 
 |         lfmt, | 
 |         std::chrono::year{2038}); | 
 |  | 
 | #else // defined(__APPLE__) || defined(_AIX) || defined(_WIN32) || defined(__FreeBSD__) | 
 |   check(loc, | 
 |         SV("%C='00'\t" | 
 |            "%EC='紀元前'\t" | 
 |            "%y='00'\t" | 
 |   // https://sourceware.org/bugzilla/show_bug.cgi?id=23758 | 
 | #  if defined(__GLIBC__) && __GLIBC__ <= 2 && __GLIBC_MINOR__ < 29 | 
 |            "%Ey='1'\t" | 
 | #  else | 
 |            "%Ey='01'\t" | 
 | #  endif | 
 |            "%Oy='〇'\t" | 
 |            "%Y='0000'\t" | 
 |   // https://sourceware.org/bugzilla/show_bug.cgi?id=23758 | 
 | #  if defined(__GLIBC__) && __GLIBC__ <= 2 && __GLIBC_MINOR__ < 29 | 
 |            "%EY='紀元前1年'\t" | 
 | #  else | 
 |            "%EY='紀元前01年'\t" | 
 | #  endif | 
 |            "\n"), | 
 |         lfmt, | 
 |         std::chrono::year{0}); | 
 |  | 
 |   check(loc, | 
 |         SV("%C='19'\t" | 
 |            "%EC='昭和'\t" | 
 |            "%y='70'\t" | 
 |            "%Ey='45'\t" | 
 |            "%Oy='七十'\t" | 
 |            "%Y='1970'\t" | 
 |            "%EY='昭和45年'\t" | 
 |            "\n"), | 
 |         lfmt, | 
 |         std::chrono::year{1970}); | 
 |  | 
 |   // Note this test will fail if the Reiwa era ends before 2038. | 
 |   check(loc, | 
 |         SV("%C='20'\t" | 
 |            "%EC='令和'\t" | 
 |            "%y='38'\t" | 
 |            "%Ey='20'\t" | 
 |            "%Oy='三十八'\t" | 
 |            "%Y='2038'\t" | 
 |            "%EY='令和20年'\t" | 
 |            "\n"), | 
 |         lfmt, | 
 |         std::chrono::year{2038}); | 
 | #endif // defined(__APPLE__) || defined(_AIX) || defined(_WIN32) || defined(__FreeBSD__) | 
 |  | 
 |   std::locale::global(std::locale::classic()); | 
 | } | 
 |  | 
 | 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}); | 
 |   check(SV("%C='-1'\t%y='09'\t%Y='-0009'\t\n"), fmt, std::chrono::year{-9}); | 
 |   check(SV("%C='00'\t%y='00'\t%Y='0000'\t\n"), fmt, std::chrono::year{0}); | 
 |   check(SV("%C='00'\t%y='09'\t%Y='0009'\t\n"), fmt, std::chrono::year{9}); | 
 |   check(SV("%C='00'\t%y='99'\t%Y='0099'\t\n"), fmt, std::chrono::year{99}); | 
 |   check(SV("%C='09'\t%y='99'\t%Y='0999'\t\n"), fmt, std::chrono::year{999}); | 
 |   check(SV("%C='99'\t%y='99'\t%Y='9999'\t\n"), fmt, std::chrono::year{9'999}); | 
 |   check(SV("%C='100'\t%y='00'\t%Y='10000'\t\n"), fmt, std::chrono::year{10'000}); | 
 | } | 
 |  | 
 | template <class CharT> | 
 | static void test() { | 
 |   test_no_chrono_specs<CharT>(); | 
 |   test_valid_values<CharT>(); | 
 |   test_padding<CharT>(); | 
 |   check_invalid_types<CharT>( | 
 |       {SV("C"), SV("y"), SV("Y"), SV("EC"), SV("Ey"), SV("EY"), SV("Oy")}, std::chrono::year{1970}); | 
 |  | 
 |   check_exception("The format specifier expects a '%' or a '}'", SV("{:A"), std::chrono::year{1970}); | 
 |   check_exception("The chrono specifiers contain a '{'", SV("{:%%{"), std::chrono::year{1970}); | 
 |   check_exception("End of input while parsing a conversion specifier", SV("{:%"), std::chrono::year{1970}); | 
 |   check_exception("End of input while parsing the modifier E", SV("{:%E"), std::chrono::year{1970}); | 
 |   check_exception("End of input while parsing the modifier O", SV("{:%O"), std::chrono::year{1970}); | 
 |  | 
 |   // Precision not allowed | 
 |   check_exception("The format specifier expects a '%' or a '}'", SV("{:.3}"), std::chrono::year{1970}); | 
 | } | 
 |  | 
 | int main(int, char**) { | 
 |   test<char>(); | 
 |  | 
 | #ifndef TEST_HAS_NO_WIDE_CHARACTERS | 
 |   test<wchar_t>(); | 
 | #endif | 
 |  | 
 |   return 0; | 
 | } |