//===----------------------------------------------------------------------===// // // 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 // XFAIL: availability-fp_to_chars-missing // REQUIRES: locale.fr_FR.UTF-8 // REQUIRES: locale.ja_JP.UTF-8 // // template // struct formatter>, charT>; #include #include #include #include #include #include #include #include #include "formatter_tests.h" #include "make_string.h" #include "platform_support.h" // locale name macros #include "string_literal.h" #include "test_macros.h" template static void test_no_chrono_specs() { using namespace std::literals::chrono_literals; std::locale::global(std::locale(LOCALE_fr_FR_UTF_8)); // Non localized output check(SV("00:00:00.000"), SV("{}"), std::chrono::hh_mm_ss{0ms}); check(SV("*00:00:00.000*"), SV("{:*^14}"), std::chrono::hh_mm_ss{0ms}); check(SV("*00:00:00.000"), SV("{:*>13}"), std::chrono::hh_mm_ss{0ms}); std::locale::global(std::locale::classic()); } template static void test_valid_values() { using namespace std::literals::chrono_literals; constexpr std::basic_string_view fmt = SV( "{:" "%%H='%H'%t" "%%OH='%OH'%t" "%%I='%I'%t" "%%OI='%OI'%t" "%%M='%M'%t" "%%OM='%OM'%t" "%%S='%S'%t" "%%OS='%OS'%t" "%%p='%p'%t" "%%R='%R'%t" "%%T='%T'%t" "%%r='%r'%t" "%%X='%X'%t" "%%EX='%EX'%t" "%n}"); constexpr std::basic_string_view lfmt = SV( "{:L" "%%H='%H'%t" "%%OH='%OH'%t" "%%I='%I'%t" "%%OI='%OI'%t" "%%M='%M'%t" "%%OM='%OM'%t" "%%S='%S'%t" "%%OS='%OS'%t" "%%p='%p'%t" "%%R='%R'%t" "%%T='%T'%t" "%%r='%r'%t" "%%X='%X'%t" "%%EX='%EX'%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("%H='00'\t" "%OH='00'\t" "%I='12'\t" "%OI='12'\t" "%M='00'\t" "%OM='00'\t" "%S='00'\t" "%OS='00'\t" "%p='AM'\t" "%R='00:00'\t" "%T='00:00:00'\t" "%r='12:00:00 AM'\t" "%X='00:00:00'\t" "%EX='00:00:00'\t" "\n"), fmt, std::chrono::hh_mm_ss(0s)); check(SV("%H='23'\t" "%OH='23'\t" "%I='11'\t" "%OI='11'\t" "%M='31'\t" "%OM='31'\t" "%S='30.123'\t" "%OS='30.123'\t" "%p='PM'\t" "%R='23:31'\t" "%T='23:31:30.123'\t" "%r='11:31:30 PM'\t" "%X='23:31:30'\t" "%EX='23:31:30'\t" "\n"), fmt, std::chrono::hh_mm_ss(23h + 31min + 30s + 123ms)); check(SV("-%H='03'\t" "%OH='03'\t" "%I='03'\t" "%OI='03'\t" "%M='02'\t" "%OM='02'\t" "%S='01.123456789012'\t" "%OS='01.123456789012'\t" "%p='AM'\t" "%R='03:02'\t" "%T='03:02:01.123456789012'\t" "%r='03:02:01 AM'\t" "%X='03:02:01'\t" "%EX='03:02:01'\t" "\n"), fmt, std::chrono::hh_mm_ss(-(3h + 2min + 1s + std::chrono::duration(123456789012)))); // The number of fractional seconds is 0 according to the Standard // TODO FMT Determine what to do. check(SV("%H='01'\t" "%OH='01'\t" "%I='01'\t" "%OI='01'\t" "%M='01'\t" "%OM='01'\t" "%S='01'\t" "%OS='01'\t" "%p='AM'\t" "%R='01:01'\t" "%T='01:01:01'\t" "%r='01:01:01 AM'\t" "%X='01:01:01'\t" "%EX='01:01:01'\t" "\n"), fmt, std::chrono::hh_mm_ss(std::chrono::duration(3661.123456))); // Use the global locale (fr_FR) check(SV("%H='00'\t" "%OH='00'\t" "%I='12'\t" "%OI='12'\t" "%M='00'\t" "%OM='00'\t" "%S='00'\t" "%OS='00'\t" #if defined(_AIX) "%p='AM'\t" #else "%p=''\t" #endif "%R='00:00'\t" "%T='00:00:00'\t" #ifdef _WIN32 "%r='00:00:00'\t" #elif defined(_AIX) "%r='12:00:00 AM'\t" #elif defined(__APPLE__) || defined(__FreeBSD__) "%r=''\t" #else "%r='12:00:00 '\t" #endif "%X='00:00:00'\t" "%EX='00:00:00'\t" "\n"), lfmt, std::chrono::hh_mm_ss(0s)); check(SV("%H='23'\t" "%OH='23'\t" "%I='11'\t" "%OI='11'\t" "%M='31'\t" "%OM='31'\t" "%S='30,123'\t" "%OS='30,123'\t" #if defined(_AIX) "%p='PM'\t" #else "%p=''\t" #endif "%R='23:31'\t" "%T='23:31:30,123'\t" #ifdef _WIN32 "%r='23:31:30'\t" #elif defined(_AIX) "%r='11:31:30 PM'\t" #elif defined(__APPLE__) || defined(__FreeBSD__) "%r=''\t" #else "%r='11:31:30 '\t" #endif "%X='23:31:30'\t" "%EX='23:31:30'\t" "\n"), lfmt, std::chrono::hh_mm_ss(23h + 31min + 30s + 123ms)); check(SV("-%H='03'\t" "%OH='03'\t" "%I='03'\t" "%OI='03'\t" "%M='02'\t" "%OM='02'\t" "%S='01,123456789012'\t" "%OS='01,123456789012'\t" #if defined(_AIX) "%p='AM'\t" #else "%p=''\t" #endif "%R='03:02'\t" "%T='03:02:01,123456789012'\t" #ifdef _WIN32 "%r='03:02:01'\t" #elif defined(_AIX) "%r='03:02:01 AM'\t" #elif defined(__APPLE__) || defined(__FreeBSD__) "%r=''\t" #else "%r='03:02:01 '\t" #endif "%X='03:02:01'\t" "%EX='03:02:01'\t" "\n"), lfmt, std::chrono::hh_mm_ss(-(3h + 2min + 1s + std::chrono::duration(123456789012)))); check(SV("%H='01'\t" "%OH='01'\t" "%I='01'\t" "%OI='01'\t" "%M='01'\t" "%OM='01'\t" "%S='01'\t" "%OS='01'\t" #if defined(_AIX) "%p='AM'\t" #else "%p=''\t" #endif "%R='01:01'\t" "%T='01:01:01'\t" #ifdef _WIN32 "%r='01:01:01'\t" #elif defined(_AIX) "%r='01:01:01 AM'\t" #elif defined(__APPLE__) || defined(__FreeBSD__) "%r=''\t" #else "%r='01:01:01 '\t" #endif "%X='01:01:01'\t" "%EX='01:01:01'\t" "\n"), lfmt, std::chrono::hh_mm_ss(std::chrono::duration(3661.123456))); // Use supplied locale (ja_JP). This locale has a different alternate. #if defined(__APPLE__) || defined(_AIX) || defined(_WIN32) || defined(__FreeBSD__) check(loc, SV("%H='00'\t" "%OH='00'\t" "%I='12'\t" "%OI='12'\t" "%M='00'\t" "%OM='00'\t" "%S='00'\t" "%OS='00'\t" # if defined(__APPLE__) "%p='AM'\t" # else "%p='午前'\t" # endif "%R='00:00'\t" "%T='00:00:00'\t" # if defined(__APPLE__) || defined(__FreeBSD__) # if defined(__APPLE__) "%r='12:00:00 AM'\t" # else "%r='12:00:00 午前'\t" # endif "%X='00時00分00秒'\t" "%EX='00時00分00秒'\t" # elif defined(_WIN32) "%r='0:00:00'\t" "%X='0:00:00'\t" "%EX='0:00:00'\t" # else "%r='午前12:00:00'\t" "%X='00:00:00'\t" "%EX='00:00:00'\t" # endif "\n"), lfmt, std::chrono::hh_mm_ss(0s)); check(loc, SV("%H='23'\t" "%OH='23'\t" "%I='11'\t" "%OI='11'\t" "%M='31'\t" "%OM='31'\t" "%S='30.123'\t" "%OS='30.123'\t" # if defined(__APPLE__) "%p='PM'\t" # else "%p='午後'\t" # endif "%R='23:31'\t" "%T='23:31:30.123'\t" # if defined(__APPLE__) || defined(__FreeBSD__) # if defined(__APPLE__) "%r='11:31:30 PM'\t" # else "%r='11:31:30 午後'\t" # endif "%X='23時31分30秒'\t" "%EX='23時31分30秒'\t" # elif defined(_WIN32) "%r='23:31:30'\t" "%X='23:31:30'\t" "%EX='23:31:30'\t" # else "%r='午後11:31:30'\t" "%X='23:31:30'\t" "%EX='23:31:30'\t" # endif "\n"), lfmt, std::chrono::hh_mm_ss(23h + 31min + 30s + 123ms)); check(loc, SV("-%H='03'\t" "%OH='03'\t" "%I='03'\t" "%OI='03'\t" "%M='02'\t" "%OM='02'\t" "%S='01.123456789012'\t" "%OS='01.123456789012'\t" # if defined(__APPLE__) "%p='AM'\t" # else "%p='午前'\t" # endif "%R='03:02'\t" "%T='03:02:01.123456789012'\t" # if defined(__APPLE__) || defined(__FreeBSD__) # if defined(__APPLE__) "%r='03:02:01 AM'\t" # else "%r='03:02:01 午前'\t" # endif "%X='03時02分01秒'\t" "%EX='03時02分01秒'\t" # elif defined(_WIN32) "%r='3:02:01'\t" "%X='3:02:01'\t" "%EX='3:02:01'\t" # else "%r='午前03:02:01'\t" "%X='03:02:01'\t" "%EX='03:02:01'\t" # endif "\n"), lfmt, std::chrono::hh_mm_ss(-(3h + 2min + 1s + std::chrono::duration(123456789012)))); check(loc, SV("%H='01'\t" "%OH='01'\t" "%I='01'\t" "%OI='01'\t" "%M='01'\t" "%OM='01'\t" "%S='01'\t" "%OS='01'\t" # if defined(__APPLE__) "%p='AM'\t" # else "%p='午前'\t" # endif "%R='01:01'\t" "%T='01:01:01'\t" # if defined(__APPLE__) || defined(__FreeBSD__) # if defined(__APPLE__) "%r='01:01:01 AM'\t" # else "%r='01:01:01 午前'\t" # endif "%X='01時01分01秒'\t" "%EX='01時01分01秒'\t" # elif defined(_WIN32) "%r='1:01:01'\t" "%X='1:01:01'\t" "%EX='1:01:01'\t" # else "%r='午前01:01:01'\t" "%X='01:01:01'\t" "%EX='01:01:01'\t" # endif "\n"), lfmt, std::chrono::hh_mm_ss(std::chrono::duration(3661.123456))); #else // defined(__APPLE__) || defined(_AIX) || defined(_WIN32) check(loc, SV("%H='00'\t" "%OH='〇'\t" "%I='12'\t" "%OI='十二'\t" "%M='00'\t" "%OM='〇'\t" "%S='00'\t" "%OS='〇'\t" "%p='午前'\t" "%R='00:00'\t" "%T='00:00:00'\t" "%r='午前12時00分00秒'\t" "%X='00時00分00秒'\t" "%EX='00時00分00秒'\t" "\n"), lfmt, std::chrono::hh_mm_ss(0s)); // TODO FMT What should fractions be in alternate display mode? check(loc, SV("%H='23'\t" "%OH='二十三'\t" "%I='11'\t" "%OI='十一'\t" "%M='31'\t" "%OM='三十一'\t" "%S='30.123'\t" "%OS='三十.123'\t" "%p='午後'\t" "%R='23:31'\t" "%T='23:31:30.123'\t" "%r='午後11時31分30秒'\t" "%X='23時31分30秒'\t" "%EX='23時31分30秒'\t" "\n"), lfmt, std::chrono::hh_mm_ss(23h + 31min + 30s + 123ms)); check(loc, SV("-%H='03'\t" "%OH='三'\t" "%I='03'\t" "%OI='三'\t" "%M='02'\t" "%OM='二'\t" "%S='01.123456789012'\t" "%OS='一.123456789012'\t" "%p='午前'\t" "%R='03:02'\t" "%T='03:02:01.123456789012'\t" "%r='午前03時02分01秒'\t" "%X='03時02分01秒'\t" "%EX='03時02分01秒'\t" "\n"), lfmt, std::chrono::hh_mm_ss(-(3h + 2min + 1s + std::chrono::duration(123456789012)))); check(loc, SV("%H='01'\t" "%OH='一'\t" "%I='01'\t" "%OI='一'\t" "%M='01'\t" "%OM='一'\t" "%S='01'\t" "%OS='一'\t" "%p='午前'\t" "%R='01:01'\t" "%T='01:01:01'\t" "%r='午前01時01分01秒'\t" "%X='01時01分01秒'\t" "%EX='01時01分01秒'\t" "\n"), lfmt, std::chrono::hh_mm_ss(std::chrono::duration(3661.123456))); #endif // defined(__APPLE__) || defined(_AIX) || defined(_WIN32) std::locale::global(std::locale::classic()); } template static void test_invalid_values() { using namespace std::literals::chrono_literals; // This looks odd, however the 24 hours is not valid for a 24 hour clock. // TODO FMT discuss what the "proper" behaviour is. check_exception("Formatting a hour needs a valid value", SV("{:%H"), std::chrono::hh_mm_ss{24h}); check_exception("Formatting a hour needs a valid value", SV("{:%OH"), std::chrono::hh_mm_ss{24h}); check_exception("Formatting a hour needs a valid value", SV("{:%I"), std::chrono::hh_mm_ss{24h}); check_exception("Formatting a hour needs a valid value", SV("{:%OI"), std::chrono::hh_mm_ss{24h}); check(SV("00"), SV("{:%M}"), std::chrono::hh_mm_ss{24h}); check(SV("00"), SV("{:%OM}"), std::chrono::hh_mm_ss{24h}); check(SV("00"), SV("{:%S}"), std::chrono::hh_mm_ss{24h}); check(SV("00"), SV("{:%OS}"), std::chrono::hh_mm_ss{24h}); check_exception("Formatting a hour needs a valid value", SV("{:%p"), std::chrono::hh_mm_ss{24h}); check_exception("Formatting a hour needs a valid value", SV("{:%R"), std::chrono::hh_mm_ss{24h}); check_exception("Formatting a hour needs a valid value", SV("{:%T"), std::chrono::hh_mm_ss{24h}); check_exception("Formatting a hour needs a valid value", SV("{:%r"), std::chrono::hh_mm_ss{24h}); check_exception("Formatting a hour needs a valid value", SV("{:%X"), std::chrono::hh_mm_ss{24h}); check_exception("Formatting a hour needs a valid value", SV("{:%EX"), std::chrono::hh_mm_ss{24h}); } template static void test() { using namespace std::literals::chrono_literals; test_no_chrono_specs(); test_valid_values(); test_invalid_values(); check_invalid_types( {SV("H"), SV("I"), SV("M"), SV("S"), SV("p"), SV("r"), SV("R"), SV("T"), SV("X"), SV("OH"), SV("OI"), SV("OM"), SV("OS"), SV("EX")}, std::chrono::hh_mm_ss{0ms}); check_exception("The format specifier expects a '%' or a '}'", SV("{:A"), std::chrono::hh_mm_ss{0ms}); check_exception("The chrono specifiers contain a '{'", SV("{:%%{"), std::chrono::hh_mm_ss{0ms}); check_exception("End of input while parsing a conversion specifier", SV("{:%"), std::chrono::hh_mm_ss{0ms}); check_exception("End of input while parsing the modifier E", SV("{:%E"), std::chrono::hh_mm_ss{0ms}); check_exception("End of input while parsing the modifier O", SV("{:%O"), std::chrono::hh_mm_ss{0ms}); check_exception("The format specifier expects a '%' or a '}'", SV("{:.3}"), std::chrono::hh_mm_ss{0ms}); } int main(int, char**) { test(); #ifndef TEST_HAS_NO_WIDE_CHARACTERS test(); #endif return 0; }