Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[libc++][chrono] implements TAI clock. #125550

Merged
merged 6 commits into from
Feb 6, 2025
Merged

Conversation

mordante
Copy link
Member

@mordante mordante commented Feb 3, 2025

Implements parts of:

  • P0355 Extending to Calendars and Time Zones
  • P1361 Integration of chrono with text formatting
  • LWG3359 leap second support should allow for negative leap seconds

Implements parts of:
- P0355 Extending <chrono> to Calendars and Time Zones
- P1361 Integration of chrono with text formatting
- LWG3359 <chrono> leap second support should allow for negative leap seconds
@mordante mordante requested a review from a team as a code owner February 3, 2025 18:08
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Feb 3, 2025
@llvmbot
Copy link
Member

llvmbot commented Feb 3, 2025

@llvm/pr-subscribers-libcxx

Author: Mark de Wever (mordante)

Changes

Implements parts of:

  • P0355 Extending <chrono> to Calendars and Time Zones
  • P1361 Integration of chrono with text formatting
  • LWG3359 <chrono> leap second support should allow for negative leap seconds

Patch is 78.95 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/125550.diff

17 Files Affected:

  • (modified) libcxx/docs/Status/FormatPaper.csv (+1-1)
  • (modified) libcxx/include/CMakeLists.txt (+1)
  • (modified) libcxx/include/__chrono/convert_to_tm.h (+13)
  • (modified) libcxx/include/__chrono/formatter.h (+14)
  • (modified) libcxx/include/__chrono/ostream.h (+7)
  • (added) libcxx/include/__chrono/tai_clock.h (+98)
  • (modified) libcxx/include/chrono (+31)
  • (modified) libcxx/include/module.modulemap (+4)
  • (modified) libcxx/modules/std/chrono.inc (+1-1)
  • (modified) libcxx/test/libcxx/diagnostics/chrono.nodiscard.verify.cpp (+11)
  • (added) libcxx/test/std/time/time.clock/time.clock.tai/tai_time.ostream.pass.cpp (+164)
  • (added) libcxx/test/std/time/time.clock/time.clock.tai/time.clock.tai.members/from_utc.pass.cpp (+159)
  • (added) libcxx/test/std/time/time.clock/time.clock.tai/time.clock.tai.members/now.pass.cpp (+30)
  • (added) libcxx/test/std/time/time.clock/time.clock.tai/time.clock.tai.members/to_utc.pass.cpp (+161)
  • (added) libcxx/test/std/time/time.clock/time.clock.tai/types.compile.pass.cpp (+59)
  • (added) libcxx/test/std/time/time.syn/formatter.tai_time.pass.cpp (+998)
  • (modified) libcxx/test/support/concat_macros.h (+5)
diff --git a/libcxx/docs/Status/FormatPaper.csv b/libcxx/docs/Status/FormatPaper.csv
index 343fa62f135654..de64e9c25a7771 100644
--- a/libcxx/docs/Status/FormatPaper.csv
+++ b/libcxx/docs/Status/FormatPaper.csv
@@ -3,7 +3,7 @@ Section,Description,Dependencies,Assignee,Status,First released version
 `[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::duration<Rep, Period>``",,Mark de Wever,|Complete|,16
 `[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::sys_time<Duration>``",,Mark de Wever,|Complete|,17
 `[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::utc_time<Duration>``",A ``<chrono>`` implementation,Mark de Wever,|Complete|,20
-`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::tai_time<Duration>``",A ``<chrono>`` implementation,Mark de Wever,,,
++`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::tai_time<Duration>``",,Mark de Wever,|Complete|,21
 `[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::gps_time<Duration>``",A ``<chrono>`` implementation,Mark de Wever,,,
 `[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::file_time<Duration>``",,Mark de Wever,|Complete|,17
 `[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::local_time<Duration>``",,Mark de Wever,|Complete|,17
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 8dac823503d73f..ce805b4eb7b8b4 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -270,6 +270,7 @@ set(files
   __chrono/steady_clock.h
   __chrono/sys_info.h
   __chrono/system_clock.h
+  __chrono/tai_clock.h
   __chrono/time_point.h
   __chrono/time_zone.h
   __chrono/time_zone_link.h
diff --git a/libcxx/include/__chrono/convert_to_tm.h b/libcxx/include/__chrono/convert_to_tm.h
index 7d06a38d87f26d..934293ce382345 100644
--- a/libcxx/include/__chrono/convert_to_tm.h
+++ b/libcxx/include/__chrono/convert_to_tm.h
@@ -23,6 +23,7 @@
 #include <__chrono/statically_widen.h>
 #include <__chrono/sys_info.h>
 #include <__chrono/system_clock.h>
+#include <__chrono/tai_clock.h>
 #include <__chrono/time_point.h>
 #include <__chrono/utc_clock.h>
 #include <__chrono/weekday.h>
@@ -112,6 +113,16 @@ _LIBCPP_HIDE_FROM_ABI _Tm __convert_to_tm(chrono::utc_time<_Duration> __tp) {
   return __result;
 }
 
+template <class _Tm, class _Duration>
+_LIBCPP_HIDE_FROM_ABI _Tm __convert_to_tm(chrono::tai_time<_Duration> __tp) {
+  using _Rp = common_type_t<_Duration, chrono::seconds>;
+  // The time between the TAI epoch (1958-01-01) and UNIX epoch (1970-01-01).
+  // This avoids leap second conversion when going from TAI to UTC.
+  // (It also avoids issues when the date is before the UTC epoch.)
+  constexpr chrono::seconds __offset{4383 * 24 * 60 * 60};
+  return std::__convert_to_tm<_Tm>(chrono::sys_time<_Rp>{__tp.time_since_epoch() - __offset});
+}
+
 #    endif // _LIBCPP_HAS_EXPERIMENTAL_TZDB
 #  endif   // _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION
 
@@ -131,6 +142,8 @@ _LIBCPP_HIDE_FROM_ABI _Tm __convert_to_tm(const _ChronoT& __value) {
 #    if _LIBCPP_HAS_EXPERIMENTAL_TZDB
     else if constexpr (same_as<typename _ChronoT::clock, chrono::utc_clock>)
       return std::__convert_to_tm<_Tm>(__value);
+    else if constexpr (same_as<typename _ChronoT::clock, chrono::tai_clock>)
+      return std::__convert_to_tm<_Tm>(__value);
 #    endif // _LIBCPP_HAS_EXPERIMENTAL_TZDB
 #  endif   // _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION
     else if constexpr (same_as<typename _ChronoT::clock, chrono::file_clock>)
diff --git a/libcxx/include/__chrono/formatter.h b/libcxx/include/__chrono/formatter.h
index d17acd274e4cda..753a824a3c50d7 100644
--- a/libcxx/include/__chrono/formatter.h
+++ b/libcxx/include/__chrono/formatter.h
@@ -31,6 +31,7 @@
 #  include <__chrono/statically_widen.h>
 #  include <__chrono/sys_info.h>
 #  include <__chrono/system_clock.h>
+#  include <__chrono/tai_clock.h>
 #  include <__chrono/time_point.h>
 #  include <__chrono/utc_clock.h>
 #  include <__chrono/weekday.h>
@@ -231,6 +232,8 @@ _LIBCPP_HIDE_FROM_ABI __time_zone __convert_to_time_zone([[maybe_unused]] const
 #    if _LIBCPP_HAS_EXPERIMENTAL_TZDB
   if constexpr (same_as<_Tp, chrono::sys_info>)
     return {__value.abbrev, __value.offset};
+  else if constexpr (__is_time_point<_Tp> && requires { requires same_as<typename _Tp::clock, chrono::tai_clock>; })
+    return {"TAI", chrono::seconds{0}};
 #      if _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM
   else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)
     return __formatter::__convert_to_time_zone(__value.get_info());
@@ -734,6 +737,17 @@ struct _LIBCPP_TEMPLATE_VIS formatter<chrono::utc_time<_Duration>, _CharT> : pub
   }
 };
 
+template <class _Duration, __fmt_char_type _CharT>
+struct _LIBCPP_TEMPLATE_VIS formatter<chrono::tai_time<_Duration>, _CharT> : public __formatter_chrono<_CharT> {
+public:
+  using _Base _LIBCPP_NODEBUG = __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::__clock);
+  }
+};
+
 #      endif // _LIBCPP_HAS_EXPERIMENTAL_TZDB
 #    endif   // _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM
 
diff --git a/libcxx/include/__chrono/ostream.h b/libcxx/include/__chrono/ostream.h
index ed9ad8e346ba94..b8cd6a4680662b 100644
--- a/libcxx/include/__chrono/ostream.h
+++ b/libcxx/include/__chrono/ostream.h
@@ -26,6 +26,7 @@
 #  include <__chrono/statically_widen.h>
 #  include <__chrono/sys_info.h>
 #  include <__chrono/system_clock.h>
+#  include <__chrono/tai_clock.h>
 #  include <__chrono/utc_clock.h>
 #  include <__chrono/weekday.h>
 #  include <__chrono/year.h>
@@ -71,6 +72,12 @@ operator<<(basic_ostream<_CharT, _Traits>& __os, const utc_time<_Duration>& __tp
   return __os << std::format(__os.getloc(), _LIBCPP_STATICALLY_WIDEN(_CharT, "{:L%F %T}"), __tp);
 }
 
+template <class _CharT, class _Traits, class _Duration>
+_LIBCPP_HIDE_FROM_ABI basic_ostream<_CharT, _Traits>&
+operator<<(basic_ostream<_CharT, _Traits>& __os, const tai_time<_Duration>& __tp) {
+  return __os << std::format(__os.getloc(), _LIBCPP_STATICALLY_WIDEN(_CharT, "{:L%F %T}"), __tp);
+}
+
 #      endif // _LIBCPP_HAS_EXPERIMENTAL_TZDB
 #    endif   // _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM
 
diff --git a/libcxx/include/__chrono/tai_clock.h b/libcxx/include/__chrono/tai_clock.h
new file mode 100644
index 00000000000000..18ba329b7b8fb4
--- /dev/null
+++ b/libcxx/include/__chrono/tai_clock.h
@@ -0,0 +1,98 @@
+// -*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___CHRONO_TAI_CLOCK_H
+#define _LIBCPP___CHRONO_TAI_CLOCK_H
+
+#include <version>
+// Enable the contents of the header only when libc++ was built with experimental features enabled.
+#if _LIBCPP_HAS_EXPERIMENTAL_TZDB
+
+#  include <__chrono/duration.h>
+#  include <__chrono/time_point.h>
+#  include <__chrono/utc_clock.h>
+#  include <__config>
+#  include <__type_traits/common_type.h>
+
+#  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#    pragma GCC system_header
+#  endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#  if _LIBCPP_STD_VER >= 20 && _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION
+
+namespace chrono {
+
+class tai_clock;
+
+template <class _Duration>
+using tai_time    = time_point<tai_clock, _Duration>;
+using tai_seconds = tai_time<seconds>;
+
+// [time.clock.tai.overview]/1
+//    The clock tai_clock measures seconds since 1958-01-01 00:00:00 and is
+//    offset 10s ahead of UTC at this date. That is, 1958-01-01 00:00:00 TAI is
+//    equivalent to 1957-12-31 23:59:50 UTC. Leap seconds are not inserted into
+//    TAI. Therefore every time a leap second is inserted into UTC, UTC shifts
+//    another second with respect to TAI. For example by 2000-01-01 there had
+//    been 22 positive and 0 negative leap seconds inserted so 2000-01-01
+//    00:00:00 UTC is equivalent to 2000-01-01 00:00:32 TAI (22s plus the
+//    initial 10s offset).
+//
+// Note this does not specify what the UTC offset before 1958-01-01 00:00:00
+// TAI is. However the member functions are fully specified in the standard.
+// https://koka-lang.github.io/koka/doc/std_time_utc.html contains more
+// information and references.
+class tai_clock {
+public:
+  using rep                       = utc_clock::rep;
+  using period                    = utc_clock::period;
+  using duration                  = chrono::duration<rep, period>;
+  using time_point                = chrono::time_point<tai_clock>;
+  static constexpr bool is_steady = false; // The utc_clock is not steady.
+
+  // The static difference between UTC and TAI time.
+  static constexpr chrono::seconds __offset{378691210};
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static time_point now() { return from_utc(utc_clock::now()); }
+
+  template <class _Duration>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static utc_time<common_type_t<_Duration, seconds>>
+  to_utc(const tai_time<_Duration>& __time) noexcept {
+    using _Rp                    = common_type_t<_Duration, seconds>;
+    _Duration __time_since_epoch = __time.time_since_epoch();
+    _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(__time_since_epoch >= utc_time<_Rp>::min().time_since_epoch() + __offset,
+                                          "the TAI to UTC conversion would underflow");
+
+    return utc_time<_Rp>{__time_since_epoch - __offset};
+  }
+
+  template <class _Duration>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static tai_time<common_type_t<_Duration, seconds>>
+  from_utc(const utc_time<_Duration>& __time) noexcept {
+    using _Rp                    = common_type_t<_Duration, seconds>;
+    _Duration __time_since_epoch = __time.time_since_epoch();
+    _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(__time_since_epoch <= utc_time<_Rp>::max().time_since_epoch() - __offset,
+                                          "the UTC to TAI conversion would overflow");
+
+    return tai_time<_Rp>{__time_since_epoch + __offset};
+  }
+};
+
+} // namespace chrono
+
+#  endif // _LIBCPP_STD_VER >= 20 && _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM &&
+         // _LIBCPP_HAS_LOCALIZATION
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_HAS_EXPERIMENTAL_TZDB
+
+#endif // _LIBCPP___CHRONO_TAI_CLOCK_H
diff --git a/libcxx/include/chrono b/libcxx/include/chrono
index 10695eea649fb7..bd4c98600440c4 100644
--- a/libcxx/include/chrono
+++ b/libcxx/include/chrono
@@ -335,6 +335,34 @@ struct leap_second_info {                               // C++20
 template<class Duration>                                // C++20
   leap_second_info get_leap_second_info(const utc_time<Duration>& ut);
 
+
+// [time.clock.tai], class tai_clock
+class tai_clock {                                      // C++20
+public:
+    using rep                       = a signed arithmetic type;
+    using period                    = ratio<unspecified, unspecified>;
+    using duration                  = chrono::duration<rep, period>;
+    using time_point                = chrono::time_point<tai_clock>;
+    static constexpr bool is_steady = unspecified;
+
+    static time_point now();
+
+    template<class Duration>
+      static utc_time<common_type_t<Duration, seconds>>
+        to_utc(const tai_time<Duration>& t);
+    template<class Duration>
+      static tai_time<common_type_t<Duration, seconds>>
+        from_utc(const utc_time<Duration>& t);
+};
+
+template<class Duration>
+using tai_time  = time_point<tai_clock, Duration>;      // C++20
+using tai_seconds = tai_time<seconds>;                  // C++20
+
+template<class charT, class traits, class Duration>     // C++20
+  basic_ostream<charT, traits>&
+    operator<<(basic_ostream<charT, traits>& os, const tai_time<Duration>& t);
+
 class file_clock                                        // C++20
 {
 public:
@@ -898,6 +926,8 @@ namespace std {
     struct formatter<chrono::sys_time<Duration>, charT>;                          // C++20
   template<class Duration, class charT>
     struct formatter<chrono::utc_time<Duration>, charT>;                          // C++20
+  template<class Duration, class charT>
+    struct formatter<chrono::tai_time<Duration>, charT>;                          // C++20
   template<class Duration, class charT>
     struct formatter<chrono::filetime<Duration>, charT>;                          // C++20
   template<class Duration, class charT>
@@ -1014,6 +1044,7 @@ constexpr chrono::year                                  operator ""y(unsigned lo
 
 #    if _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION
 #      include <__chrono/leap_second.h>
+#      include <__chrono/tai_clock.h>
 #      include <__chrono/time_zone.h>
 #      include <__chrono/time_zone_link.h>
 #      include <__chrono/tzdb.h>
diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index 4bae02137b37b2..fd39c946b992a4 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -967,6 +967,10 @@ module std [system] {
       header "__chrono/system_clock.h"
       export std.chrono.time_point
     }
+    module tai_clock {
+      header "__chrono/tai_clock.h"
+      export std.chrono.time_point
+    }
     module time_point                 { header "__chrono/time_point.h" }
     module time_zone_link             { header "__chrono/time_zone_link.h" }
     module time_zone                  { header "__chrono/time_zone.h" }
diff --git a/libcxx/modules/std/chrono.inc b/libcxx/modules/std/chrono.inc
index 98f14f716c2078..43e8da36e90448 100644
--- a/libcxx/modules/std/chrono.inc
+++ b/libcxx/modules/std/chrono.inc
@@ -97,13 +97,13 @@ export namespace std {
 
     using std::chrono::get_leap_second_info;
 
-#    if 0
     // [time.clock.tai], class tai_clock
     using std::chrono::tai_clock;
 
     using std::chrono::tai_seconds;
     using std::chrono::tai_time;
 
+#    if 0
     // [time.clock.gps], class gps_clock
     using std::chrono::gps_clock;
 
diff --git a/libcxx/test/libcxx/diagnostics/chrono.nodiscard.verify.cpp b/libcxx/test/libcxx/diagnostics/chrono.nodiscard.verify.cpp
index 644c5b598c018d..bb40e0cfc4e1b8 100644
--- a/libcxx/test/libcxx/diagnostics/chrono.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/diagnostics/chrono.nodiscard.verify.cpp
@@ -102,4 +102,15 @@ void test(std::chrono::time_zone tz, std::chrono::time_zone_link link, std::chro
     zt.get_sys_time();   // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
     zt.get_info();       // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   }
+
+  { // [time.clock.tai]
+    // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::chrono::tai_clock::now();
+
+    // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::chrono::tai_clock::to_utc(std::chrono::tai_seconds{});
+
+    // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::chrono::tai_clock::from_utc(std::chrono::utc_seconds{});
+  }
 }
diff --git a/libcxx/test/std/time/time.clock/time.clock.tai/tai_time.ostream.pass.cpp b/libcxx/test/std/time/time.clock/time.clock.tai/tai_time.ostream.pass.cpp
new file mode 100644
index 00000000000000..3508ceb8b2d3f6
--- /dev/null
+++ b/libcxx/test/std/time/time.clock/time.clock.tai/tai_time.ostream.pass.cpp
@@ -0,0 +1,164 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
+// 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
+// XFAIL: availability-tzdb-missing
+
+// REQUIRES: locale.fr_FR.UTF-8
+// REQUIRES: locale.ja_JP.UTF-8
+
+// <chrono>
+
+// class taitem_clock;
+
+// template<class charT, class traits, class Duration>
+//   basic_ostream<charT, traits>&
+//     operator<<(basic_ostream<charT, traits>& os, const tai_time<Duration>& tp);
+
+#include <chrono>
+#include <cassert>
+#include <ratio>
+#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, class Duration>
+static std::basic_string<CharT> stream_c_locale(std::chrono::tai_time<Duration> time_point) {
+  std::basic_stringstream<CharT> sstr;
+  sstr << std::fixed << time_point;
+  return sstr.str();
+}
+
+template <class CharT, class Duration>
+static std::basic_string<CharT> stream_fr_FR_locale(std::chrono::tai_time<Duration> time_point) {
+  std::basic_stringstream<CharT> sstr;
+  const std::locale locale(LOCALE_fr_FR_UTF_8);
+  sstr.imbue(locale);
+  sstr << std::fixed << time_point;
+  return sstr.str();
+}
+
+template <class CharT, class Duration>
+static std::basic_string<CharT> stream_ja_JP_locale(std::chrono::tai_time<Duration> time_point) {
+  std::basic_stringstream<CharT> sstr;
+  const std::locale locale(LOCALE_ja_JP_UTF_8);
+  sstr.imbue(locale);
+  sstr << std::fixed << time_point;
+  return sstr.str();
+}
+
+template <class CharT>
+static void test_c() {
+  using namespace std::literals::chrono_literals;
+  namespace cr = std::chrono;
+
+  assert(stream_c_locale<CharT>(cr::tai_time<cr::nanoseconds>{946'688'523'123'456'789ns}) ==
+         SV("1988-01-01 01:02:03.123456789"));
+  assert(stream_c_locale<CharT>(cr::tai_time<cr::microseconds>{946'688'523'123'456us}) ==
+         SV("1988-01-01 01:02:03.123456"));
+
+  assert(stream_c_locale<CharT>(cr::tai_time<cr::milliseconds>{946'684'822'123ms}) == SV("1988-01-01 00:00:22.123"));
+  assert(stream_c_locale<CharT>(cr::tai_seconds{1'234'567'890s}) == SV("1997-02-13 23:31:30"));
+  assert(stream_c_locale<CharT>(cr::tai_time<cr::minutes>{20'576'131min}) == SV("1997-02-13 23:31:00"));
+  assert(stream_c_locale<CharT>(cr::tai_time<cr::hours>{342'935h}) == SV("1997-02-13 23:00:00"));
+
+  assert(stream_c_locale<CharT>(cr::tai_time<cr::duration<signed char, std::ratio<2, 1>>>{
+             cr::duration<signed char, std::ratio<2, 1>>{60}}) == SV("1958-01-01 00:02:00"));
+  assert(stream_c_locale<CharT>(cr::tai_time<cr::duration<short, std::ratio<1, 2>>>{
+             cr::duration<short, std::ratio<1, 2>>{3600}}) == SV("1958-01-01 00:30:00.0"));
+  assert(stream_c_locale<CharT>(cr::tai_time<cr::duration<int, std::ratio<1, 4>>>{
+             cr::duration<int, std::ratio<1, 4>>{3600}}) == SV("1958-01-01 00:15:00.00"));
+  assert(stream_c_locale<CharT>(cr::tai_time<cr::duration<long, std::ratio<1, 10>>>{
+             cr::duration<long, std::ratio<1, 10>>{36611}}) == SV("1958-01-01 01:01:01.1"));
+  assert(stream_c_locale<CharT>(cr::tai_time<cr::duration<long long, std::ratio<1, 100>>>{
+             cr::duration<long long, std::ratio<1, 100>>{12'345'678'9010}}) == SV("1997-02-13 23:31:30.10"));
+}
+
+template <class CharT>
+static void test_fr_FR() {
+  using namespace std::literals::chrono_literals;
+  namespace cr = std::chrono;
+
+  assert(stream_fr_FR_locale<CharT>(cr::tai_time<cr::nanoseconds>{946'688'523'123'456'789ns}) ==
+         SV("1988-01-01 01:02:03,123456789"));
+  assert(stream_fr_FR_locale<CharT>(cr::tai_time<cr::microseconds>{946'688'523'123'456us}) ==
+         SV("1988-01-01 01:02:03,123456"));
+
+  assert(stream_fr_FR_locale<CharT>(cr::tai_time<cr::milliseconds>{946'684'822'123ms}) ==
+         SV("1988-01-01 00:00:22,123"));
+  assert(str...
[truncated]

Copy link
Member

@ldionne ldionne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This LGTM with minor comments applied.

@@ -11,6 +11,7 @@

#include <cstdio>
#include <string>
#include <source_location>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is C++20 only, this might require a TEST_STD_VER check.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually the file already is mostly C++20, so the check is already there.

//===----------------------------------------------------------------------===//

// REQUIRES: std-at-least-c++20
// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It probably makes sense to only use UNSUPPORTED: no-tzdb here. The tzdb feature should probably depend on the filesystem feature.

@mordante mordante force-pushed the users/mordante/tai_clock branch from e3c4749 to 0858a05 Compare February 4, 2025 20:42
@mordante mordante merged commit aca829d into main Feb 6, 2025
82 checks passed
@mordante mordante deleted the users/mordante/tai_clock branch February 6, 2025 16:55
Icohedron pushed a commit to Icohedron/llvm-project that referenced this pull request Feb 11, 2025
Implements parts of:
- P0355 Extending <chrono> to Calendars and Time Zones
- P1361 Integration of chrono with text formatting
- LWG3359 <chrono> leap second support should allow for negative leap
seconds
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants