[llvm-branch-commits] [libcxx] [libc++][TZDB] Adds sys_info formatter. (PR #85896)
Mark de Wever via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Thu Mar 21 04:46:06 PDT 2024
https://github.com/mordante updated https://github.com/llvm/llvm-project/pull/85896
>From c17bb85f4863f1fffa8654b4e1931dfebc430e2d Mon Sep 17 00:00:00 2001
From: Mark de Wever <koraq at xs4all.nl>
Date: Sun, 10 Mar 2024 17:49:39 +0100
Subject: [PATCH] [libc++][TZDB] Adds sys_info formatter.
Implements parts of:
- P0355 Extending <chrono> to Calendars and Time Zones
- P1361 Integration of chrono with text formatting
---
libcxx/docs/Status/FormatPaper.csv | 2 +-
libcxx/include/__chrono/convert_to_tm.h | 5 +
libcxx/include/__chrono/formatter.h | 58 ++++++--
libcxx/include/__chrono/ostream.h | 20 +++
libcxx/include/__chrono/sys_info.h | 6 +-
libcxx/include/chrono | 7 +-
.../time.zone.info.sys/ostream.pass.cpp | 74 ++++++++++
.../test/libcxx/transitive_includes/cxx23.csv | 1 -
.../test/libcxx/transitive_includes/cxx26.csv | 1 -
.../time/time.syn/formatter.sys_info.pass.cpp | 137 ++++++++++++++++++
.../time.zone.info.sys/ostream.pass.cpp | 52 +++++++
11 files changed, 344 insertions(+), 19 deletions(-)
create mode 100644 libcxx/test/libcxx/time/time.zone/time.zone.info/time.zone.info.sys/ostream.pass.cpp
create mode 100644 libcxx/test/std/time/time.syn/formatter.sys_info.pass.cpp
create mode 100644 libcxx/test/std/time/time.zone/time.zone.info/time.zone.info.sys/ostream.pass.cpp
diff --git a/libcxx/docs/Status/FormatPaper.csv b/libcxx/docs/Status/FormatPaper.csv
index 82da54284c7386..32166ec72da753 100644
--- a/libcxx/docs/Status/FormatPaper.csv
+++ b/libcxx/docs/Status/FormatPaper.csv
@@ -24,7 +24,7 @@ Section,Description,Dependencies,Assignee,Status,First released version
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::year_month_weekday``",,Mark de Wever,|Complete|,16.0
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::year_month_weekday_last``",,Mark de Wever,|Complete|,16.0
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::hh_mm_ss<duration<Rep, Period>>``",,Mark de Wever,|Complete|,17.0
-`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::sys_info``",A ``<chrono>`` implementation,Mark de Wever,,
+`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::sys_info``",,Mark de Wever,|Complete|,19.0
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::local_info``",A ``<chrono>`` implementation,Mark de Wever,,
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::zoned_time<Duration, TimeZonePtr>``",A ``<chrono>`` implementation,Mark de Wever,,
diff --git a/libcxx/include/__chrono/convert_to_tm.h b/libcxx/include/__chrono/convert_to_tm.h
index 1301cd6f1f1ada..d2c5cf922ba671 100644
--- a/libcxx/include/__chrono/convert_to_tm.h
+++ b/libcxx/include/__chrono/convert_to_tm.h
@@ -20,6 +20,7 @@
#include <__chrono/month_weekday.h>
#include <__chrono/monthday.h>
#include <__chrono/statically_widen.h>
+#include <__chrono/sys_info.h>
#include <__chrono/system_clock.h>
#include <__chrono/time_point.h>
#include <__chrono/weekday.h>
@@ -171,6 +172,10 @@ _LIBCPP_HIDE_FROM_ABI _Tm __convert_to_tm(const _ChronoT& __value) {
if (__value.hours().count() > std::numeric_limits<decltype(__result.tm_hour)>::max())
std::__throw_format_error("Formatting hh_mm_ss, encountered an hour overflow");
__result.tm_hour = __value.hours().count();
+# if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
+ } else if constexpr (same_as<_ChronoT, chrono::sys_info>) {
+ // Has no time information.
+# endif
} else
static_assert(sizeof(_ChronoT) == 0, "Add the missing type specialization");
diff --git a/libcxx/include/__chrono/formatter.h b/libcxx/include/__chrono/formatter.h
index 8b8592041a1fb9..98976e20b1c820 100644
--- a/libcxx/include/__chrono/formatter.h
+++ b/libcxx/include/__chrono/formatter.h
@@ -24,6 +24,7 @@
#include <__chrono/ostream.h>
#include <__chrono/parser_std_format_spec.h>
#include <__chrono/statically_widen.h>
+#include <__chrono/sys_info.h>
#include <__chrono/system_clock.h>
#include <__chrono/time_point.h>
#include <__chrono/weekday.h>
@@ -185,10 +186,11 @@ __format_zone_offset(basic_stringstream<_CharT>& __sstr, chrono::seconds __offse
chrono::hh_mm_ss __hms{__offset};
std::ostreambuf_iterator<_CharT> __out_it{__sstr};
+ // Note HMS does not allow formatting hours > 23, but the offset is not limited to 24H.
+ std::format_to(__out_it, _LIBCPP_STATICALLY_WIDEN(_CharT, "{:02}"), __hms.hours().count());
if (__modifier)
- std::format_to(__out_it, _LIBCPP_STATICALLY_WIDEN(_CharT, "{:%H:%M}"), __hms);
- else
- std::format_to(__out_it, _LIBCPP_STATICALLY_WIDEN(_CharT, "{:%H%M}"), __hms);
+ __sstr << _CharT(':');
+ std::format_to(__out_it, _LIBCPP_STATICALLY_WIDEN(_CharT, "{:02}"), __hms.minutes().count());
}
// Helper to store the time zone information needed for formatting.
@@ -202,6 +204,12 @@ struct _LIBCPP_HIDE_FROM_ABI __time_zone {
template <class _Tp>
_LIBCPP_HIDE_FROM_ABI __time_zone __convert_to_time_zone([[maybe_unused]] const _Tp& __value) {
__time_zone __result;
+# if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
+ if constexpr (same_as<_Tp, chrono::sys_info>) {
+ __result.__offset = __value.offset;
+ __result.__abbrev = __value.abbrev;
+ }
+# endif
return __result;
}
@@ -323,14 +331,12 @@ _LIBCPP_HIDE_FROM_ABI void __format_chrono_using_chrono_specs(
break;
case _CharT('F'): {
- int __year = __t.tm_year + 1900;
- if (__year < 1000) {
- __formatter::__format_year(__sstr, __year);
- __sstr << std::format(_LIBCPP_STATICALLY_WIDEN(_CharT, "-{:02}-{:02}"), __t.tm_mon + 1, __t.tm_mday);
- } else
- __facet.put(
- {__sstr}, __sstr, _CharT(' '), std::addressof(__t), std::to_address(__s), std::to_address(__it + 1));
- } break;
+ // 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(__sstr, __year);
+ __sstr << std::format(_LIBCPP_STATICALLY_WIDEN(_CharT, "-{:02}-{:02}"), __t.tm_mon + 1, __t.tm_mday);
+ break;
case _CharT('z'):
__formatter::__format_zone_offset(__sstr, __z.__offset, false);
@@ -411,6 +417,10 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool __weekday_ok(const _Tp& __value) {
return __value.weekday().ok();
else if constexpr (__is_hh_mm_ss<_Tp>)
return true;
+# if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
+ else if constexpr (same_as<_Tp, chrono::sys_info>)
+ return true;
+# endif
else
static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
}
@@ -451,6 +461,10 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool __weekday_name_ok(const _Tp& __value) {
return __value.weekday().ok();
else if constexpr (__is_hh_mm_ss<_Tp>)
return true;
+# if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
+ else if constexpr (same_as<_Tp, chrono::sys_info>)
+ return true;
+# endif
else
static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
}
@@ -491,6 +505,10 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool __date_ok(const _Tp& __value) {
return __value.ok();
else if constexpr (__is_hh_mm_ss<_Tp>)
return true;
+# if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
+ else if constexpr (same_as<_Tp, chrono::sys_info>)
+ return true;
+# endif
else
static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
}
@@ -531,6 +549,10 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool __month_name_ok(const _Tp& __value) {
return __value.month().ok();
else if constexpr (__is_hh_mm_ss<_Tp>)
return true;
+# if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
+ else if constexpr (same_as<_Tp, chrono::sys_info>)
+ return true;
+# endif
else
static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
}
@@ -860,6 +882,20 @@ struct formatter<chrono::hh_mm_ss<_Duration>, _CharT> : public __formatter_chron
return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__time);
}
};
+
+# if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
+template <__fmt_char_type _CharT>
+struct formatter<chrono::sys_info, _CharT> : public __formatter_chrono<_CharT> {
+public:
+ using _Base = __formatter_chrono<_CharT>;
+
+ template <class _ParseContext>
+ _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
+ return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__time_zone);
+ }
+};
+# endif // !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
+
#endif // if _LIBCPP_STD_VER >= 20
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__chrono/ostream.h b/libcxx/include/__chrono/ostream.h
index b687ef8059d5f5..01608b8c29c9ce 100644
--- a/libcxx/include/__chrono/ostream.h
+++ b/libcxx/include/__chrono/ostream.h
@@ -19,6 +19,7 @@
#include <__chrono/month_weekday.h>
#include <__chrono/monthday.h>
#include <__chrono/statically_widen.h>
+#include <__chrono/sys_info.h>
#include <__chrono/system_clock.h>
#include <__chrono/weekday.h>
#include <__chrono/year.h>
@@ -262,6 +263,25 @@ operator<<(basic_ostream<_CharT, _Traits>& __os, const hh_mm_ss<_Duration> __hms
return __os << std::format(__os.getloc(), _LIBCPP_STATICALLY_WIDEN(_CharT, "{:L%T}"), __hms);
}
+# if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
+
+template <class _CharT, class _Traits>
+_LIBCPP_HIDE_FROM_ABI basic_ostream<_CharT, _Traits>&
+operator<<(basic_ostream<_CharT, _Traits>& __os, const sys_info& __info) {
+ // __info.abbrev is always std::basic_string<char>.
+ // Since these strings typically are short the conversion should be cheap.
+ std::basic_string<_CharT> __abbrev{__info.abbrev.begin(), __info.abbrev.end()};
+ return __os << std::format(
+ _LIBCPP_STATICALLY_WIDEN(_CharT, "[{:%F %T}, {:%F %T}) {:%T} {:%Q%q} {}"),
+ __info.begin,
+ __info.end,
+ hh_mm_ss{__info.offset},
+ __info.save,
+ __abbrev);
+}
+
+# endif // !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
+
} // namespace chrono
#endif // if _LIBCPP_STD_VER >= 20
diff --git a/libcxx/include/__chrono/sys_info.h b/libcxx/include/__chrono/sys_info.h
index 16ec5dd254d59a..461d5322d413b3 100644
--- a/libcxx/include/__chrono/sys_info.h
+++ b/libcxx/include/__chrono/sys_info.h
@@ -28,8 +28,7 @@
_LIBCPP_BEGIN_NAMESPACE_STD
-# if _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \
- !defined(_LIBCPP_HAS_NO_LOCALIZATION)
+# if _LIBCPP_STD_VER >= 20
namespace chrono {
@@ -43,8 +42,7 @@ struct sys_info {
} // namespace chrono
-# endif // _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM)
- // && !defined(_LIBCPP_HAS_NO_LOCALIZATION)
+# endif // _LIBCPP_STD_VER >= 20
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/chrono b/libcxx/include/chrono
index 00b940a6610a3a..91e57f7f4fb62b 100644
--- a/libcxx/include/chrono
+++ b/libcxx/include/chrono
@@ -733,6 +733,10 @@ struct sys_info {
string abbrev;
};
+template<class charT, class traits> // C++20
+ basic_ostream<charT, traits>&
+ operator<<(basic_ostream<charT, traits>& os, const sys_info& si);
+
// 25.10.5, class time_zone // C++20
enum class choose {earliest, latest};
class time_zone {
@@ -829,6 +833,7 @@ namespace std {
template<class charT> struct formatter<chrono::year_month_weekday_last, charT>; // C++20
template<class Rep, class Period, class charT>
struct formatter<chrono::hh_mm_ss<duration<Rep, Period>>, charT>; // C++20
+ template<class charT> struct formatter<chrono::sys_info, charT>; // C++20
} // namespace std
namespace chrono {
@@ -893,6 +898,7 @@ constexpr chrono::year operator ""y(unsigned lo
#include <__chrono/month_weekday.h>
#include <__chrono/monthday.h>
#include <__chrono/steady_clock.h>
+#include <__chrono/sys_info.h>
#include <__chrono/system_clock.h>
#include <__chrono/time_point.h>
#include <__chrono/weekday.h>
@@ -918,7 +924,6 @@ constexpr chrono::year operator ""y(unsigned lo
#if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \
!defined(_LIBCPP_HAS_NO_LOCALIZATION)
# include <__chrono/leap_second.h>
-# include <__chrono/sys_info.h>
# include <__chrono/time_zone.h>
# include <__chrono/time_zone_link.h>
# include <__chrono/tzdb.h>
diff --git a/libcxx/test/libcxx/time/time.zone/time.zone.info/time.zone.info.sys/ostream.pass.cpp b/libcxx/test/libcxx/time/time.zone/time.zone.info/time.zone.info.sys/ostream.pass.cpp
new file mode 100644
index 00000000000000..bd274c047f168a
--- /dev/null
+++ b/libcxx/test/libcxx/time/time.zone/time.zone.info/time.zone.info.sys/ostream.pass.cpp
@@ -0,0 +1,74 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// TODO FMT This test should not require std::to_chars(floating-point)
+// XFAIL: availability-fp_to_chars-missing
+
+// XFAIL: libcpp-has-no-incomplete-tzdb
+
+// <chrono>
+
+// template<class charT, class traits>
+// basic_ostream<charT, traits>&
+// operator<<(basic_ostream<charT, traits>& os, const sys_info& r);
+
+// [time.zone.info.sys]
+// 7 Effects: Streams out the sys_info object r in an unspecified format.
+// 8 Returns: os.
+//
+// Tests the output produced by this function.
+
+#include <cassert>
+#include <chrono>
+#include <memory>
+#include <sstream>
+
+#include "assert_macros.h"
+#include "test_macros.h"
+#include "make_string.h"
+#include "concat_macros.h"
+
+#define SV(S) MAKE_STRING_VIEW(CharT, S)
+
+template <class CharT>
+static void test(std::basic_string_view<CharT> expected, std::chrono::sys_info&& info) {
+ std::basic_stringstream<CharT> sstr;
+ sstr << info;
+ std::basic_string<CharT> output = sstr.str();
+
+ TEST_REQUIRE(expected == output,
+ TEST_WRITE_CONCATENATED("\nExpected output ", expected, "\nActual output ", output, '\n'));
+}
+
+template <class CharT>
+static void test() {
+ using namespace std::literals::chrono_literals;
+ namespace tz = std::chrono;
+
+ test(SV("[-10484-10-16 15:30:08, 14423-03-17 15:30:07) 00:00:00 0min TZ"),
+ tz::sys_info{tz::sys_seconds::min(), tz::sys_seconds::max(), 0s, 0min, "TZ"});
+
+ test(SV("[1970-01-01 00:00:00, 2038-12-31 00:00:00) 12:23:45 -67min DMY"),
+ tz::sys_info{static_cast<tz::sys_days>(tz::year_month_day{1970y, tz::January, 1d}),
+ static_cast<tz::sys_days>(tz::year_month_day{2038y, tz::December, 31d}),
+ 12h + 23min + 45s,
+ -67min,
+ "DMY"});
+}
+
+int main(int, const char**) {
+ test<char>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+ test<wchar_t>();
+#endif
+
+ return 0;
+}
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index 8150f0935900e4..838ca02c16df5c 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -80,7 +80,6 @@ chrono cstring
chrono ctime
chrono cwchar
chrono forward_list
-chrono initializer_list
chrono limits
chrono new
chrono optional
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index 8150f0935900e4..838ca02c16df5c 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -80,7 +80,6 @@ chrono cstring
chrono ctime
chrono cwchar
chrono forward_list
-chrono initializer_list
chrono limits
chrono new
chrono optional
diff --git a/libcxx/test/std/time/time.syn/formatter.sys_info.pass.cpp b/libcxx/test/std/time/time.syn/formatter.sys_info.pass.cpp
new file mode 100644
index 00000000000000..1194f9ae8022b6
--- /dev/null
+++ b/libcxx/test/std/time/time.syn/formatter.sys_info.pass.cpp
@@ -0,0 +1,137 @@
+//===----------------------------------------------------------------------===//
+// 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
+
+// TODO FMT This test should not require std::to_chars(floating-point)
+// XFAIL: availability-fp_to_chars-missing
+
+// XFAIL: libcpp-has-no-incomplete-tzdb
+
+// REQUIRES: locale.fr_FR.UTF-8
+// REQUIRES: locale.ja_JP.UTF-8
+
+// <chrono>
+//
+// template<class charT> struct formatter<chrono::sys_info, 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 "test_macros.h"
+
+template <class CharT>
+static void test_no_chrono_specs() {
+// This test libc++ specific due to
+// [time.zone.info.sys]/7
+// Effects: Streams out the sys_info object r in an unspecified format.
+#ifdef _LIBCPP_VERSION
+ using namespace std::literals::chrono_literals;
+ namespace tz = std::chrono;
+
+ std::locale::global(std::locale(LOCALE_fr_FR_UTF_8));
+
+ // Non localized output
+
+ check(SV("[-10484-10-16 15:30:08, 14423-03-17 15:30:07) 00:00:00 0min TZ"),
+ SV("{}"),
+ tz::sys_info{tz::sys_seconds::min(), tz::sys_seconds::max(), 0s, 0min, "TZ"});
+
+ check(SV("[1970-01-01 00:00:00, 2038-12-31 00:00:00) 12:23:45 -67min DMY"),
+ SV("{}"),
+ tz::sys_info{static_cast<tz::sys_days>(tz::year_month_day{1970y, tz::January, 1d}),
+ static_cast<tz::sys_days>(tz::year_month_day{2038y, tz::December, 31d}),
+ 12h + 23min + 45s,
+ -67min,
+ "DMY"});
+
+ std::locale::global(std::locale::classic());
+#endif // _LIBCPP_VERSION
+}
+
+template <class CharT>
+static void test_valid_values() {
+ using namespace std::literals::chrono_literals;
+
+ constexpr std::basic_string_view<CharT> fmt = SV("{:%%z='%z'%t%%Ez='%Ez'%t%%Oz='%Oz'%t%%Z='%Z'%n}");
+ constexpr std::basic_string_view<CharT> lfmt = SV("{:L%%z='%z'%t%%Ez='%Ez'%t%%Oz='%Oz'%t%%Z='%Z'%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("%z='-0200'\t%Ez='-02:00'\t%Oz='-02:00'\t%Z='NEG'\n"),
+ fmt,
+ std::chrono::sys_info{std::chrono::sys_seconds{0s}, std::chrono::sys_seconds{0s}, -2h, 0min, "NEG"});
+
+ check(SV("%z='+0000'\t%Ez='+00:00'\t%Oz='+00:00'\t%Z='ZERO'\n"),
+ fmt,
+ std::chrono::sys_info{std::chrono::sys_seconds{0s}, std::chrono::sys_seconds{0s}, 0s, 0min, "ZERO"});
+
+ check(SV("%z='+1115'\t%Ez='+11:15'\t%Oz='+11:15'\t%Z='POS'\n"),
+ fmt,
+ std::chrono::sys_info{std::chrono::sys_seconds{0s}, std::chrono::sys_seconds{0s}, 11h + 15min, 0min, "POS"});
+
+ // Use the global locale (fr_FR)
+ check(SV("%z='-0200'\t%Ez='-02:00'\t%Oz='-02:00'\t%Z='NEG'\n"),
+ lfmt,
+ std::chrono::sys_info{std::chrono::sys_seconds{0s}, std::chrono::sys_seconds{0s}, -2h, 0min, "NEG"});
+
+ check(SV("%z='+0000'\t%Ez='+00:00'\t%Oz='+00:00'\t%Z='ZERO'\n"),
+ lfmt,
+ std::chrono::sys_info{std::chrono::sys_seconds{0s}, std::chrono::sys_seconds{0s}, 0s, 0min, "ZERO"});
+
+ check(SV("%z='+1115'\t%Ez='+11:15'\t%Oz='+11:15'\t%Z='POS'\n"),
+ lfmt,
+ std::chrono::sys_info{std::chrono::sys_seconds{0s}, std::chrono::sys_seconds{0s}, 11h + 15min, 0min, "POS"});
+
+ // Use supplied locale (ja_JP).
+ check(loc,
+ SV("%z='-0200'\t%Ez='-02:00'\t%Oz='-02:00'\t%Z='NEG'\n"),
+ lfmt,
+ std::chrono::sys_info{std::chrono::sys_seconds{0s}, std::chrono::sys_seconds{0s}, -2h, 0min, "NEG"});
+
+ check(loc,
+ SV("%z='+0000'\t%Ez='+00:00'\t%Oz='+00:00'\t%Z='ZERO'\n"),
+ lfmt,
+ std::chrono::sys_info{std::chrono::sys_seconds{0s}, std::chrono::sys_seconds{0s}, 0s, 0min, "ZERO"});
+
+ check(loc,
+ SV("%z='+1115'\t%Ez='+11:15'\t%Oz='+11:15'\t%Z='POS'\n"),
+ lfmt,
+ std::chrono::sys_info{std::chrono::sys_seconds{0s}, std::chrono::sys_seconds{0s}, 11h + 15min, 0min, "POS"});
+
+ std::locale::global(std::locale::classic());
+}
+
+template <class CharT>
+static void test() {
+ test_no_chrono_specs<CharT>();
+ test_valid_values<CharT>();
+
+ check_invalid_types<CharT>({SV("z"), SV("Z"), SV("Ez"), SV("Oz")}, std::chrono::sys_info{});
+}
+
+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.zone/time.zone.info/time.zone.info.sys/ostream.pass.cpp b/libcxx/test/std/time/time.zone/time.zone.info/time.zone.info.sys/ostream.pass.cpp
new file mode 100644
index 00000000000000..82c4844b423c5a
--- /dev/null
+++ b/libcxx/test/std/time/time.zone/time.zone.info/time.zone.info.sys/ostream.pass.cpp
@@ -0,0 +1,52 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// TODO FMT This test should not require std::to_chars(floating-point)
+// XFAIL: availability-fp_to_chars-missing
+
+// XFAIL: libcpp-has-no-incomplete-tzdb
+
+// <chrono>
+
+// template<class charT, class traits>
+// basic_ostream<charT, traits>&
+// operator<<(basic_ostream<charT, traits>& os, const sys_info& r);
+
+// [time.zone.info.sys]
+// 7 Effects: Streams out the sys_info object r in an unspecified format.
+// 8 Returns: os.
+//
+// There is a private libc++ test that validates the exact output.
+
+#include <cassert>
+#include <chrono>
+#include <memory>
+#include <sstream>
+
+#include "test_macros.h"
+
+template <class CharT>
+static void test() {
+ using namespace std::literals::chrono_literals;
+ std::chrono::sys_info s{std::chrono::sys_seconds{0s}, std::chrono::sys_seconds{0s}, 0h, 0min, ""};
+ std::basic_ostringstream<CharT> os;
+ std::basic_ostream<CharT>& result = std::chrono::operator<<(os, s);
+ assert(std::addressof(result) == std::addressof(os));
+}
+
+int main(int, const char**) {
+ test<char>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+ test<wchar_t>();
+#endif
+
+ return 0;
+}
More information about the llvm-branch-commits
mailing list