diff --git a/libcxx/docs/Status/FormatPaper.csv b/libcxx/docs/Status/FormatPaper.csv --- a/libcxx/docs/Status/FormatPaper.csv +++ b/libcxx/docs/Status/FormatPaper.csv @@ -6,7 +6,7 @@ `[time.syn] `_,"Formatter ``chrono::tai_time``",A ```` implementation,Not assigned,,, `[time.syn] `_,"Formatter ``chrono::gps_time``",A ```` implementation,Not assigned,,, `[time.syn] `_,"Formatter ``chrono::file_time``",,Mark de Wever,|Complete|, Clang 17 -`[time.syn] `_,"Formatter ``chrono::local_time``",,Mark de Wever,|In Progress|,, +`[time.syn] `_,"Formatter ``chrono::local_time``",,Mark de Wever,|Complete|, Clang 17 `[time.syn] `_,"Formatter ``chrono::local-time-format-t``",A ```` implementation,Not assigned,,, `[time.syn] `_,"Formatter ``chrono::day``",,Mark de Wever,|Complete|, Clang 16 `[time.syn] `_,"Formatter ``chrono::month``",,Mark de Wever,|Complete|, Clang 16 diff --git a/libcxx/include/__chrono/convert_to_tm.h b/libcxx/include/__chrono/convert_to_tm.h --- a/libcxx/include/__chrono/convert_to_tm.h +++ b/libcxx/include/__chrono/convert_to_tm.h @@ -10,6 +10,7 @@ #ifndef _LIBCPP___CHRONO_CONVERT_TO_TM_H #define _LIBCPP___CHRONO_CONVERT_TO_TM_H +#include <__chrono/calendar.h> #include <__chrono/concepts.h> #include <__chrono/day.h> #include <__chrono/duration.h> @@ -106,6 +107,8 @@ return std::__convert_to_tm<_Tm>(__value); else if constexpr (same_as) return std::__convert_to_tm<_Tm>(_ChronoT::clock::to_sys(__value)); + else if constexpr (same_as) + return std::__convert_to_tm<_Tm>(chrono::sys_time{__value.time_since_epoch()}); else static_assert(sizeof(_ChronoT) == 0, "TODO: Add the missing clock specialization"); } else if constexpr (chrono::__is_duration<_ChronoT>::value) { diff --git a/libcxx/include/__chrono/formatter.h b/libcxx/include/__chrono/formatter.h --- a/libcxx/include/__chrono/formatter.h +++ b/libcxx/include/__chrono/formatter.h @@ -603,6 +603,18 @@ } }; +template +struct _LIBCPP_TEMPLATE_VIS formatter, _CharT> : public __formatter_chrono<_CharT> { +public: + using _Base = __formatter_chrono<_CharT>; + + _LIBCPP_HIDE_FROM_ABI constexpr auto parse(basic_format_parse_context<_CharT>& __parse_ctx) + -> decltype(__parse_ctx.begin()) { + // The flags are not __clock since there is no associated time-zone. + return _Base::__parse(__parse_ctx, __format_spec::__fields_chrono, __format_spec::__flags::__date_time); + } +}; + template struct formatter, _CharT> : public __formatter_chrono<_CharT> { public: diff --git a/libcxx/include/__chrono/ostream.h b/libcxx/include/__chrono/ostream.h --- a/libcxx/include/__chrono/ostream.h +++ b/libcxx/include/__chrono/ostream.h @@ -10,6 +10,7 @@ #ifndef _LIBCPP___CHRONO_OSTREAM_H #define _LIBCPP___CHRONO_OSTREAM_H +#include <__chrono/calendar.h> #include <__chrono/day.h> #include <__chrono/duration.h> #include <__chrono/file_clock.h> @@ -52,6 +53,12 @@ return __os << std::format(__os.getloc(), _LIBCPP_STATICALLY_WIDEN(_CharT, "{:L%F %T}"), __tp); } +template +_LIBCPP_HIDE_FROM_ABI basic_ostream<_CharT, _Traits>& +operator<<(basic_ostream<_CharT, _Traits>& __os, const local_time<_Duration> __tp) { + return __os << sys_time<_Duration>{__tp.time_since_epoch()}; +} + // Depending on the type the return is a const _CharT* or a basic_string<_CharT> template _LIBCPP_HIDE_FROM_ABI auto __units_suffix() { diff --git a/libcxx/include/chrono b/libcxx/include/chrono --- a/libcxx/include/chrono +++ b/libcxx/include/chrono @@ -315,6 +315,10 @@ template using file_time = time_point; // C++20 +template // C++20 + basic_ostream& + operator<<(basic_ostream& os, const file_time& tp); + class steady_clock { public: @@ -336,6 +340,10 @@ using local_seconds = local_time; using local_days = local_time; +template // C++20 + basic_ostream& + operator<<(basic_ostream& os, const local_time& tp); + // 25.8.2, class last_spec // C++20 struct last_spec; @@ -692,6 +700,10 @@ namespace std { template struct formatter, charT>; // C++20 + template + struct formatter, charT>; // C++20 + template + struct formatter, charT>; // C++20 template struct formatter, charT>; // C++20 template struct formatter; // C++20 diff --git a/libcxx/test/std/time/time.clock/time.clock.local/ostream.pass.cpp b/libcxx/test/std/time/time.clock/time.clock.local/ostream.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/time/time.clock/time.clock.local/ostream.pass.cpp @@ -0,0 +1,199 @@ +//===----------------------------------------------------------------------===// +// +// 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: libcpp-has-no-incomplete-format + +// TODO FMT Investigate Windows issues. +// UNSUPPORTED msvc, target={{.+}}-windows-gnu +// TODO FMT It seems GCC uses too much memory in the CI and fails. +// UNSUPPORTED: gcc-12 + +// 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 + +// + +// class local_t; + +// template +// basic_ostream& +// operator<<(basic_ostream& os, const local_time& tp); + +#include +#include +#include +#include + +#include "make_string.h" +#include "platform_support.h" // locale name macros +#include "test_macros.h" + +#define SV(S) MAKE_STRING_VIEW(CharT, S) + +template +static std::basic_string stream_c_locale(std::chrono::local_time time_point) { + std::basic_stringstream sstr; + sstr << std::fixed << time_point; + return sstr.str(); +} + +template +static std::basic_string stream_fr_FR_locale(std::chrono::local_time time_point) { + std::basic_stringstream sstr; + const std::locale locale(LOCALE_fr_FR_UTF_8); + sstr.imbue(locale); + sstr << std::fixed << time_point; + return sstr.str(); +} + +template +static std::basic_string stream_ja_JP_locale(std::chrono::local_time time_point) { + std::basic_stringstream sstr; + const std::locale locale(LOCALE_ja_JP_UTF_8); + sstr.imbue(locale); + sstr << std::fixed << time_point; + return sstr.str(); +} + +template +static void test_c() { + using namespace std::literals::chrono_literals; + + assert(stream_c_locale(std::chrono::local_time{946'688'523'123'456'789ns}) == + SV("2000-01-01 01:02:03.123456789")); + assert(stream_c_locale(std::chrono::local_time{946'688'523'123'456us}) == + SV("2000-01-01 01:02:03.123456")); + + assert(stream_c_locale(std::chrono::local_time{946'684'800'123ms}) == + SV("2000-01-01 00:00:00.123")); + assert(stream_c_locale(std::chrono::local_seconds{1'234'567'890s}) == SV("2009-02-13 23:31:30")); + assert(stream_c_locale(std::chrono::local_time{20'576'131min}) == + SV("2009-02-13 23:31:00")); + assert(stream_c_locale(std::chrono::local_time{342'935h}) == SV("2009-02-13 23:00:00")); + assert(stream_c_locale(std::chrono::local_days{std::chrono::days{14'288}}) == SV("2009-02-13 00:00:00")); + assert(stream_c_locale(std::chrono::local_time{std::chrono::weeks{2041}}) == + SV("2009-02-12 00:00:00")); + + assert(stream_c_locale(std::chrono::local_time>>{ + std::chrono::duration>{60}}) == SV("1970-01-01 00:02:00")); + assert(stream_c_locale(std::chrono::local_time>>{ + std::chrono::duration>{3600}}) == SV("1970-01-01 00:30:00.0")); + assert(stream_c_locale(std::chrono::local_time>>{ + std::chrono::duration>{3600}}) == SV("1970-01-01 00:15:00.00")); + assert(stream_c_locale(std::chrono::local_time>>{ + std::chrono::duration>{36611}}) == SV("1970-01-01 01:01:01.1")); + assert(stream_c_locale(std::chrono::local_time>>{ + std::chrono::duration>{12'345'678'9010}}) == SV("2009-02-13 23:31:30.10")); + + assert(stream_c_locale(std::chrono::local_time>>{ + std::chrono::duration>{123.456}}) == SV("1970-01-01 00:02:03")); + assert(stream_c_locale(std::chrono::local_time>>{ + std::chrono::duration>{123.456}}) == SV("1970-01-01 00:00:12.3")); + assert(stream_c_locale(std::chrono::local_time>>{ + std::chrono::duration>{123.456}}) == SV("1970-01-01 00:00:01.23")); +} + +template +static void test_fr_FR() { + using namespace std::literals::chrono_literals; + + assert(stream_fr_FR_locale(std::chrono::local_time{946'688'523'123'456'789ns}) == + SV("2000-01-01 01:02:03,123456789")); + assert(stream_fr_FR_locale(std::chrono::local_time{946'688'523'123'456us}) == + SV("2000-01-01 01:02:03,123456")); + + assert(stream_fr_FR_locale(std::chrono::local_time{946'684'800'123ms}) == + SV("2000-01-01 00:00:00,123")); + assert(stream_fr_FR_locale(std::chrono::local_seconds{1'234'567'890s}) == SV("2009-02-13 23:31:30")); + assert(stream_fr_FR_locale(std::chrono::local_time{20'576'131min}) == + SV("2009-02-13 23:31:00")); + assert(stream_fr_FR_locale(std::chrono::local_time{342'935h}) == + SV("2009-02-13 23:00:00")); + assert(stream_fr_FR_locale(std::chrono::local_days{std::chrono::days{14'288}}) == SV("2009-02-13 00:00:00")); + assert(stream_fr_FR_locale(std::chrono::local_time{std::chrono::weeks{2041}}) == + SV("2009-02-12 00:00:00")); + + assert(stream_fr_FR_locale(std::chrono::local_time>>{ + std::chrono::duration>{60}}) == SV("1970-01-01 00:02:00")); + assert(stream_fr_FR_locale(std::chrono::local_time>>{ + std::chrono::duration>{3600}}) == SV("1970-01-01 00:30:00,0")); + assert(stream_fr_FR_locale(std::chrono::local_time>>{ + std::chrono::duration>{3600}}) == SV("1970-01-01 00:15:00,00")); + assert(stream_fr_FR_locale(std::chrono::local_time>>{ + std::chrono::duration>{36611}}) == SV("1970-01-01 01:01:01,1")); + assert(stream_fr_FR_locale(std::chrono::local_time>>{ + std::chrono::duration>{12'345'678'9010}}) == SV("2009-02-13 23:31:30,10")); + + assert(stream_fr_FR_locale(std::chrono::local_time>>{ + std::chrono::duration>{123.456}}) == SV("1970-01-01 00:02:03")); + assert(stream_fr_FR_locale(std::chrono::local_time>>{ + std::chrono::duration>{123.456}}) == SV("1970-01-01 00:00:12,3")); + assert(stream_fr_FR_locale(std::chrono::local_time>>{ + std::chrono::duration>{123.456}}) == SV("1970-01-01 00:00:01,23")); +} + +template +static void test_ja_JP() { + using namespace std::literals::chrono_literals; + + assert(stream_ja_JP_locale(std::chrono::local_time{946'688'523'123'456'789ns}) == + SV("2000-01-01 01:02:03.123456789")); + assert(stream_ja_JP_locale(std::chrono::local_time{946'688'523'123'456us}) == + SV("2000-01-01 01:02:03.123456")); + + assert(stream_ja_JP_locale(std::chrono::local_time{946'684'800'123ms}) == + SV("2000-01-01 00:00:00.123")); + assert(stream_ja_JP_locale(std::chrono::local_seconds{1'234'567'890s}) == SV("2009-02-13 23:31:30")); + assert(stream_ja_JP_locale(std::chrono::local_time{20'576'131min}) == + SV("2009-02-13 23:31:00")); + assert(stream_ja_JP_locale(std::chrono::local_time{342'935h}) == + SV("2009-02-13 23:00:00")); + assert(stream_ja_JP_locale(std::chrono::local_days{std::chrono::days{14'288}}) == SV("2009-02-13 00:00:00")); + assert(stream_ja_JP_locale(std::chrono::local_time{std::chrono::weeks{2041}}) == + SV("2009-02-12 00:00:00")); + + assert(stream_ja_JP_locale(std::chrono::local_time>>{ + std::chrono::duration>{60}}) == SV("1970-01-01 00:02:00")); + assert(stream_ja_JP_locale(std::chrono::local_time>>{ + std::chrono::duration>{3600}}) == SV("1970-01-01 00:30:00.0")); + assert(stream_ja_JP_locale(std::chrono::local_time>>{ + std::chrono::duration>{3600}}) == SV("1970-01-01 00:15:00.00")); + assert(stream_ja_JP_locale(std::chrono::local_time>>{ + std::chrono::duration>{36611}}) == SV("1970-01-01 01:01:01.1")); + assert(stream_ja_JP_locale(std::chrono::local_time>>{ + std::chrono::duration>{12'345'678'9010}}) == SV("2009-02-13 23:31:30.10")); + + assert(stream_ja_JP_locale(std::chrono::local_time>>{ + std::chrono::duration>{123.456}}) == SV("1970-01-01 00:02:03")); + assert(stream_ja_JP_locale(std::chrono::local_time>>{ + std::chrono::duration>{123.456}}) == SV("1970-01-01 00:00:12.3")); + assert(stream_ja_JP_locale(std::chrono::local_time>>{ + std::chrono::duration>{123.456}}) == SV("1970-01-01 00:00:01.23")); +} + +template +static void test() { + test_c(); + test_fr_FR(); + test_ja_JP(); +} + +int main(int, char**) { + test(); + +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test(); +#endif + + return 0; +} diff --git a/libcxx/test/std/time/time.syn/formatter.local_time.pass.cpp b/libcxx/test/std/time/time.syn/formatter.local_time.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/time/time.syn/formatter.local_time.pass.cpp @@ -0,0 +1,892 @@ +//===----------------------------------------------------------------------===// +// 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: libcpp-has-no-incomplete-format + +// XFAIL: LIBCXX-FREEBSD-FIXME + +// TODO FMT This test should not require std::to_chars(floating-point) +// XFAIL: availability-fp_to_chars-missing + +// TODO FMT Investigate Windows issues. +// UNSUPPORTED: msvc, target={{.+}}-windows-gnu +// TODO FMT It seems GCC uses too much memory in the CI and fails. +// UNSUPPORTED: gcc-12 + +// REQUIRES: locale.fr_FR.UTF-8 +// REQUIRES: locale.ja_JP.UTF-8 + +// +// +// template struct formatter; + +#include +#include + +#include +#include +#include +#include +#include + +#include "formatter_tests.h" +#include "make_string.h" +#include "platform_support.h" // locale name macros +#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("1970-01-01 00:00:00"), SV("{}"), std::chrono::local_seconds(0s)); + check(SV("2000-01-01 00:00:00"), SV("{}"), std::chrono::local_seconds(946'684'800s)); + check(SV("2000-01-01 01:02:03"), SV("{}"), std::chrono::local_seconds(946'688'523s)); + + check(SV("2000-01-01 01:02:03.123"), SV("{}"), std::chrono::local_time(946'688'523'123ms)); + + std::locale::global(std::locale::classic()); +} + +template +static void test_valid_values_year() { + using namespace std::literals::chrono_literals; + + constexpr std::basic_string_view fmt = + SV("{:%%C='%C'%t%%EC='%EC'%t%%y='%y'%t%%Oy='%Oy'%t%%Ey='%Ey'%t%%Y='%Y'%t%%EY='%EY'%n}"); + constexpr std::basic_string_view lfmt = + SV("{:L%%C='%C'%t%%EC='%EC'%t%%y='%y'%t%%Oy='%Oy'%t%%Ey='%Ey'%t%%Y='%Y'%t%%EY='%EY'%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='19'\t%EC='19'\t%y='70'\t%Oy='70'\t%Ey='70'\t%Y='1970'\t%EY='1970'\n"), + fmt, + std::chrono::local_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970 + + check(SV("%C='20'\t%EC='20'\t%y='09'\t%Oy='09'\t%Ey='09'\t%Y='2009'\t%EY='2009'\n"), + fmt, + std::chrono::local_seconds(1'234'567'890s)); // 23:31:30 UTC on Friday, 13 February 2009 + + // Use the global locale (fr_FR) + check(SV("%C='19'\t%EC='19'\t%y='70'\t%Oy='70'\t%Ey='70'\t%Y='1970'\t%EY='1970'\n"), + lfmt, + std::chrono::local_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970 + + check(SV("%C='20'\t%EC='20'\t%y='09'\t%Oy='09'\t%Ey='09'\t%Y='2009'\t%EY='2009'\n"), + lfmt, + std::chrono::local_seconds(1'234'567'890s)); // 23:31:30 UTC on Friday, 13 February 2009 + + // Use supplied locale (ja_JP). This locale has a different alternate. +#if defined(_WIN32) || defined(__APPLE__) || defined(_AIX) + check(loc, + SV("%C='19'\t%EC='19'\t%y='70'\t%Oy='70'\t%Ey='70'\t%Y='1970'\t%EY='1970'\n"), + lfmt, + std::chrono::local_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970 + + check(loc, + SV("%C='20'\t%EC='20'\t%y='09'\t%Oy='09'\t%Ey='09'\t%Y='2009'\t%EY='2009'\n"), + lfmt, + std::chrono::local_seconds(1'234'567'890s)); // 23:31:30 UTC on Friday, 13 February 2009 +#else // defined(_WIN32) || defined(__APPLE__) || defined(_AIX) + check(loc, + SV("%C='19'\t%EC='昭和'\t%y='70'\t%Oy='七十'\t%Ey='45'\t%Y='1970'\t%EY='昭和45年'\n"), + lfmt, + std::chrono::local_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970 + + check(loc, + SV("%C='20'\t%EC='平成'\t%y='09'\t%Oy='九'\t%Ey='21'\t%Y='2009'\t%EY='平成21年'\n"), + lfmt, + std::chrono::local_seconds(1'234'567'890s)); // 23:31:30 UTC on Friday, 13 February 2009 +#endif // defined(_WIN32) || defined(__APPLE__) || defined(_AIX) + + std::locale::global(std::locale::classic()); +} + +template +static void test_valid_values_month() { + using namespace std::literals::chrono_literals; + + constexpr std::basic_string_view fmt = SV("{:%%b='%b'%t%%h='%h'%t%%B='%B'%t%%m='%m'%t%%Om='%Om'%n}"); + constexpr std::basic_string_view lfmt = SV("{:L%%b='%b'%t%%h='%h'%t%%B='%B'%t%%m='%m'%t%%Om='%Om'%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("%b='Jan'\t%h='Jan'\t%B='January'\t%m='01'\t%Om='01'\n"), + fmt, + std::chrono::local_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970 + + check(SV("%b='May'\t%h='May'\t%B='May'\t%m='05'\t%Om='05'\n"), + fmt, + std::chrono::local_seconds(2'000'000'000s)); // 03:33:20 UTC on Wednesday, 18 May 2033 + + // Use the global locale (fr_FR) +#if defined(__APPLE__) + check(SV("%b='jan'\t%h='jan'\t%B='janvier'\t%m='01'\t%Om='01'\n"), + lfmt, + std::chrono::local_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970 +#else + check(SV("%b='janv.'\t%h='janv.'\t%B='janvier'\t%m='01'\t%Om='01'\n"), + lfmt, + std::chrono::local_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970 +#endif + + check(SV("%b='mai'\t%h='mai'\t%B='mai'\t%m='05'\t%Om='05'\n"), + lfmt, + std::chrono::local_seconds(2'000'000'000s)); // 03:33:20 UTC on Wednesday, 18 May 2033 + + // Use supplied locale (ja_JP). This locale has a different alternate. +#ifdef _WIN32 + check(loc, + SV("%b='1'\t%h='1'\t%B='1月'\t%m='01'\t%Om='01'\n"), + lfmt, + std::chrono::local_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970 + + check(loc, + SV("%b='5'\t%h='5'\t%B='5月'\t%m='05'\t%Om='05'\n"), + lfmt, + std::chrono::local_seconds(2'000'000'000s)); // 03:33:20 UTC on Wednesday, 18 May 2033 +#elif defined(_AIX) // _WIN32 + check(loc, + SV("%b='1月'\t%h='1月'\t%B='1月'\t%m='01'\t%Om='01'\n"), + lfmt, + std::chrono::local_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970 + + check(loc, + SV("%b='5月'\t%h='5月'\t%B='5月'\t%m='05'\t%Om='05'\n"), + lfmt, + std::chrono::local_seconds(2'000'000'000s)); // 03:33:20 UTC on Wednesday, 18 May 2033 +#elif defined(__APPLE__) // _WIN32 + check(loc, + SV("%b=' 1'\t%h=' 1'\t%B='1月'\t%m='01'\t%Om='01'\n"), + lfmt, + std::chrono::local_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970 + + check(loc, + SV("%b=' 5'\t%h=' 5'\t%B='5月'\t%m='05'\t%Om='05'\n"), + lfmt, + std::chrono::local_seconds(2'000'000'000s)); // 03:33:20 UTC on Wednesday, 18 May 2033 +#else // _WIN32 + check(loc, + SV("%b=' 1月'\t%h=' 1月'\t%B='1月'\t%m='01'\t%Om='一'\n"), + lfmt, + std::chrono::local_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970 + + check(loc, + SV("%b=' 5月'\t%h=' 5月'\t%B='5月'\t%m='05'\t%Om='五'\n"), + lfmt, + std::chrono::local_seconds(2'000'000'000s)); // 03:33:20 UTC on Wednesday, 18 May 2033 +#endif // _WIN32 + + std::locale::global(std::locale::classic()); +} + +template +static void test_valid_values_day() { + using namespace std::literals::chrono_literals; + + constexpr std::basic_string_view fmt = SV("{:%%d='%d'%t%%Od='%Od'%t%%e='%e'%t%%Oe='%Oe'%n}"); + constexpr std::basic_string_view lfmt = SV("{:L%%d='%d'%t%%Od='%Od'%t%%e='%e'%t%%Oe='%Oe'%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("%d='01'\t%Od='01'\t%e=' 1'\t%Oe=' 1'\n"), + fmt, + std::chrono::local_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970 + + check(SV("%d='13'\t%Od='13'\t%e='13'\t%Oe='13'\n"), + fmt, + std::chrono::local_seconds(1'234'567'890s)); // 23:31:30 UTC on Friday, 13 February 2009 + + // Use the global locale (fr_FR) + check(SV("%d='01'\t%Od='01'\t%e=' 1'\t%Oe=' 1'\n"), + lfmt, + std::chrono::local_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970 + + check(SV("%d='13'\t%Od='13'\t%e='13'\t%Oe='13'\n"), + lfmt, + std::chrono::local_seconds(1'234'567'890s)); // 23:31:30 UTC on Friday, 13 February 2009 + + // Use supplied locale (ja_JP). This locale has a different alternate. +#if defined(_WIN32) || defined(__APPLE__) || defined(_AIX) + check(loc, + SV("%d='01'\t%Od='01'\t%e=' 1'\t%Oe=' 1'\n"), + lfmt, + std::chrono::local_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970 + + check(loc, + SV("%d='13'\t%Od='13'\t%e='13'\t%Oe='13'\n"), + lfmt, + std::chrono::local_seconds(1'234'567'890s)); // 23:31:30 UTC on Friday, 13 February 2009 +#else // _WIN32 + check(loc, + SV("%d='01'\t%Od='一'\t%e=' 1'\t%Oe='一'\n"), + lfmt, + std::chrono::local_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970 + + check(loc, + SV("%d='13'\t%Od='十三'\t%e='13'\t%Oe='十三'\n"), + lfmt, + std::chrono::local_seconds(1'234'567'890s)); // 23:31:30 UTC on Friday, 13 February 2009 + +#endif // _WIN32 + + std::locale::global(std::locale::classic()); +} + +template +static void test_valid_values_weekday() { + using namespace std::literals::chrono_literals; + + constexpr std::basic_string_view fmt = + SV("{:%%a='%a'%t%%A='%A'%t%%u='%u'%t%%Ou='%Ou'%t%%w='%w'%t%%Ow='%Ow'%n}"); + constexpr std::basic_string_view lfmt = + SV("{:L%%a='%a'%t%%A='%A'%t%%u='%u'%t%%Ou='%Ou'%t%%w='%w'%t%%Ow='%Ow'%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("%a='Thu'\t%A='Thursday'\t%u='4'\t%Ou='4'\t%w='4'\t%Ow='4'\n"), + fmt, + std::chrono::local_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970 + + check(SV("%a='Sun'\t%A='Sunday'\t%u='7'\t%Ou='7'\t%w='0'\t%Ow='0'\n"), + fmt, + std::chrono::local_seconds(4'294'967'295s)); // 06:28:15 UTC on Sunday, 7 February 2106 + + // Use the global locale (fr_FR) +#if defined(__APPLE__) + check(SV("%a='Jeu'\t%A='Jeudi'\t%u='4'\t%Ou='4'\t%w='4'\t%Ow='4'\n"), + lfmt, + std::chrono::local_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970 + + check(SV("%a='Dim'\t%A='Dimanche'\t%u='7'\t%Ou='7'\t%w='0'\t%Ow='0'\n"), + lfmt, + std::chrono::local_seconds(4'294'967'295s)); // 06:28:15 UTC on Sunday, 7 February 2106 +#else + check(SV("%a='jeu.'\t%A='jeudi'\t%u='4'\t%Ou='4'\t%w='4'\t%Ow='4'\n"), + lfmt, + std::chrono::local_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970 + + check(SV("%a='dim.'\t%A='dimanche'\t%u='7'\t%Ou='7'\t%w='0'\t%Ow='0'\n"), + lfmt, + std::chrono::local_seconds(4'294'967'295s)); // 06:28:15 UTC on Sunday, 7 February 2106 +#endif + + // Use supplied locale (ja_JP). + // This locale has a different alternate, but not on all platforms +#if defined(_WIN32) || defined(__APPLE__) || defined(_AIX) + check(loc, + SV("%a='木'\t%A='木曜日'\t%u='4'\t%Ou='4'\t%w='4'\t%Ow='4'\n"), + lfmt, + std::chrono::local_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970 + + check(loc, + SV("%a='日'\t%A='日曜日'\t%u='7'\t%Ou='7'\t%w='0'\t%Ow='0'\n"), + lfmt, + std::chrono::local_seconds(4'294'967'295s)); // 06:28:15 UTC on Sunday, 7 February 2106 +#else // defined(_WIN32) || defined(__APPLE__) || defined(_AIX) + check(loc, + SV("%a='木'\t%A='木曜日'\t%u='4'\t%Ou='四'\t%w='4'\t%Ow='四'\n"), + lfmt, + std::chrono::local_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970 + + check(loc, + SV("%a='日'\t%A='日曜日'\t%u='7'\t%Ou='七'\t%w='0'\t%Ow='〇'\n"), + lfmt, + std::chrono::local_seconds(4'294'967'295s)); // 06:28:15 UTC on Sunday, 7 February 2106 +#endif // defined(_WIN32) || defined(__APPLE__) || defined(_AIX) + + std::locale::global(std::locale::classic()); +} + +template +static void test_valid_values_day_of_year() { + using namespace std::literals::chrono_literals; + + constexpr std::basic_string_view fmt = SV("{:%%j='%j'%n}"); + constexpr std::basic_string_view lfmt = SV("{:L%%j='%j'%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("%j='001'\n"), fmt, std::chrono::local_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970 + check(SV("%j='138'\n"), fmt, std::chrono::local_seconds(2'000'000'000s)); // 03:33:20 UTC on Wednesday, 18 May 2033 + + // Use the global locale (fr_FR) + check(SV("%j='001'\n"), lfmt, std::chrono::local_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970 + check(SV("%j='138'\n"), lfmt, std::chrono::local_seconds(2'000'000'000s)); // 03:33:20 UTC on Wednesday, 18 May 2033 + + // Use supplied locale (ja_JP). This locale has a different alternate. + check(loc, SV("%j='001'\n"), lfmt, std::chrono::local_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970 + + check(loc, + SV("%j='138'\n"), + lfmt, + std::chrono::local_seconds(2'000'000'000s)); // 03:33:20 UTC on Wednesday, 18 May 2033 + + std::locale::global(std::locale::classic()); +} + +template +static void test_valid_values_week() { + using namespace std::literals::chrono_literals; + + constexpr std::basic_string_view fmt = SV("{:%%U='%U'%t%%OU='%OU'%t%%W='%W'%t%%OW='%OW'%n}"); + constexpr std::basic_string_view lfmt = SV("{:L%%U='%U'%t%%OU='%OU'%t%%W='%W'%t%%OW='%OW'%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("%U='00'\t%OU='00'\t%W='00'\t%OW='00'\n"), + fmt, + std::chrono::local_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970 + + check(SV("%U='20'\t%OU='20'\t%W='20'\t%OW='20'\n"), + fmt, + std::chrono::local_seconds(2'000'000'000s)); // 03:33:20 UTC on Wednesday, 18 May 2033 + + // Use the global locale (fr_FR) + check(SV("%U='00'\t%OU='00'\t%W='00'\t%OW='00'\n"), + lfmt, + std::chrono::local_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970 + + check(SV("%U='20'\t%OU='20'\t%W='20'\t%OW='20'\n"), + lfmt, + std::chrono::local_seconds(2'000'000'000s)); // 03:33:20 UTC on Wednesday, 18 May 2033 + + // Use supplied locale (ja_JP). This locale has a different alternate. +#if defined(_WIN32) || defined(__APPLE__) || defined(_AIX) + check(loc, + SV("%U='00'\t%OU='00'\t%W='00'\t%OW='00'\n"), + lfmt, + std::chrono::local_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970 + + check(loc, + SV("%U='20'\t%OU='20'\t%W='20'\t%OW='20'\n"), + lfmt, + std::chrono::local_seconds(2'000'000'000s)); // 03:33:20 UTC on Wednesday, 18 May 2033 +#else // defined(_WIN32) || defined(__APPLE__) || defined(_AIX) + check(loc, + SV("%U='00'\t%OU='〇'\t%W='00'\t%OW='〇'\n"), + lfmt, + std::chrono::local_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970 + + check(loc, + SV("%U='20'\t%OU='二十'\t%W='20'\t%OW='二十'\n"), + lfmt, + std::chrono::local_seconds(2'000'000'000s)); // 03:33:20 UTC on Wednesday, 18 May 2033 +#endif // defined(_WIN32) || defined(__APPLE__) || defined(_AIX) + std::locale::global(std::locale::classic()); +} + +template +static void test_valid_values_iso_8601_week() { + using namespace std::literals::chrono_literals; + + constexpr std::basic_string_view fmt = SV("{:%%g='%g'%t%%G='%G'%t%%V='%V'%t%%OV='%OV'%n}"); + constexpr std::basic_string_view lfmt = SV("{:L%%g='%g'%t%%G='%G'%t%%V='%V'%t%%OV='%OV'%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("%g='70'\t%G='1970'\t%V='01'\t%OV='01'\n"), + fmt, + std::chrono::local_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970 + + check(SV("%g='09'\t%G='2009'\t%V='07'\t%OV='07'\n"), + fmt, + std::chrono::local_seconds(1'234'567'890s)); // 23:31:30 UTC on Friday, 13 February 2009 + + // Use the global locale (fr_FR) + check(SV("%g='70'\t%G='1970'\t%V='01'\t%OV='01'\n"), + lfmt, + std::chrono::local_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970 + + check(SV("%g='09'\t%G='2009'\t%V='07'\t%OV='07'\n"), + lfmt, + std::chrono::local_seconds(1'234'567'890s)); // 23:31:30 UTC on Friday, 13 February 2009 + + // Use supplied locale (ja_JP). This locale has a different alternate. +#if defined(_WIN32) || defined(__APPLE__) || defined(_AIX) + check(loc, + SV("%g='70'\t%G='1970'\t%V='01'\t%OV='01'\n"), + lfmt, + std::chrono::local_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970 + + check(loc, + SV("%g='09'\t%G='2009'\t%V='07'\t%OV='07'\n"), + lfmt, + std::chrono::local_seconds(1'234'567'890s)); // 23:31:30 UTC on Friday, 13 February 2009 +#else // defined(_WIN32) || defined(__APPLE__) || defined(_AIX) + check(loc, + SV("%g='70'\t%G='1970'\t%V='01'\t%OV='一'\n"), + lfmt, + std::chrono::local_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970 + + check(loc, + SV("%g='09'\t%G='2009'\t%V='07'\t%OV='七'\n"), + lfmt, + std::chrono::local_seconds(1'234'567'890s)); // 23:31:30 UTC on Friday, 13 February 2009 +#endif // defined(_WIN32) || defined(__APPLE__) || defined(_AIX) + + std::locale::global(std::locale::classic()); +} + +template +static void test_valid_values_date() { + using namespace std::literals::chrono_literals; + + constexpr std::basic_string_view fmt = SV("{:%%D='%D'%t%%F='%F'%t%%x='%x'%t%%Ex='%Ex'%n}"); + constexpr std::basic_string_view lfmt = SV("{:L%%D='%D'%t%%F='%F'%t%%x='%x'%t%%Ex='%Ex'%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("%D='01/01/70'\t%F='1970-01-01'\t%x='01/01/70'\t%Ex='01/01/70'\n"), + fmt, + std::chrono::local_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970 + + check(SV("%D='02/13/09'\t%F='2009-02-13'\t%x='02/13/09'\t%Ex='02/13/09'\n"), + fmt, + std::chrono::local_seconds(1'234'567'890s)); // 23:31:30 UTC on Friday, 13 February 2009 + + // Use the global locale (fr_FR) +#if defined(__APPLE__) + check(SV("%D='01/01/70'\t%F='1970-01-01'\t%x='01.01.1970'\t%Ex='01.01.1970'\n"), + lfmt, + std::chrono::local_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970 + + check(SV("%D='02/13/09'\t%F='2009-02-13'\t%x='13.02.2009'\t%Ex='13.02.2009'\n"), + lfmt, + std::chrono::local_seconds(1'234'567'890s)); // 23:31:30 UTC on Friday, 13 February 2009 +#else + check(SV("%D='01/01/70'\t%F='1970-01-01'\t%x='01/01/1970'\t%Ex='01/01/1970'\n"), + lfmt, + std::chrono::local_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970 + + check(SV("%D='02/13/09'\t%F='2009-02-13'\t%x='13/02/2009'\t%Ex='13/02/2009'\n"), + lfmt, + std::chrono::local_seconds(1'234'567'890s)); // 23:31:30 UTC on Friday, 13 February 2009 +#endif + + // Use supplied locale (ja_JP). This locale has a different alternate. +#if defined(_WIN32) || defined(__APPLE__) || defined(_AIX) + check(loc, + SV("%D='01/01/70'\t%F='1970-01-01'\t%x='1970/01/01'\t%Ex='1970/01/01'\n"), + lfmt, + std::chrono::local_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970 + + check(loc, + SV("%D='02/13/09'\t%F='2009-02-13'\t%x='2009/02/13'\t%Ex='2009/02/13'\n"), + lfmt, + std::chrono::local_seconds(1'234'567'890s)); // 23:31:30 UTC on Friday, 13 February 2009 +#else // defined(_WIN32) || defined(__APPLE__) || defined(_AIX) + check(loc, + SV("%D='01/01/70'\t%F='1970-01-01'\t%x='1970年01月01日'\t%Ex='昭和45年01月01日'\n"), + lfmt, + std::chrono::local_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970 + + check(loc, + SV("%D='02/13/09'\t%F='2009-02-13'\t%x='2009年02月13日'\t%Ex='平成21年02月13日'\n"), + lfmt, + std::chrono::local_seconds(1'234'567'890s)); // 23:31:30 UTC on Friday, 13 February 2009 +#endif // defined(_WIN32) || defined(__APPLE__) || defined(_AIX) + + std::locale::global(std::locale::classic()); +} + +template +static void test_valid_values_time() { + 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::local_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970 + + 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::local_time( + 1'234'567'890'123ms)); // 23:31:30 UTC on Friday, 13 February 2009 + // 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__) + "%r=''\t" +#else + "%r='12:00:00 '\t" +#endif + "%X='00:00:00'\t" + "%EX='00:00:00'\t" + "\n"), + lfmt, + std::chrono::local_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970 + + 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__) + "%r=''\t" +#else + "%r='11:31:30 '\t" +#endif + "%X='23:31:30'\t" + "%EX='23:31:30'\t" + "\n"), + lfmt, + std::chrono::local_time( + 1'234'567'890'123ms)); // 23:31:30 UTC on Friday, 13 February 2009 + + // Use supplied locale (ja_JP). This locale has a different alternate.a +#if defined(__APPLE__) || defined(_AIX) + 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__) + "%r='12:00:00 AM'\t" + "%X='00時00分00秒'\t" + "%EX='00時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__) + "%r='11:31:30 PM'\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)); +#else // defined(__APPLE__) || defined(_AIX) + 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::local_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970 + + 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::local_time( + 1'234'567'890'123ms)); // 23:31:30 UTC on Friday, 13 February 2009 +#endif // defined(__APPLE__) || defined(_AIX) + + std::locale::global(std::locale::classic()); +} + +template +static void test_valid_values_date_time() { + using namespace std::literals::chrono_literals; + + constexpr std::basic_string_view fmt = SV("{:%%c='%c'%t%%Ec='%Ec'%n}"); + constexpr std::basic_string_view lfmt = SV("{:L%%c='%c'%t%%Ec='%Ec'%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='Thu Jan 1 00:00:00 1970'\t%Ec='Thu Jan 1 00:00:00 1970'\n"), + fmt, + std::chrono::local_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970 + + check(SV("%c='Fri Feb 13 23:31:30 2009'\t%Ec='Fri Feb 13 23:31:30 2009'\n"), + fmt, + std::chrono::local_seconds(1'234'567'890s)); // 23:31:30 UTC on Friday, 13 February 2009 + + // Use the global locale (fr_FR) + check( +// https://sourceware.org/bugzilla/show_bug.cgi?id=24054 +#if defined(__GLIBC__) && __GLIBC__ <= 2 && __GLIBC_MINOR__ < 29 + SV("%c='jeu. 01 janv. 1970 00:00:00 GMT'\t%Ec='jeu. 01 janv. 1970 00:00:00 GMT'\n"), +#elif defined(_AIX) + SV("%c=' 1 janvier 1970 à 00:00:00 UTC'\t%Ec=' 1 janvier 1970 à 00:00:00 UTC'\n"), +#elif defined(__APPLE__) + SV("%c='Jeu 1 jan 00:00:00 1970'\t%Ec='Jeu 1 jan 00:00:00 1970'\n"), +#else + SV("%c='jeu. 01 janv. 1970 00:00:00'\t%Ec='jeu. 01 janv. 1970 00:00:00'\n"), +#endif + lfmt, + std::chrono::local_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970 + + check( +// https://sourceware.org/bugzilla/show_bug.cgi?id=24054 +#if defined(__GLIBC__) && __GLIBC__ <= 2 && __GLIBC_MINOR__ < 29 + SV("%c='ven. 13 févr. 2009 23:31:30 GMT'\t%Ec='ven. 13 févr. 2009 23:31:30 GMT'\n"), +#elif defined(_AIX) + SV("%c='13 février 2009 à 23:31:30 UTC'\t%Ec='13 février 2009 à 23:31:30 UTC'\n"), +#elif defined(__APPLE__) + SV("%c='Ven 13 fév 23:31:30 2009'\t%Ec='Ven 13 fév 23:31:30 2009'\n"), +#else + SV("%c='ven. 13 févr. 2009 23:31:30'\t%Ec='ven. 13 févr. 2009 23:31:30'\n"), +#endif + lfmt, + std::chrono::local_seconds(1'234'567'890s)); // 23:31:30 UTC on Friday, 13 February 2009 + + // Use supplied locale (ja_JP). This locale has a different alternate.a +#if defined(__APPLE__) + check(loc, + SV("%c='木 1/ 1 00:00:00 1970'\t%Ec='木 1/ 1 00:00:00 1970'\n"), + lfmt, + std::chrono::local_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970 + check(loc, + SV("%c='金 2/13 23:31:30 2009'\t%Ec='金 2/13 23:31:30 2009'\n"), + lfmt, + std::chrono::local_seconds(1'234'567'890s)); // 23:31:30 UTC on Friday, 13 February 2009 +#elif defined(_AIX) // __APPLE__ + check(loc, + SV("%c='1970年01月 1日 00:00:00 UTC'\t%Ec='1970年01月 1日 00:00:00 UTC'\n"), + lfmt, + std::chrono::local_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970 + check(loc, + SV("%c='2009年02月13日 23:31:30 UTC'\t%Ec='2009年02月13日 23:31:30 UTC'\n"), + lfmt, + std::chrono::local_seconds(1'234'567'890s)); // 23:31:30 UTC on Friday, 13 February 2009 +#else // __APPLE__ + check(loc, + SV("%c='1970年01月01日 00時00分00秒'\t%Ec='昭和45年01月01日 00時00分00秒'\n"), + lfmt, + std::chrono::local_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970 + + check(loc, + SV("%c='2009年02月13日 23時31分30秒'\t%Ec='平成21年02月13日 23時31分30秒'\n"), + lfmt, + std::chrono::local_seconds(1'234'567'890s)); // 23:31:30 UTC on Friday, 13 February 2009 +#endif // __APPLE__ + + std::locale::global(std::locale::classic()); +} + +template +static void test_valid_values() { + test_valid_values_year(); + test_valid_values_month(); + test_valid_values_day(); + test_valid_values_weekday(); + test_valid_values_day_of_year(); + test_valid_values_week(); + test_valid_values_iso_8601_week(); + test_valid_values_date(); + test_valid_values_time(); + test_valid_values_date_time(); +} + +template +static void test() { + using namespace std::literals::chrono_literals; + + test_no_chrono_specs(); + test_valid_values(); + check_invalid_types( + { + SV("a"), SV("A"), SV("b"), SV("B"), SV("c"), SV("C"), SV("d"), SV("D"), SV("e"), SV("F"), SV("g"), + SV("G"), SV("h"), SV("H"), SV("I"), SV("j"), SV("m"), SV("M"), SV("p"), SV("r"), SV("R"), SV("S"), + SV("T"), SV("u"), SV("U"), SV("V"), SV("w"), SV("W"), SV("x"), SV("X"), SV("y"), SV("Y"), SV("Ec"), + SV("EC"), SV("Ex"), SV("EX"), SV("Ey"), SV("EY"), SV("Od"), SV("Oe"), SV("OH"), SV("OI"), SV("Om"), SV("OM"), + SV("OS"), SV("Ou"), SV("OU"), SV("OV"), SV("Ow"), SV("OW"), SV("Oy"), + }, + std::chrono::local_seconds(0s)); + + check_exception("Expected '%' or '}' in the chrono format-string", SV("{:A"), std::chrono::local_seconds(0s)); + check_exception("The chrono-specs contains a '{'", SV("{:%%{"), std::chrono::local_seconds(0s)); + check_exception( + "End of input while parsing the modifier chrono conversion-spec", SV("{:%"), std::chrono::local_seconds(0s)); + check_exception("End of input while parsing the modifier E", SV("{:%E"), std::chrono::local_seconds(0s)); + check_exception("End of input while parsing the modifier O", SV("{:%O"), std::chrono::local_seconds(0s)); + + // Precision not allowed + check_exception("Expected '%' or '}' in the chrono format-string", SV("{:.3}"), std::chrono::local_seconds(0s)); +} + +int main(int, char**) { + test(); + +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test(); +#endif + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.formattable/concept.formattable.compile.pass.cpp b/libcxx/test/std/utilities/format/format.formattable/concept.formattable.compile.pass.cpp --- a/libcxx/test/std/utilities/format/format.formattable/concept.formattable.compile.pass.cpp +++ b/libcxx/test/std/utilities/format/format.formattable/concept.formattable.compile.pass.cpp @@ -141,7 +141,7 @@ //assert_is_formattable, CharT>(); //assert_is_formattable, CharT>(); assert_is_formattable, CharT>(); - assert_is_not_formattable, CharT>(); + assert_is_formattable, CharT>(); assert_is_formattable(); assert_is_formattable();