[libcxx-commits] [libcxx] 8a21d59 - [libc++][TZDB] Adds local_info formatter. (#86256)

via libcxx-commits libcxx-commits at lists.llvm.org
Thu Apr 18 08:23:11 PDT 2024


Author: Mark de Wever
Date: 2024-04-18T17:23:07+02:00
New Revision: 8a21d59f29ef8d6011c6defa05ff7e8b7decc6d2

URL: https://github.com/llvm/llvm-project/commit/8a21d59f29ef8d6011c6defa05ff7e8b7decc6d2
DIFF: https://github.com/llvm/llvm-project/commit/8a21d59f29ef8d6011c6defa05ff7e8b7decc6d2.diff

LOG: [libc++][TZDB] Adds local_info formatter. (#86256)

This adds the local_info type and its formatting options.
The usage of the local_info object will be done in separate patches.

Implements parts of:
- P0355 Extending to Calendars and Time Zones
- P1361 Integration of chrono with text formatting

Added: 
    libcxx/include/__chrono/local_info.h
    libcxx/test/libcxx/time/time.zone/time.zone.info/time.zone.info.local/ostream.pass.cpp
    libcxx/test/std/time/time.syn/formatter.local_info.pass.cpp
    libcxx/test/std/time/time.zone/time.zone.info/time.zone.info.local/local_info,members.pass.cpp
    libcxx/test/std/time/time.zone/time.zone.info/time.zone.info.local/ostream.pass.cpp

Modified: 
    libcxx/docs/Status/FormatPaper.csv
    libcxx/include/CMakeLists.txt
    libcxx/include/__chrono/convert_to_tm.h
    libcxx/include/__chrono/formatter.h
    libcxx/include/__chrono/ostream.h
    libcxx/include/chrono
    libcxx/include/libcxx.imp
    libcxx/include/module.modulemap
    libcxx/modules/std/chrono.inc
    libcxx/test/std/utilities/format/format.formattable/concept.formattable.compile.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/Status/FormatPaper.csv b/libcxx/docs/Status/FormatPaper.csv
index 8ace18815f5375..f29f1f7ca74875 100644
--- a/libcxx/docs/Status/FormatPaper.csv
+++ b/libcxx/docs/Status/FormatPaper.csv
@@ -25,7 +25,7 @@ Section,Description,Dependencies,Assignee,Status,First released version
 `[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``",,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::local_info``",,Mark de Wever,|Complete|,19.0
 `[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::zoned_time<Duration, TimeZonePtr>``",A ``<chrono>`` implementation,Mark de Wever,,
 
 "`P2693R1 <https://wg21.link/P2693R1>`__","Formatting ``thread::id`` and ``stacktrace``"

diff  --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index dc364cc8d0a226..4ecd834c5382ae 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -270,6 +270,7 @@ set(files
   __chrono/high_resolution_clock.h
   __chrono/leap_second.h
   __chrono/literals.h
+  __chrono/local_info.h
   __chrono/month.h
   __chrono/month_weekday.h
   __chrono/monthday.h

diff  --git a/libcxx/include/__chrono/convert_to_tm.h b/libcxx/include/__chrono/convert_to_tm.h
index d2c5cf922ba671..f7256db3bea661 100644
--- a/libcxx/include/__chrono/convert_to_tm.h
+++ b/libcxx/include/__chrono/convert_to_tm.h
@@ -16,6 +16,7 @@
 #include <__chrono/duration.h>
 #include <__chrono/file_clock.h>
 #include <__chrono/hh_mm_ss.h>
+#include <__chrono/local_info.h>
 #include <__chrono/month.h>
 #include <__chrono/month_weekday.h>
 #include <__chrono/monthday.h>
@@ -175,6 +176,8 @@ _LIBCPP_HIDE_FROM_ABI _Tm __convert_to_tm(const _ChronoT& __value) {
 #  if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
   } else if constexpr (same_as<_ChronoT, chrono::sys_info>) {
     // Has no time information.
+  } else if constexpr (same_as<_ChronoT, chrono::local_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 0196deb8c1ffb3..6a14c344fa1188 100644
--- a/libcxx/include/__chrono/formatter.h
+++ b/libcxx/include/__chrono/formatter.h
@@ -18,6 +18,7 @@
 #include <__chrono/duration.h>
 #include <__chrono/file_clock.h>
 #include <__chrono/hh_mm_ss.h>
+#include <__chrono/local_info.h>
 #include <__chrono/month.h>
 #include <__chrono/month_weekday.h>
 #include <__chrono/monthday.h>
@@ -419,6 +420,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool __weekday_ok(const _Tp& __value) {
 #  if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
   else if constexpr (same_as<_Tp, chrono::sys_info>)
     return true;
+  else if constexpr (same_as<_Tp, chrono::local_info>)
+    return true;
 #  endif
   else
     static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
@@ -463,6 +466,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool __weekday_name_ok(const _Tp& __value) {
 #  if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
   else if constexpr (same_as<_Tp, chrono::sys_info>)
     return true;
+  else if constexpr (same_as<_Tp, chrono::local_info>)
+    return true;
 #  endif
   else
     static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
@@ -507,6 +512,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool __date_ok(const _Tp& __value) {
 #  if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
   else if constexpr (same_as<_Tp, chrono::sys_info>)
     return true;
+  else if constexpr (same_as<_Tp, chrono::local_info>)
+    return true;
 #  endif
   else
     static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
@@ -551,6 +558,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool __month_name_ok(const _Tp& __value) {
 #  if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
   else if constexpr (same_as<_Tp, chrono::sys_info>)
     return true;
+  else if constexpr (same_as<_Tp, chrono::local_info>)
+    return true;
 #  endif
   else
     static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
@@ -893,6 +902,17 @@ struct formatter<chrono::sys_info, _CharT> : public __formatter_chrono<_CharT> {
     return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__time_zone);
   }
 };
+
+template <__fmt_char_type _CharT>
+struct formatter<chrono::local_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{});
+  }
+};
 #  endif // !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
 
 #endif // if _LIBCPP_STD_VER >= 20

diff  --git a/libcxx/include/__chrono/local_info.h b/libcxx/include/__chrono/local_info.h
new file mode 100644
index 00000000000000..b1a03ad7df2aca
--- /dev/null
+++ b/libcxx/include/__chrono/local_info.h
@@ -0,0 +1,50 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html
+
+#ifndef _LIBCPP___CHRONO_LOCAL_INFO_H
+#define _LIBCPP___CHRONO_LOCAL_INFO_H
+
+#include <version>
+// Enable the contents of the header only when libc++ was built with experimental features enabled.
+#if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
+
+#  include <__chrono/sys_info.h>
+#  include <__config>
+
+#  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#    pragma GCC system_header
+#  endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#  if _LIBCPP_STD_VER >= 20
+
+namespace chrono {
+
+struct local_info {
+  static constexpr int unique      = 0;
+  static constexpr int nonexistent = 1;
+  static constexpr int ambiguous   = 2;
+
+  int result;
+  sys_info first;
+  sys_info second;
+};
+
+} // namespace chrono
+
+#  endif // _LIBCPP_STD_VER >= 20
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
+
+#endif // _LIBCPP___CHRONO_LOCAL_INFO_H

diff  --git a/libcxx/include/__chrono/ostream.h b/libcxx/include/__chrono/ostream.h
index 9476406d761075..cb17dbea58bee3 100644
--- a/libcxx/include/__chrono/ostream.h
+++ b/libcxx/include/__chrono/ostream.h
@@ -15,6 +15,7 @@
 #include <__chrono/duration.h>
 #include <__chrono/file_clock.h>
 #include <__chrono/hh_mm_ss.h>
+#include <__chrono/local_info.h>
 #include <__chrono/month.h>
 #include <__chrono/month_weekday.h>
 #include <__chrono/monthday.h>
@@ -280,6 +281,27 @@ operator<<(basic_ostream<_CharT, _Traits>& __os, const sys_info& __info) {
              __abbrev);
 }
 
+template <class _CharT, class _Traits>
+_LIBCPP_HIDE_FROM_ABI basic_ostream<_CharT, _Traits>&
+operator<<(basic_ostream<_CharT, _Traits>& __os, const local_info& __info) {
+  auto __result = [&]() -> basic_string<_CharT> {
+    switch (__info.result) {
+    case local_info::unique:
+      return _LIBCPP_STATICALLY_WIDEN(_CharT, "unique");
+    case local_info::nonexistent:
+      return _LIBCPP_STATICALLY_WIDEN(_CharT, "non-existent");
+    case local_info::ambiguous:
+      return _LIBCPP_STATICALLY_WIDEN(_CharT, "ambiguous");
+
+    default:
+      return std::format(_LIBCPP_STATICALLY_WIDEN(_CharT, "unspecified result ({})"), __info.result);
+    };
+  };
+
+  return __os << std::format(
+             _LIBCPP_STATICALLY_WIDEN(_CharT, "{}: {{{}, {}}}"), __result(), __info.first, __info.second);
+}
+
 #  endif // !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
 
 } // namespace chrono

diff  --git a/libcxx/include/chrono b/libcxx/include/chrono
index f0275ec1eba7ae..96a3e92faa81f2 100644
--- a/libcxx/include/chrono
+++ b/libcxx/include/chrono
@@ -737,6 +737,20 @@ template<class charT, class traits>
   basic_ostream<charT, traits>&
     operator<<(basic_ostream<charT, traits>& os, const sys_info& si);
 
+struct local_info {                                                             // C++20
+  static constexpr int unique      = 0;
+  static constexpr int nonexistent = 1;
+  static constexpr int ambiguous   = 2;
+
+  int result;
+  sys_info first;
+  sys_info second;
+};
+
+template<class charT, class traits>                                              // C++20
+  basic_ostream<charT, traits>&
+    operator<<(basic_ostream<charT, traits>& os, const local_info& li);
+
 // 25.10.5, class time_zone                                                      // C++20
 enum class choose {earliest, latest};
 class time_zone {
@@ -834,6 +848,7 @@ namespace std {
   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
+  template<class charT> struct formatter<chrono::local_info, charT>;              // C++20
 } // namespace std
 
 namespace chrono {
@@ -899,6 +914,7 @@ constexpr chrono::year                                  operator ""y(unsigned lo
 #  include <__chrono/day.h>
 #  include <__chrono/hh_mm_ss.h>
 #  include <__chrono/literals.h>
+#  include <__chrono/local_info.h>
 #  include <__chrono/month.h>
 #  include <__chrono/month_weekday.h>
 #  include <__chrono/monthday.h>

diff  --git a/libcxx/include/libcxx.imp b/libcxx/include/libcxx.imp
index c224090e72465e..1d12b6e8d86bba 100644
--- a/libcxx/include/libcxx.imp
+++ b/libcxx/include/libcxx.imp
@@ -267,6 +267,7 @@
   { include: [ "<__chrono/high_resolution_clock.h>", "private", "<chrono>", "public" ] },
   { include: [ "<__chrono/leap_second.h>", "private", "<chrono>", "public" ] },
   { include: [ "<__chrono/literals.h>", "private", "<chrono>", "public" ] },
+  { include: [ "<__chrono/local_info.h>", "private", "<chrono>", "public" ] },
   { include: [ "<__chrono/month.h>", "private", "<chrono>", "public" ] },
   { include: [ "<__chrono/month_weekday.h>", "private", "<chrono>", "public" ] },
   { include: [ "<__chrono/monthday.h>", "private", "<chrono>", "public" ] },

diff  --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index 62ac4316647483..64652c8307c9e6 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -1124,6 +1124,7 @@ module std_private_chrono_high_resolution_clock  [system] {
 }
 module std_private_chrono_leap_second            [system] { header "__chrono/leap_second.h" }
 module std_private_chrono_literals               [system] { header "__chrono/literals.h" }
+module std_private_chrono_local_info             [system] { header "__chrono/local_info.h" }
 module std_private_chrono_month                  [system] { header "__chrono/month.h" }
 module std_private_chrono_month_weekday          [system] { header "__chrono/month_weekday.h" }
 module std_private_chrono_monthday               [system] { header "__chrono/monthday.h" }

diff  --git a/libcxx/modules/std/chrono.inc b/libcxx/modules/std/chrono.inc
index 575e6347aecce1..1265e21dc54ef6 100644
--- a/libcxx/modules/std/chrono.inc
+++ b/libcxx/modules/std/chrono.inc
@@ -215,6 +215,7 @@ export namespace std {
 #    endif // if 0
 
     // [time.zone.info], information classes
+    using std::chrono::local_info;
     using std::chrono::sys_info;
 
 #    if 0

diff  --git a/libcxx/test/libcxx/time/time.zone/time.zone.info/time.zone.info.local/ostream.pass.cpp b/libcxx/test/libcxx/time/time.zone/time.zone.info/time.zone.info.local/ostream.pass.cpp
new file mode 100644
index 00000000000000..d18076a642ec6a
--- /dev/null
+++ b/libcxx/test/libcxx/time/time.zone/time.zone.info/time.zone.info.local/ostream.pass.cpp
@@ -0,0 +1,114 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 local_info& r);
+
+// [time.zone.info.local]
+//   7 Effects: Streams out the local_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::local_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;
+  // result values matching the "known" results
+  test(SV("unique: "
+          "{[-10484-10-16 15:30:08, 14423-03-17 15:30:07) 00:00:00 0min \"TZ\", "
+          "[1970-01-01 00:00:00, 1970-01-01 00:00:00) 00:00:00 0min \"\"}"),
+       tz::local_info{tz::local_info::unique,
+                      tz::sys_info{tz::sys_seconds::min(), tz::sys_seconds::max(), 0s, 0min, "TZ"},
+                      tz::sys_info{}});
+
+  test(SV("non-existent: "
+          "{[1970-01-01 00:00:00, 2038-12-31 00:00:00) 12:23:45 -67min \"NEG\", "
+          "[1970-01-01 00:00:00, 2038-12-31 00:00:00) -12:23:45 67min \"POS\"}"),
+       tz::local_info{
+           tz::local_info::nonexistent,
+           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,
+                        "NEG"},
+           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,
+                        "POS"}});
+
+  test(SV("ambiguous: "
+          "{[1970-01-01 00:00:00, 2038-12-31 00:00:00) 12:23:45 -67min \"NEG\", "
+          "[1970-01-01 00:00:00, 2038-12-31 00:00:00) -12:23:45 67min \"POS\"}"),
+       tz::local_info{
+           tz::local_info::ambiguous,
+           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,
+                        "NEG"},
+           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,
+                        "POS"}});
+
+  // result values not matching the "known" results
+  test(
+      SV("unspecified result (-1): "
+         "{[-10484-10-16 15:30:08, 14423-03-17 15:30:07) 00:00:00 0min \"TZ\", "
+         "[1970-01-01 00:00:00, 1970-01-01 00:00:00) 00:00:00 0min \"\"}"),
+      tz::local_info{-1, tz::sys_info{tz::sys_seconds::min(), tz::sys_seconds::max(), 0s, 0min, "TZ"}, tz::sys_info{}});
+  test(SV("unspecified result (3): "
+          "{[-10484-10-16 15:30:08, 14423-03-17 15:30:07) 00:00:00 0min \"TZ\", "
+          "[1970-01-01 00:00:00, 1970-01-01 00:00:00) 00:00:00 0min \"\"}"),
+       tz::local_info{3, tz::sys_info{tz::sys_seconds::min(), tz::sys_seconds::max(), 0s, 0min, "TZ"}, tz::sys_info{}});
+}
+
+int main(int, const 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.local_info.pass.cpp b/libcxx/test/std/time/time.syn/formatter.local_info.pass.cpp
new file mode 100644
index 00000000000000..ef36416c3fb232
--- /dev/null
+++ b/libcxx/test/std/time/time.syn/formatter.local_info.pass.cpp
@@ -0,0 +1,126 @@
+//===----------------------------------------------------------------------===//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: no-localization
+// UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
+
+// TODO FMT This test should not require std::to_chars(floating-point)
+// XFAIL: availability-fp_to_chars-missing
+
+// XFAIL: libcpp-has-no-incomplete-tzdb
+
+// REQUIRES: locale.fr_FR.UTF-8
+
+// <chrono>
+//
+// template<class charT> struct formatter<chrono::local_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.local]/3
+//   Effects: Streams out the local_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
+
+  // result values matching the "known" results
+  check(SV("unique: "
+           "{[-10484-10-16 15:30:08, 14423-03-17 15:30:07) 00:00:00 0min \"TZ\", "
+           "[1970-01-01 00:00:00, 1970-01-01 00:00:00) 00:00:00 0min \"\"}"),
+        SV("{}"),
+        tz::local_info{tz::local_info::unique,
+                       tz::sys_info{tz::sys_seconds::min(), tz::sys_seconds::max(), 0s, 0min, "TZ"},
+                       tz::sys_info{}});
+
+  check(SV("non-existent: "
+           "{[1970-01-01 00:00:00, 2038-12-31 00:00:00) 12:23:45 -67min \"NEG\", "
+           "[1970-01-01 00:00:00, 2038-12-31 00:00:00) -12:23:45 67min \"POS\"}"),
+        SV("{}"),
+        tz::local_info{
+            tz::local_info::nonexistent,
+            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,
+                         "NEG"},
+            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,
+                         "POS"}});
+
+  check(SV("ambiguous: "
+           "{[1970-01-01 00:00:00, 2038-12-31 00:00:00) 12:23:45 -67min \"NEG\", "
+           "[1970-01-01 00:00:00, 2038-12-31 00:00:00) -12:23:45 67min \"POS\"}"),
+        SV("{}"),
+        tz::local_info{
+            tz::local_info::ambiguous,
+            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,
+                         "NEG"},
+            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,
+                         "POS"}});
+
+  // result values not matching the "known" results
+  check(
+      SV("unspecified result (-1): "
+         "{[-10484-10-16 15:30:08, 14423-03-17 15:30:07) 00:00:00 0min \"TZ\", "
+         "[1970-01-01 00:00:00, 1970-01-01 00:00:00) 00:00:00 0min \"\"}"),
+      SV("{}"),
+      tz::local_info{-1, tz::sys_info{tz::sys_seconds::min(), tz::sys_seconds::max(), 0s, 0min, "TZ"}, tz::sys_info{}});
+  check(
+      SV("unspecified result (3): "
+         "{[-10484-10-16 15:30:08, 14423-03-17 15:30:07) 00:00:00 0min \"TZ\", "
+         "[1970-01-01 00:00:00, 1970-01-01 00:00:00) 00:00:00 0min \"\"}"),
+      SV("{}"),
+      tz::local_info{3, tz::sys_info{tz::sys_seconds::min(), tz::sys_seconds::max(), 0s, 0min, "TZ"}, tz::sys_info{}});
+
+  std::locale::global(std::locale::classic());
+#endif // _LIBCPP_VERSION
+}
+
+template <class CharT>
+static void test() {
+  test_no_chrono_specs<CharT>();
+
+  check_invalid_types<CharT>({}, std::chrono::local_info{0, {}, {}});
+}
+
+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.local/local_info,members.pass.cpp b/libcxx/test/std/time/time.zone/time.zone.info/time.zone.info.local/local_info,members.pass.cpp
new file mode 100644
index 00000000000000..4e2a757a0a970f
--- /dev/null
+++ b/libcxx/test/std/time/time.zone/time.zone.info/time.zone.info.local/local_info,members.pass.cpp
@@ -0,0 +1,57 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// XFAIL: libcpp-has-no-incomplete-tzdb
+
+// <chrono>
+
+// struct local_info {
+//   static constexpr int unique      = 0;
+//   static constexpr int nonexistent = 1;
+//   static constexpr int ambiguous   = 2;
+//
+//   int result;
+//   sys_info first;
+//   sys_info second;
+// };
+
+// Validates whether:
+// - The static members are present as static constexpr members.
+// - The members are present as non-const members.
+// - The struct is an aggregate.
+
+#include <chrono>
+#include <string>
+#include <type_traits>
+
+int main(int, const char**) {
+  {
+    constexpr const int& result = std::chrono::local_info::unique;
+    static_assert(result == 0);
+  }
+  {
+    constexpr const int& result = std::chrono::local_info::nonexistent;
+    static_assert(result == 1);
+  }
+  {
+    constexpr const int& result = std::chrono::local_info::ambiguous;
+    static_assert(result == 2);
+  }
+
+  static_assert(std::is_aggregate_v<std::chrono::local_info>);
+
+  std::chrono::local_info local_info{.result = 0, .first = std::chrono::sys_info{}, .second = std::chrono::sys_info{}};
+
+  [[maybe_unused]] int& result                   = local_info.result;
+  [[maybe_unused]] std::chrono::sys_info& first  = local_info.first;
+  [[maybe_unused]] std::chrono::sys_info& second = local_info.second;
+
+  return 0;
+}

diff  --git a/libcxx/test/std/time/time.zone/time.zone.info/time.zone.info.local/ostream.pass.cpp b/libcxx/test/std/time/time.zone/time.zone.info/time.zone.info.local/ostream.pass.cpp
new file mode 100644
index 00000000000000..d9bf066b068783
--- /dev/null
+++ b/libcxx/test/std/time/time.zone/time.zone.info/time.zone.info.local/ostream.pass.cpp
@@ -0,0 +1,53 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 local_info& r);
+
+// [time.zone.info.local]
+//   3 Effects: Streams out the local_info object r in an unspecified format.
+//   4 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::chrono::local_info l{0, s, s};
+  std::basic_ostringstream<CharT> os;
+  std::basic_ostream<CharT>& result = std::chrono::operator<<(os, l);
+  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;
+}

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
index f40784ec446fae..fd3416f694ba7c 100644
--- 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
@@ -178,7 +178,7 @@ void test_P1361() {
 
 #  if !defined(TEST_HAS_NO_INCOMPLETE_TZDB)
   assert_is_formattable<std::chrono::sys_info, CharT>();
-  //assert_is_formattable<std::chrono::local_info, CharT>();
+  assert_is_formattable<std::chrono::local_info, CharT>();
 
   //assert_is_formattable<std::chrono::zoned_time, CharT>();
 #  endif // !defined(TEST_HAS_NO_INCOMPLETE_TZDB)


        


More information about the libcxx-commits mailing list