[libcxx-commits] [libcxx] 3eb4f16 - [libc++][chrono] Implements formatter year.
Mark de Wever via libcxx-commits
libcxx-commits at lists.llvm.org
Wed Oct 5 08:01:28 PDT 2022
Author: Mark de Wever
Date: 2022-10-05T17:01:19+02:00
New Revision: 3eb4f16b446ed4619d5c2ddeaec06040c83f189c
URL: https://github.com/llvm/llvm-project/commit/3eb4f16b446ed4619d5c2ddeaec06040c83f189c
DIFF: https://github.com/llvm/llvm-project/commit/3eb4f16b446ed4619d5c2ddeaec06040c83f189c.diff
LOG: [libc++][chrono] Implements formatter year.
Partially implements:
- P1361 Integration of chrono with text formatting
Reviewed By: ldionne, #libc
Differential Revision: https://reviews.llvm.org/D133663
Added:
libcxx/test/std/time/time.cal/time.cal.year/time.cal.year.nonmembers/ostream.pass.cpp
libcxx/test/std/time/time.syn/formatter.year.pass.cpp
Modified:
libcxx/docs/Status/FormatPaper.csv
libcxx/include/__chrono/convert_to_tm.h
libcxx/include/__chrono/formatter.h
libcxx/include/__chrono/ostream.h
libcxx/include/chrono
libcxx/test/libcxx/transitive_includes/cxx20.csv
libcxx/test/libcxx/transitive_includes/cxx2b.csv
libcxx/test/libcxx/utilities/format/format.formatter/format.formatter.spec/formattable.compile.pass.cpp
libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/types.compile.pass.cpp
libcxx/utils/ci/run-buildbot
Removed:
################################################################################
diff --git a/libcxx/docs/Status/FormatPaper.csv b/libcxx/docs/Status/FormatPaper.csv
index dffb39384c9dd..4e7c27d95e024 100644
--- a/libcxx/docs/Status/FormatPaper.csv
+++ b/libcxx/docs/Status/FormatPaper.csv
@@ -10,7 +10,7 @@ Section,Description,Dependencies,Assignee,Status,First released version
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::local-time-format-t<Duration>``",A ``<chrono>`` implementation,Not assigned,,,
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::day``",,Mark de Wever,|Complete|, Clang 16
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::month``",,Mark de Wever,|In Progress|,
-`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::year``",,Mark de Wever,|In Progress|,
+`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::year``",,Mark de Wever,|Complete|, Clang 16
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::weekday``",,Mark de Wever,|In Progress|,
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::weekday_indexed``",,Mark de Wever,|In Progress|,
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::weekday_last``",,Mark de Wever,|In Progress|,
diff --git a/libcxx/include/__chrono/convert_to_tm.h b/libcxx/include/__chrono/convert_to_tm.h
index 169d97b34d112..6c22b5a1277b3 100644
--- a/libcxx/include/__chrono/convert_to_tm.h
+++ b/libcxx/include/__chrono/convert_to_tm.h
@@ -11,6 +11,7 @@
#define _LIBCPP___CHRONO_CONVERT_TO_TM_H
#include <__chrono/day.h>
+#include <__chrono/year.h>
#include <__concepts/same_as.h>
#include <__config>
@@ -33,6 +34,8 @@ _LIBCPP_HIDE_FROM_ABI _Tm __convert_to_tm(const _ChronoCalendarTimePoint& __valu
if constexpr (same_as<_ChronoCalendarTimePoint, chrono::day>)
__result.tm_mday = static_cast<unsigned>(__value);
+ else if constexpr (same_as<_ChronoCalendarTimePoint, chrono::year>)
+ __result.tm_year = static_cast<int>(__value) - 1900;
else
static_assert(sizeof(_ChronoCalendarTimePoint) == 0, "Add the missing type specialization");
diff --git a/libcxx/include/__chrono/formatter.h b/libcxx/include/__chrono/formatter.h
index 47c79201e62ef..1dad41d3004f5 100644
--- a/libcxx/include/__chrono/formatter.h
+++ b/libcxx/include/__chrono/formatter.h
@@ -13,12 +13,17 @@
#include <__chrono/convert_to_tm.h>
#include <__chrono/day.h>
#include <__chrono/parser_std_format_spec.h>
+#include <__chrono/statically_widen.h>
+#include <__chrono/year.h>
#include <__config>
#include <__format/concepts.h>
+#include <__format/format_functions.h>
#include <__format/format_parse_context.h>
#include <__format/formatter.h>
#include <__format/formatter_output.h>
#include <__format/parser_std_format_spec.h>
+#include <__memory/addressof.h>
+#include <cmath>
#include <ctime>
#include <sstream>
#include <string>
@@ -52,6 +57,34 @@ namespace __formatter {
///
/// When no chrono-specs are provided it uses the stream formatter.
+template <class _CharT>
+_LIBCPP_HIDE_FROM_ABI void __format_year(int __year, basic_stringstream<_CharT>& __sstr) {
+ if (__year < 0) {
+ __sstr << _CharT('-');
+ __year = -__year;
+ }
+
+ // TODO FMT Write an issue
+ // If the result has less than four digits it is zero-padded with 0 to two digits.
+ // is less -> has less
+ // left-padded -> zero-padded, otherwise the proper value would be 000-0.
+
+ // Note according to the wording it should be left padded, which is odd.
+ __sstr << std::format(_LIBCPP_STATICALLY_WIDEN(_CharT, "{:04}"), __year);
+}
+
+template <class _CharT>
+_LIBCPP_HIDE_FROM_ABI void __format_century(int __year, basic_stringstream<_CharT>& __sstr) {
+ // TODO FMT Write an issue
+ // [tab:time.format.spec]
+ // %C The year divided by 100 using floored division. If the result is a
+ // single decimal digit, it is prefixed with 0.
+
+ bool __negative = __year < 0;
+ int __century = (__year - (99 * __negative)) / 100; // floored division
+ __sstr << std::format(_LIBCPP_STATICALLY_WIDEN(_CharT, "{:02}"), __century);
+}
+
template <class _CharT, class _Tp>
_LIBCPP_HIDE_FROM_ABI void __format_chrono_using_chrono_specs(
const _Tp& __value, basic_stringstream<_CharT>& __sstr, basic_string_view<_CharT> __chrono_specs) {
@@ -74,7 +107,59 @@ _LIBCPP_HIDE_FROM_ABI void __format_chrono_using_chrono_specs(
__sstr << *__it;
break;
+ case _CharT('C'): {
+ // strftime's output is only defined in the range [00, 99].
+ int __year = __t.tm_year + 1900;
+ if (__year < 1000 || __year > 9999)
+ __formatter::__format_century(__year, __sstr);
+ else
+ __facet.put({__sstr}, __sstr, _CharT(' '), std::addressof(__t), __s, __it + 1);
+ } break;
+
+ // Unlike time_put and strftime the formatting library requires %Y
+ //
+ // [tab:time.format.spec]
+ // The year as a decimal number. If the result is less than four digits
+ // it is left-padded with 0 to four digits.
+ //
+ // This means years in the range (-1000, 1000) need manual formatting.
+ // It's unclear whether %EY needs the same treatment. For example the
+ // Japanese EY contains the era name and year. This is zero-padded to 2
+ // digits in time_put (note that older glibc versions didn't do
+ // padding.) However most eras won't reach 100 years, let alone 1000.
+ // So padding to 4 digits seems unwanted for Japanese.
+ //
+ // The same applies to %Ex since that too depends on the era.
+ //
+ // %x the locale's date representation is currently doesn't handle the
+ // zero-padding too.
+ //
+ // The 4 digits can be implemented better at a later time. On POSIX
+ // systems the required information can be extracted by nl_langinfo
+ // https://man7.org/linux/man-pages/man3/nl_langinfo.3.html
+ //
+ // Note since year < -1000 is expected to be rare it uses the more
+ // expensive year routine.
+ //
+ // TODO FMT evaluate the comment above.
+
+# if defined(__GLIBC__) || defined(_AIX)
+ 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)
+
+ 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), __s, __it + 1);
+ } break;
+
case _CharT('O'):
+ case _CharT('E'):
++__it;
[[fallthrough]];
default:
@@ -146,7 +231,19 @@ struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter<chrono::day, _
}
};
-#endif //if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_FORMAT)
+template <__fmt_char_type _CharT>
+struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter<chrono::year, _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()) {
+ return _Base::__parse(__parse_ctx, __format_spec::__fields_chrono, __format_spec::__flags::__year);
+ }
+};
+
+#endif // if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_FORMAT)
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__chrono/ostream.h b/libcxx/include/__chrono/ostream.h
index 7d6796452fdd4..b6dcc5035ef66 100644
--- a/libcxx/include/__chrono/ostream.h
+++ b/libcxx/include/__chrono/ostream.h
@@ -12,6 +12,7 @@
#include <__chrono/day.h>
#include <__chrono/statically_widen.h>
+#include <__chrono/year.h>
#include <__config>
#include <__format/format_functions.h>
#include <ostream>
@@ -40,6 +41,13 @@ operator<<(basic_ostream<_CharT, _Traits>& __os, const day& __d) {
: std::format(_LIBCPP_STATICALLY_WIDEN(_CharT, "{:02} is not a valid day"), static_cast<unsigned>(__d)));
}
+template <class _CharT, class _Traits>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT basic_ostream<_CharT, _Traits>&
+operator<<(basic_ostream<_CharT, _Traits>& __os, const year& __y) {
+ return __os << (__y.ok() ? std::format(_LIBCPP_STATICALLY_WIDEN(_CharT, "{:%Y}"), __y)
+ : std::format(_LIBCPP_STATICALLY_WIDEN(_CharT, "{:%Y} is not a valid year"), __y));
+}
+
} // namespace chrono
#endif //if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_FORMAT)
diff --git a/libcxx/include/chrono b/libcxx/include/chrono
index ce7c621e25e68..eefb26d7aa9d4 100644
--- a/libcxx/include/chrono
+++ b/libcxx/include/chrono
@@ -355,6 +355,9 @@ constexpr year operator+(const year& x, const years& y) noexcept;
constexpr year operator+(const years& x, const year& y) noexcept;
constexpr year operator-(const year& x, const years& y) noexcept;
constexpr years operator-(const year& x, const year& y) noexcept;
+template<class charT, class traits>
+ basic_ostream<charT, traits>&
+ operator<<(basic_ostream<charT, traits>& os, const year& y);
// 25.8.6, class weekday // C++20
class weekday;
@@ -620,6 +623,7 @@ bool operator>=(const time_zone& x, const time_zone& y) noexcept;
namespace std {
template<class charT> struct formatter<chrono::day, charT>; // C++20
+ template<class charT> struct formatter<chrono::year, charT>; // C++20
} // namespace std
namespace chrono {
diff --git a/libcxx/test/libcxx/transitive_includes/cxx20.csv b/libcxx/test/libcxx/transitive_includes/cxx20.csv
index 346e40b24eb57..b58d3d00ed4be 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx20.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv
@@ -106,6 +106,7 @@ charconv type_traits
chrono array
chrono bit
chrono charconv
+chrono cmath
chrono compare
chrono concepts
chrono cstddef
diff --git a/libcxx/test/libcxx/transitive_includes/cxx2b.csv b/libcxx/test/libcxx/transitive_includes/cxx2b.csv
index e1e57466a6833..d052fb41f9ce8 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx2b.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx2b.csv
@@ -86,6 +86,7 @@ charconv type_traits
chrono array
chrono bit
chrono charconv
+chrono cmath
chrono compare
chrono concepts
chrono cstddef
diff --git a/libcxx/test/libcxx/utilities/format/format.formatter/format.formatter.spec/formattable.compile.pass.cpp b/libcxx/test/libcxx/utilities/format/format.formatter/format.formatter.spec/formattable.compile.pass.cpp
index 177900b181cd4..ccf7c58c06955 100644
--- a/libcxx/test/libcxx/utilities/format/format.formatter/format.formatter.spec/formattable.compile.pass.cpp
+++ b/libcxx/test/libcxx/utilities/format/format.formatter/format.formatter.spec/formattable.compile.pass.cpp
@@ -143,7 +143,7 @@ void test_P1361() {
assert_is_formattable<std::chrono::day, CharT>();
assert_is_not_formattable<std::chrono::month, CharT>();
- assert_is_not_formattable<std::chrono::year, CharT>();
+ assert_is_formattable<std::chrono::year, CharT>();
assert_is_not_formattable<std::chrono::weekday, CharT>();
assert_is_not_formattable<std::chrono::weekday_indexed, CharT>();
diff --git a/libcxx/test/std/time/time.cal/time.cal.year/time.cal.year.nonmembers/ostream.pass.cpp b/libcxx/test/std/time/time.cal/time.cal.year/time.cal.year.nonmembers/ostream.pass.cpp
new file mode 100644
index 0000000000000..c979fc2874ca7
--- /dev/null
+++ b/libcxx/test/std/time/time.cal/time.cal.year/time.cal.year.nonmembers/ostream.pass.cpp
@@ -0,0 +1,90 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// REQUIRES: locale.fr_FR.UTF-8
+// REQUIRES: locale.ja_JP.UTF-8
+
+// <chrono>
+// class year;
+
+// template<class charT, class traits>
+// basic_ostream<charT, traits>&
+// operator<<(basic_ostream<charT, traits>& os, const year& year);
+
+#include <chrono>
+#include <cassert>
+#include <sstream>
+
+#include "make_string.h"
+#include "platform_support.h" // locale name macros
+#include "test_macros.h"
+
+#define SV(S) MAKE_STRING_VIEW(CharT, S)
+
+template <class CharT>
+static std::basic_string<CharT> stream_c_locale(std::chrono::year year) {
+ std::basic_stringstream<CharT> sstr;
+ sstr << year;
+ return sstr.str();
+}
+
+template <class CharT>
+static std::basic_string<CharT> stream_fr_FR_locale(std::chrono::year year) {
+ std::basic_stringstream<CharT> sstr;
+ const std::locale locale(LOCALE_fr_FR_UTF_8);
+ sstr.imbue(locale);
+ sstr << year;
+ return sstr.str();
+}
+
+template <class CharT>
+static std::basic_string<CharT> stream_ja_JP_locale(std::chrono::year year) {
+ std::basic_stringstream<CharT> sstr;
+ const std::locale locale(LOCALE_ja_JP_UTF_8);
+ sstr.imbue(locale);
+ sstr << year;
+ return sstr.str();
+}
+
+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"));
+
+ 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"));
+
+ 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"));
+}
+
+int main(int, char**) {
+ test<char>();
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+ test<wchar_t>();
+#endif
+
+ return 0;
+}
diff --git a/libcxx/test/std/time/time.syn/formatter.year.pass.cpp b/libcxx/test/std/time/time.syn/formatter.year.pass.cpp
new file mode 100644
index 0000000000000..cbb2dcd135444
--- /dev/null
+++ b/libcxx/test/std/time/time.syn/formatter.year.pass.cpp
@@ -0,0 +1,308 @@
+//===----------------------------------------------------------------------===//
+// 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
+
+// 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__)
+ "%EC='00'\t"
+#else
+ "%EC='0'\t"
+#endif
+ "%y='00'\t"
+ "%Ey='00'\t"
+ "%Oy='00'\t"
+ "%Y='0000'\t"
+#if defined(__APPLE__)
+ "%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__)
+ "%EC='00'\t"
+#else
+ "%EC='0'\t"
+#endif
+ "%y='00'\t"
+ "%Ey='00'\t"
+ "%Oy='00'\t"
+ "%Y='0000'\t"
+#if defined(__APPLE__)
+ "%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
diff erent alternate.
+#if defined(__APPLE__) || defined(_AIX)
+
+ check(SV("%C='00'\t"
+# if defined(__APPLE__)
+ "%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)
+ 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)
+
+ 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("Expected '%' or '}' in the chrono format-string", SV("{:A"), std::chrono::year{1970});
+ check_exception("The chrono-specs contains a '{'", SV("{:%%{"), std::chrono::year{1970});
+ check_exception("End of input while parsing the modifier chrono conversion-spec", 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("Expected '%' or '}' in the chrono format-string", SV("{:.3}"), std::chrono::year{1970});
+}
+
+int main(int, char**) {
+ test<char>();
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+ test<wchar_t>();
+#endif
+
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/types.compile.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/types.compile.pass.cpp
index 8beaeb2cfe472..766f0422fbaaa 100644
--- a/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/types.compile.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/types.compile.pass.cpp
@@ -185,7 +185,7 @@ void test_P1361() {
assert_formatter_is_enabled<std::chrono::day, CharT>();
assert_formatter_is_disabled<std::chrono::month, CharT>();
- assert_formatter_is_disabled<std::chrono::year, CharT>();
+ assert_formatter_is_enabled<std::chrono::year, CharT>();
assert_formatter_is_disabled<std::chrono::weekday, CharT>();
assert_formatter_is_disabled<std::chrono::weekday_indexed, CharT>();
diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index 8a306801d7e02..d32bf5df2f36e 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -190,7 +190,7 @@ check-generated-output)
! grep -rn '[^ -~]' libcxx/include libcxx/src libcxx/test libcxx/benchmarks \
--exclude '*.dat' \
--exclude 'format_tests.h' \
- --exclude 'formatter.day.pass.cpp' \
+ --exclude 'formatter.*.pass.cpp' \
--exclude 'grep.pass.cpp' \
--exclude 'locale-specific_form.pass.cpp' \
--exclude 'std_format_spec_string_unicode.bench.cpp' \
More information about the libcxx-commits
mailing list