[libcxx-commits] [libcxx] 7f5d130 - [libc++][chrono] Add hh_mm_ss formatter.
Mark de Wever via libcxx-commits
libcxx-commits at lists.llvm.org
Tue Feb 14 10:12:24 PST 2023
Author: Mark de Wever
Date: 2023-02-14T19:12:19+01:00
New Revision: 7f5d130a428f3d605f8a38d48f7ed7d8f6087ee2
URL: https://github.com/llvm/llvm-project/commit/7f5d130a428f3d605f8a38d48f7ed7d8f6087ee2
DIFF: https://github.com/llvm/llvm-project/commit/7f5d130a428f3d605f8a38d48f7ed7d8f6087ee2.diff
LOG: [libc++][chrono] Add hh_mm_ss formatter.
Partially implements:
- P1361 Integration of chrono with text formatting
- P2372 Fixing locale handling in chrono formatters
- P1466 Miscellaneous minor fixes for chrono
Depends on D137022
Reviewed By: ldionne, #libc
Differential Revision: https://reviews.llvm.org/D139771
Added:
libcxx/include/__chrono/concepts.h
libcxx/test/libcxx/time/convert_to_tm.pass.cpp
libcxx/test/std/time/time.hms/time.hms.nonmembers/ostream.pass.cpp
libcxx/test/std/time/time.syn/formatter.hh_mm_ss.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/hh_mm_ss.h
libcxx/include/__chrono/ostream.h
libcxx/include/__chrono/parser_std_format_spec.h
libcxx/include/__format/parser_std_format_spec.h
libcxx/include/chrono
libcxx/include/module.modulemap.in
libcxx/test/libcxx/private_headers.verify.cpp
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 8b91cfdfb9d14..d2f9fdf6c8efe 100644
--- a/libcxx/docs/Status/FormatPaper.csv
+++ b/libcxx/docs/Status/FormatPaper.csv
@@ -23,7 +23,7 @@ Section,Description,Dependencies,Assignee,Status,First released version
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::year_month_day_last``",,Mark de Wever,|Complete|, Clang 16
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::year_month_weekday``",,Mark de Wever,|Complete|, Clang 16
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::year_month_weekday_last``",,Mark de Wever,|Complete|, Clang 16
-`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::hh_mm_ss<duration<Rep, Period>>``",,Mark de Wever,|In Progress|,
+`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::hh_mm_ss<duration<Rep, Period>>``",,Mark de Wever,|Complete|, Clang 17
`[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::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/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 0388df32c763c..25904fe25b54e 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -216,6 +216,7 @@ set(files
__charconv/to_chars_base_10.h
__charconv/to_chars_result.h
__chrono/calendar.h
+ __chrono/concepts.h
__chrono/convert_to_timespec.h
__chrono/convert_to_tm.h
__chrono/day.h
diff --git a/libcxx/include/__chrono/concepts.h b/libcxx/include/__chrono/concepts.h
new file mode 100644
index 0000000000000..d7502ef9f84dc
--- /dev/null
+++ b/libcxx/include/__chrono/concepts.h
@@ -0,0 +1,32 @@
+// -*- 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_CONCEPTS_H
+#define _LIBCPP___CHRONO_CONCEPTS_H
+
+#include <__chrono/hh_mm_ss.h>
+#include <__config>
+#include <__type_traits/is_specialization.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER > 17
+
+template <class _Tp>
+concept __is_hh_mm_ss = __is_specialization_v<_Tp, chrono::hh_mm_ss>;
+
+#endif // _LIBCPP_STD_VER > 17
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___CHRONO_CONCEPTS_H
diff --git a/libcxx/include/__chrono/convert_to_tm.h b/libcxx/include/__chrono/convert_to_tm.h
index 36846b3f71404..f5cfde07b5e0b 100644
--- a/libcxx/include/__chrono/convert_to_tm.h
+++ b/libcxx/include/__chrono/convert_to_tm.h
@@ -10,6 +10,7 @@
#ifndef _LIBCPP___CHRONO_CONVERT_TO_TM_H
#define _LIBCPP___CHRONO_CONVERT_TO_TM_H
+#include <__chrono/concepts.h>
#include <__chrono/day.h>
#include <__chrono/duration.h>
#include <__chrono/hh_mm_ss.h>
@@ -26,14 +27,19 @@
#include <__chrono/year_month_weekday.h>
#include <__concepts/same_as.h>
#include <__config>
+#include <__format/format_error.h>
#include <__memory/addressof.h>
#include <cstdint>
#include <ctime>
+#include <limits>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
#endif
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
_LIBCPP_BEGIN_NAMESPACE_STD
#if _LIBCPP_STD_VER > 17
@@ -114,6 +120,16 @@ _LIBCPP_HIDE_FROM_ABI _Tm __convert_to_tm(const _ChronoT& __value) {
} else if constexpr (same_as<_ChronoT, chrono::year_month_weekday> ||
same_as<_ChronoT, chrono::year_month_weekday_last>) {
return std::__convert_to_tm<_Tm>(chrono::year_month_day{static_cast<chrono::sys_days>(__value)}, __value.weekday());
+ } else if constexpr (__is_hh_mm_ss<_ChronoT>) {
+ __result.tm_sec = __value.seconds().count();
+ __result.tm_min = __value.minutes().count();
+ // In libc++ hours is stored as a long. The type in std::tm is an int. So
+ // the overflow can only occur when hour uses more bits than an int
+ // provides.
+ if constexpr (sizeof(std::chrono::hours::rep) > sizeof(__result.tm_hour))
+ 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();
} else
static_assert(sizeof(_ChronoT) == 0, "Add the missing type specialization");
@@ -124,4 +140,6 @@ _LIBCPP_HIDE_FROM_ABI _Tm __convert_to_tm(const _ChronoT& __value) {
_LIBCPP_END_NAMESPACE_STD
+_LIBCPP_POP_MACROS
+
#endif // _LIBCPP___CHRONO_CONVERT_TO_TM_H
diff --git a/libcxx/include/__chrono/formatter.h b/libcxx/include/__chrono/formatter.h
index f3748bc730528..cc2d7da27fb2d 100644
--- a/libcxx/include/__chrono/formatter.h
+++ b/libcxx/include/__chrono/formatter.h
@@ -11,6 +11,7 @@
#define _LIBCPP___CHRONO_FORMATTER_H
#include <__chrono/calendar.h>
+#include <__chrono/concepts.h>
#include <__chrono/convert_to_tm.h>
#include <__chrono/day.h>
#include <__chrono/duration.h>
@@ -75,13 +76,15 @@ namespace __formatter {
// For tiny ratios it's not possible to convert a duration to a hh_mm_ss. This
// fails compile-time due to the limited precision of the ratio (64-bit is too
// small). Therefore a duration uses its own conversion.
-template <class _CharT, class _Tp>
- requires(chrono::__is_duration<_Tp>::value)
-_LIBCPP_HIDE_FROM_ABI void __format_sub_seconds(const _Tp& __value, basic_stringstream<_CharT>& __sstr) {
+template <class _CharT, class _Rep, class _Period>
+_LIBCPP_HIDE_FROM_ABI void
+__format_sub_seconds(const chrono::duration<_Rep, _Period>& __value, basic_stringstream<_CharT>& __sstr) {
__sstr << std::use_facet<numpunct<_CharT>>(__sstr.getloc()).decimal_point();
+ using __duration = chrono::duration<_Rep, _Period>;
+
auto __fraction = __value - chrono::duration_cast<chrono::seconds>(__value);
- if constexpr (chrono::treat_as_floating_point_v<typename _Tp::rep>)
+ if constexpr (chrono::treat_as_floating_point_v<_Rep>)
// When the floating-point value has digits itself they are ignored based
// on the wording in [tab:time.format.spec]
// If the precision of the input cannot be exactly represented with
@@ -97,18 +100,36 @@ _LIBCPP_HIDE_FROM_ABI void __format_sub_seconds(const _Tp& __value, basic_string
std::format_to(std::ostreambuf_iterator<_CharT>{__sstr},
_LIBCPP_STATICALLY_WIDEN(_CharT, "{:0{}.0f}"),
__fraction.count(),
- chrono::hh_mm_ss<_Tp>::fractional_width);
+ chrono::hh_mm_ss<__duration>::fractional_width);
else
std::format_to(std::ostreambuf_iterator<_CharT>{__sstr},
_LIBCPP_STATICALLY_WIDEN(_CharT, "{:0{}}"),
__fraction.count(),
- chrono::hh_mm_ss<_Tp>::fractional_width);
+ chrono::hh_mm_ss<__duration>::fractional_width);
+}
+
+template <class _CharT, class _Duration>
+_LIBCPP_HIDE_FROM_ABI void
+__format_sub_seconds(const chrono::hh_mm_ss<_Duration>& __value, basic_stringstream<_CharT>& __sstr) {
+ __sstr << std::use_facet<numpunct<_CharT>>(__sstr.getloc()).decimal_point();
+ if constexpr (chrono::treat_as_floating_point_v<typename _Duration::rep>)
+ std::format_to(std::ostreambuf_iterator<_CharT>{__sstr},
+ _LIBCPP_STATICALLY_WIDEN(_CharT, "{:0{}.0f}"),
+ __value.subseconds().count(),
+ __value.fractional_width);
+ else
+ std::format_to(std::ostreambuf_iterator<_CharT>{__sstr},
+ _LIBCPP_STATICALLY_WIDEN(_CharT, "{:0{}}"),
+ __value.subseconds().count(),
+ __value.fractional_width);
}
template <class _Tp>
consteval bool __use_fraction() {
if constexpr (chrono::__is_duration<_Tp>::value)
return chrono::hh_mm_ss<_Tp>::fractional_width;
+ else if constexpr (__is_hh_mm_ss<_Tp>)
+ return _Tp::fractional_width;
else
return false;
}
@@ -322,6 +343,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool __weekday_ok(const _Tp& __value) {
return __value.weekday().ok();
else if constexpr (same_as<_Tp, chrono::year_month_weekday_last>)
return __value.weekday().ok();
+ else if constexpr (__is_hh_mm_ss<_Tp>)
+ return true;
else
static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
}
@@ -358,6 +381,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool __weekday_name_ok(const _Tp& __value) {
return __value.weekday().ok();
else if constexpr (same_as<_Tp, chrono::year_month_weekday_last>)
return __value.weekday().ok();
+ else if constexpr (__is_hh_mm_ss<_Tp>)
+ return true;
else
static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
}
@@ -394,6 +419,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool __date_ok(const _Tp& __value) {
return __value.ok();
else if constexpr (same_as<_Tp, chrono::year_month_weekday_last>)
return __value.ok();
+ else if constexpr (__is_hh_mm_ss<_Tp>)
+ return true;
else
static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
}
@@ -430,6 +457,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool __month_name_ok(const _Tp& __value) {
return __value.month().ok();
else if constexpr (same_as<_Tp, chrono::year_month_weekday_last>)
return __value.month().ok();
+ else if constexpr (__is_hh_mm_ss<_Tp>)
+ return true;
else
static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
}
@@ -478,6 +507,29 @@ __format_chrono(const _Tp& __value,
if (__specs.__chrono_.__month_name_ && !__formatter::__month_name_ok(__value))
std::__throw_format_error("formatting a month name from an invalid month number");
+ if constexpr (__is_hh_mm_ss<_Tp>) {
+ // Note this is a pedantic intepretation of the Standard. A hh_mm_ss
+ // is no longer a time_of_day and can store an arbitrary number of
+ // hours. A number of hours in a 12 or 24 hour clock can't represent
+ // 24 hours or more. The functions std::chrono::make12 and
+ // std::chrono::make24 reaffirm this view point.
+ //
+ // Interestingly this will be the only output stream function that
+ // throws.
+ //
+ // TODO FMT The wording probably needs to be adapted to
+ // - The displayed hours is hh_mm_ss.hours() % 24
+ // - It should probably allow %j in the same fashion as duration.
+ // - The stream formatter should change its output when hours >= 24
+ // - Write it as not valid,
+ // - or write the number of days.
+ if (__specs.__chrono_.__hour_ && __value.hours().count() > 23)
+ std::__throw_format_error("formatting a hour needs a valid value");
+
+ if (__value.is_negative())
+ __sstr << _CharT('-');
+ }
+
__formatter::__format_chrono_using_chrono_specs(__value, __sstr, __chrono_specs);
}
}
@@ -709,6 +761,16 @@ struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter<chrono::year_m
}
};
+template <class _Duration, __fmt_char_type _CharT>
+struct formatter<chrono::hh_mm_ss<_Duration>, _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::__time);
+ }
+};
#endif // if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_FORMAT)
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__chrono/hh_mm_ss.h b/libcxx/include/__chrono/hh_mm_ss.h
index fd61cbe8f8453..a8c8c84acc249 100644
--- a/libcxx/include/__chrono/hh_mm_ss.h
+++ b/libcxx/include/__chrono/hh_mm_ss.h
@@ -85,6 +85,7 @@ class hh_mm_ss
chrono::seconds __s_;
precision __f_;
};
+_LIBCPP_CTAD_SUPPORTED_FOR_TYPE(hh_mm_ss);
_LIBCPP_HIDE_FROM_ABI constexpr bool is_am(const hours& __h) noexcept { return __h >= hours( 0) && __h < hours(12); }
_LIBCPP_HIDE_FROM_ABI constexpr bool is_pm(const hours& __h) noexcept { return __h >= hours(12) && __h < hours(24); }
diff --git a/libcxx/include/__chrono/ostream.h b/libcxx/include/__chrono/ostream.h
index 30a04bd2658b0..4af4476fa63d9 100644
--- a/libcxx/include/__chrono/ostream.h
+++ b/libcxx/include/__chrono/ostream.h
@@ -12,6 +12,7 @@
#include <__chrono/day.h>
#include <__chrono/duration.h>
+#include <__chrono/hh_mm_ss.h>
#include <__chrono/month.h>
#include <__chrono/month_weekday.h>
#include <__chrono/monthday.h>
@@ -229,6 +230,12 @@ operator<<(basic_ostream<_CharT, _Traits>& __os, const year_month_weekday_last&
__ymwdl.weekday_last());
}
+template <class _CharT, class _Traits, class _Duration>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT basic_ostream<_CharT, _Traits>&
+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);
+}
+
} // namespace chrono
#endif //if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_FORMAT)
diff --git a/libcxx/include/__chrono/parser_std_format_spec.h b/libcxx/include/__chrono/parser_std_format_spec.h
index f4f2b079b6bb1..d30a44c64e551 100644
--- a/libcxx/include/__chrono/parser_std_format_spec.h
+++ b/libcxx/include/__chrono/parser_std_format_spec.h
@@ -214,6 +214,7 @@ class _LIBCPP_TEMPLATE_VIS __parser_chrono {
case _CharT('p'): // TODO FMT does the formater require an hour or a time?
case _CharT('H'):
case _CharT('I'):
+ __parser_.__hour_ = true;
__validate_hour(__flags);
break;
@@ -221,6 +222,7 @@ class _LIBCPP_TEMPLATE_VIS __parser_chrono {
case _CharT('R'):
case _CharT('T'):
case _CharT('X'):
+ __parser_.__hour_ = true;
__format_spec::__validate_time(__flags);
break;
@@ -313,6 +315,7 @@ class _LIBCPP_TEMPLATE_VIS __parser_chrono {
switch (*__begin) {
case _CharT('X'):
+ __parser_.__hour_ = true;
__format_spec::__validate_time(__flags);
break;
@@ -361,6 +364,7 @@ class _LIBCPP_TEMPLATE_VIS __parser_chrono {
case _CharT('I'):
case _CharT('H'):
+ __parser_.__hour_ = true;
__format_spec::__validate_hour(__flags);
break;
diff --git a/libcxx/include/__format/parser_std_format_spec.h b/libcxx/include/__format/parser_std_format_spec.h
index f70a8258ce655..adbc5fc7c7b1c 100644
--- a/libcxx/include/__format/parser_std_format_spec.h
+++ b/libcxx/include/__format/parser_std_format_spec.h
@@ -199,6 +199,7 @@ struct __std {
struct __chrono {
__alignment __alignment_ : 3;
bool __locale_specific_form_ : 1;
+ bool __hour_ : 1;
bool __weekday_name_ : 1;
bool __weekday_ : 1;
bool __day_of_year_ : 1;
@@ -329,6 +330,7 @@ class _LIBCPP_TEMPLATE_VIS __parser {
.__chrono_ =
__chrono{.__alignment_ = __alignment_,
.__locale_specific_form_ = __locale_specific_form_,
+ .__hour_ = __hour_,
.__weekday_name_ = __weekday_name_,
.__weekday_ = __weekday_,
.__day_of_year_ = __day_of_year_,
@@ -348,6 +350,8 @@ class _LIBCPP_TEMPLATE_VIS __parser {
// These flags are only used for formatting chrono. Since the struct has
// padding space left it's added to this structure.
+ bool __hour_ : 1 {false};
+
bool __weekday_name_ : 1 {false};
bool __weekday_ : 1 {false};
@@ -356,7 +360,7 @@ class _LIBCPP_TEMPLATE_VIS __parser {
bool __month_name_ : 1 {false};
- uint8_t __reserved_1_ : 3 {0};
+ uint8_t __reserved_1_ : 2 {0};
uint8_t __reserved_2_ : 6 {0};
// These two flags are only used internally and not part of the
// __parsed_specifications. Therefore put them at the end.
diff --git a/libcxx/include/chrono b/libcxx/include/chrono
index 44073b557ba12..0d1c5bb875044 100644
--- a/libcxx/include/chrono
+++ b/libcxx/include/chrono
@@ -656,6 +656,10 @@ public:
constexpr precision to_duration() const noexcept;
};
+template<class charT, class traits, class Duration>
+ basic_ostream<charT, traits>&
+ operator<<(basic_ostream<charT, traits>& os, const hh_mm_ss<Duration>& hms); // C++20
+
// 26.10, 12/24 hour functions
constexpr bool is_am(hours const& h) noexcept;
constexpr bool is_pm(hours const& h) noexcept;
@@ -691,6 +695,8 @@ namespace std {
template<class charT> struct formatter<chrono::year_month_day_last, charT>; // C++20
template<class charT> struct formatter<chrono::year_month_weekday, charT>; // C++20
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
} // namespace std
namespace chrono {
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 53bfd1a11a2d4..b601ef0671669 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -674,6 +674,7 @@ module std [system] {
module __chrono {
module calendar { private header "__chrono/calendar.h" }
+ module concepts { private header "__chrono/concepts.h" }
module convert_to_timespec { private header "__chrono/convert_to_timespec.h" }
module convert_to_tm { private header "__chrono/convert_to_tm.h" }
module day { private header "__chrono/day.h" }
diff --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp
index d94bb185ed6a9..49c0d1a81a765 100644
--- a/libcxx/test/libcxx/private_headers.verify.cpp
+++ b/libcxx/test/libcxx/private_headers.verify.cpp
@@ -250,6 +250,7 @@ END-SCRIPT
#include <__charconv/to_chars_base_10.h> // expected-error@*:* {{use of private header from outside its module: '__charconv/to_chars_base_10.h'}}
#include <__charconv/to_chars_result.h> // expected-error@*:* {{use of private header from outside its module: '__charconv/to_chars_result.h'}}
#include <__chrono/calendar.h> // expected-error@*:* {{use of private header from outside its module: '__chrono/calendar.h'}}
+#include <__chrono/concepts.h> // expected-error@*:* {{use of private header from outside its module: '__chrono/concepts.h'}}
#include <__chrono/convert_to_timespec.h> // expected-error@*:* {{use of private header from outside its module: '__chrono/convert_to_timespec.h'}}
#include <__chrono/convert_to_tm.h> // expected-error@*:* {{use of private header from outside its module: '__chrono/convert_to_tm.h'}}
#include <__chrono/day.h> // expected-error@*:* {{use of private header from outside its module: '__chrono/day.h'}}
diff --git a/libcxx/test/libcxx/time/convert_to_tm.pass.cpp b/libcxx/test/libcxx/time/convert_to_tm.pass.cpp
new file mode 100644
index 0000000000000..9e52b5b869f66
--- /dev/null
+++ b/libcxx/test/libcxx/time/convert_to_tm.pass.cpp
@@ -0,0 +1,62 @@
+//===----------------------------------------------------------------------===//
+// 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: libcpp-has-no-incomplete-format
+
+// <chrono>
+
+// template <class _Tm, class _ChronoT>
+// _LIBCPP_HIDE_FROM_ABI _Tm __convert_to_tm(const _ChronoT& __value)
+
+// Most of the code is tested indirectly in the chrono formatters. This only
+// tests the hour overflow.
+
+#include <chrono>
+#include <cassert>
+#include <format>
+#include <string_view>
+
+#include "test_macros.h"
+
+// libc++ uses a long as representation in std::chrono::hours.
+// std::tm uses an int for its integral members. The overflow in the hour
+// conversion can only occur on platforms where sizeof(long) > sizeof(int).
+// Instead emulate this error by using a "tm" with shorts.
+// (The function is already templated to this is quite easy to do,)
+struct minimal_short_tm {
+ short tm_sec;
+ short tm_min;
+ short tm_hour;
+ const char* tm_zone;
+};
+
+int main(int, char**) {
+ { // Test with the maximum number of hours that fit in a short.
+ std::chrono::hh_mm_ss time{std::chrono::hours{32767}};
+ minimal_short_tm result = std::__convert_to_tm<minimal_short_tm>(time);
+ assert(result.tm_sec == 0);
+ assert(result.tm_min == 0);
+ assert(result.tm_hour == 32767);
+ }
+
+#ifndef TEST_HAS_NO_EXCEPTIONS
+ { // Test above the maximum number of hours that fit in a short.
+ std::chrono::hh_mm_ss time{std::chrono::hours{32768}};
+ try {
+ TEST_IGNORE_NODISCARD std::__convert_to_tm<minimal_short_tm>(time);
+ assert(false);
+ } catch ([[maybe_unused]] const std::format_error& e) {
+ LIBCPP_ASSERT(e.what() == std::string_view("Formatting hh_mm_ss, encountered an hour overflow"));
+ return 0;
+ }
+ assert(false);
+ }
+#endif // TEST_HAS_NO_EXCEPTIONS
+
+ return 0;
+}
diff --git a/libcxx/test/std/time/time.hms/time.hms.nonmembers/ostream.pass.cpp b/libcxx/test/std/time/time.hms/time.hms.nonmembers/ostream.pass.cpp
new file mode 100644
index 0000000000000..76d4b27cd7b0b
--- /dev/null
+++ b/libcxx/test/std/time/time.hms/time.hms.nonmembers/ostream.pass.cpp
@@ -0,0 +1,160 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 Evaluate gcc-12 status
+// UNSUPPORTED: gcc-12
+
+// TODO FMT Investigate Windows issues.
+// UNSUPPORTED: msvc, target={{.+}}-windows-gnu
+
+// REQUIRES: locale.fr_FR.UTF-8
+// REQUIRES: locale.ja_JP.UTF-8
+
+// <chrono>
+
+// class hh_mm_ss;
+
+// template<class charT, class traits, class Duration>
+// basic_ostream<charT, traits>&
+// operator<<(basic_ostream<charT, traits>& os, const hh_mm_ss<Duration>& hms);
+
+#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, class Duration>
+static std::basic_string<CharT> stream_c_locale(std::chrono::hh_mm_ss<Duration> hms) {
+ std::basic_stringstream<CharT> sstr;
+ sstr << hms;
+ return sstr.str();
+}
+
+template <class CharT, class Duration>
+static std::basic_string<CharT> stream_fr_FR_locale(std::chrono::hh_mm_ss<Duration> hms) {
+ std::basic_stringstream<CharT> sstr;
+ const std::locale locale(LOCALE_fr_FR_UTF_8);
+ sstr.imbue(locale);
+ sstr << hms;
+ return sstr.str();
+}
+
+template <class CharT, class Duration>
+static std::basic_string<CharT> stream_ja_JP_locale(std::chrono::hh_mm_ss<Duration> hms) {
+ std::basic_stringstream<CharT> sstr;
+ const std::locale locale(LOCALE_ja_JP_UTF_8);
+ sstr.imbue(locale);
+ sstr << hms;
+ return sstr.str();
+}
+
+template <class CharT>
+static void test() {
+ // Note std::atto can't be tested since the ratio conversion from std::atto
+ // std::chrono::seconds to std::chrono::hours overflows when intmax_t is a
+ // 64-bit type. This is a limitiation in the constructor of
+ // std::chrono::hh_mm_ss.
+
+ // C locale - integral power of 10 ratios
+ assert(stream_c_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<intmax_t, std::femto>{1'234'567'890}}) ==
+ SV("00:00:00.000001234567890"));
+ assert(stream_c_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<intmax_t, std::pico>{1'234'567'890}}) ==
+ SV("00:00:00.001234567890"));
+ assert(stream_c_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<intmax_t, std::nano>{1'234'567'890}}) ==
+ SV("00:00:01.234567890"));
+ assert(stream_c_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<intmax_t, std::micro>{1'234'567}}) ==
+ SV("00:00:01.234567"));
+ assert(stream_c_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<intmax_t, std::milli>{123'456}}) ==
+ SV("00:02:03.456"));
+ assert(stream_c_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<intmax_t, std::centi>{12'345}}) ==
+ SV("00:02:03.45"));
+ assert(stream_c_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<intmax_t, std::deci>{1'234}}) ==
+ SV("00:02:03.4"));
+
+ assert(stream_c_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<intmax_t>{123}}) == SV("00:02:03"));
+
+ assert(stream_c_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<intmax_t, std::deca>{-366}}) ==
+ SV("-01:01:00"));
+ assert(stream_c_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<intmax_t, std::hecto>{-72}}) ==
+ SV("-02:00:00"));
+ assert(stream_c_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<intmax_t, std::kilo>{-86}}) ==
+ SV("-23:53:20"));
+
+ // Starting at mega it will pass one day
+
+ // fr_FR locale - integral power of not 10 ratios
+ assert(stream_fr_FR_locale<CharT>(std::chrono::hh_mm_ss{
+ std::chrono::duration<intmax_t, std::ratio<1, 5'000'000>>{5'000}}) == SV("00:00:00,0010000"));
+ assert(stream_fr_FR_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<intmax_t, std::ratio<1, 8'000>>{3}}) ==
+ SV("00:00:00,000375"));
+ assert(stream_fr_FR_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<intmax_t, std::ratio<1, 4'000>>{1}}) ==
+ SV("00:00:00,00025"));
+ assert(stream_fr_FR_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<intmax_t, std::ratio<1, 5'000>>{5}}) ==
+ SV("00:00:00,0010"));
+ assert(stream_fr_FR_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<intmax_t, std::ratio<1, 8>>{-4}}) ==
+ SV("-00:00:00,500"));
+ assert(stream_fr_FR_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<intmax_t, std::ratio<1, 4>>{-8}}) ==
+ SV("-00:00:02,00"));
+ assert(stream_fr_FR_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<intmax_t, std::ratio<1, 5>>{-5}}) ==
+ SV("-00:00:01,0"));
+
+ // TODO FMT Note there's no wording on the rounding
+ assert(stream_fr_FR_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<intmax_t, std::ratio<1, 9>>{5}}) ==
+ SV("00:00:00,555555"));
+ assert(stream_fr_FR_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<intmax_t, std::ratio<1, 7>>{7}}) ==
+ SV("00:00:01,000000"));
+ assert(stream_fr_FR_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<intmax_t, std::ratio<1, 6>>{1}}) ==
+ SV("00:00:00,166666"));
+ assert(stream_fr_FR_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<intmax_t, std::ratio<1, 3>>{2}}) ==
+ SV("00:00:00,666666"));
+
+ // ja_JP locale - floating points
+
+ assert(stream_c_locale<CharT>(std::chrono::hh_mm_ss{
+ std::chrono::duration<long double, std::femto>{1'234'567'890.123}}) == SV("00:00:00.000001234567890"));
+ assert(stream_c_locale<CharT>(std::chrono::hh_mm_ss{
+ std::chrono::duration<long double, std::pico>{1'234'567'890.123}}) == SV("00:00:00.001234567890"));
+ assert(stream_c_locale<CharT>(std::chrono::hh_mm_ss{
+ std::chrono::duration<long double, std::nano>{1'234'567'890.123}}) == SV("00:00:01.234567890"));
+ assert(stream_c_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<double, std::micro>{1'234'567.123}}) ==
+ SV("00:00:01.234567"));
+ assert(stream_c_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<double, std::milli>{123'456.123}}) ==
+ SV("00:02:03.456"));
+ assert(stream_c_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<double, std::centi>{12'345.123}}) ==
+ SV("00:02:03.45"));
+ assert(stream_c_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<float, std::deci>{1'234.123}}) ==
+ SV("00:02:03.4"));
+
+ assert(stream_c_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<float>{123.123}}) == SV("00:02:03"));
+
+ assert(stream_c_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<double, std::deca>{-366.5}}) ==
+ SV("-01:01:05"));
+ assert(stream_c_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<double, std::hecto>{-72.64}}) ==
+ SV("-02:01:04"));
+ assert(stream_c_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<double, std::kilo>{-86}}) ==
+ SV("-23:53:20"));
+}
+
+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.hh_mm_ss.pass.cpp b/libcxx/test/std/time/time.syn/formatter.hh_mm_ss.pass.cpp
new file mode 100644
index 0000000000000..49ab0d025c15a
--- /dev/null
+++ b/libcxx/test/std/time/time.syn/formatter.hh_mm_ss.pass.cpp
@@ -0,0 +1,556 @@
+//===----------------------------------------------------------------------===//
+// 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 Evaluate gcc-12 status
+// UNSUPPORTED: gcc-12
+
+// 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 Rep, class Period, class charT>
+// struct formatter<chrono::hh_mm_ss<duration<Rep, Period>>, 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() {
+ using namespace std::literals::chrono_literals;
+
+ std::locale::global(std::locale(LOCALE_fr_FR_UTF_8));
+
+ // Non localized output
+ check(SV("00:00:00.000"), SV("{}"), std::chrono::hh_mm_ss{0ms});
+ check(SV("*00:00:00.000*"), SV("{:*^14}"), std::chrono::hh_mm_ss{0ms});
+ check(SV("*00:00:00.000"), SV("{:*>13}"), std::chrono::hh_mm_ss{0ms});
+
+ std::locale::global(std::locale::classic());
+}
+
+template <class CharT>
+static void test_valid_values() {
+ using namespace std::literals::chrono_literals;
+
+ constexpr std::basic_string_view<CharT> fmt = SV(
+ "{:"
+ "%%H='%H'%t"
+ "%%OH='%OH'%t"
+ "%%I='%I'%t"
+ "%%OI='%OI'%t"
+ "%%M='%M'%t"
+ "%%OM='%OM'%t"
+ "%%S='%S'%t"
+ "%%OS='%OS'%t"
+ "%%p='%p'%t"
+ "%%R='%R'%t"
+ "%%T='%T'%t"
+ "%%r='%r'%t"
+ "%%X='%X'%t"
+ "%%EX='%EX'%t"
+ "%n}");
+ constexpr std::basic_string_view<CharT> lfmt = SV(
+ "{:L"
+ "%%H='%H'%t"
+ "%%OH='%OH'%t"
+ "%%I='%I'%t"
+ "%%OI='%OI'%t"
+ "%%M='%M'%t"
+ "%%OM='%OM'%t"
+ "%%S='%S'%t"
+ "%%OS='%OS'%t"
+ "%%p='%p'%t"
+ "%%R='%R'%t"
+ "%%T='%T'%t"
+ "%%r='%r'%t"
+ "%%X='%X'%t"
+ "%%EX='%EX'%t"
+ "%n}");
+
+ const std::locale loc(LOCALE_ja_JP_UTF_8);
+ std::locale::global(std::locale(LOCALE_fr_FR_UTF_8));
+
+ // Non localized output using C-locale
+ check(SV("%H='00'\t"
+ "%OH='00'\t"
+ "%I='12'\t"
+ "%OI='12'\t"
+ "%M='00'\t"
+ "%OM='00'\t"
+ "%S='00'\t"
+ "%OS='00'\t"
+ "%p='AM'\t"
+ "%R='00:00'\t"
+ "%T='00:00:00'\t"
+ "%r='12:00:00 AM'\t"
+ "%X='00:00:00'\t"
+ "%EX='00:00:00'\t"
+ "\n"),
+ fmt,
+ std::chrono::hh_mm_ss(0s));
+
+ check(SV("%H='23'\t"
+ "%OH='23'\t"
+ "%I='11'\t"
+ "%OI='11'\t"
+ "%M='31'\t"
+ "%OM='31'\t"
+ "%S='30.123'\t"
+ "%OS='30.123'\t"
+ "%p='PM'\t"
+ "%R='23:31'\t"
+ "%T='23:31:30.123'\t"
+ "%r='11:31:30 PM'\t"
+ "%X='23:31:30'\t"
+ "%EX='23:31:30'\t"
+ "\n"),
+ fmt,
+ std::chrono::hh_mm_ss(23h + 31min + 30s + 123ms));
+
+ check(SV("-%H='03'\t"
+ "%OH='03'\t"
+ "%I='03'\t"
+ "%OI='03'\t"
+ "%M='02'\t"
+ "%OM='02'\t"
+ "%S='01.123456789012'\t"
+ "%OS='01.123456789012'\t"
+ "%p='AM'\t"
+ "%R='03:02'\t"
+ "%T='03:02:01.123456789012'\t"
+ "%r='03:02:01 AM'\t"
+ "%X='03:02:01'\t"
+ "%EX='03:02:01'\t"
+ "\n"),
+ fmt,
+ std::chrono::hh_mm_ss(-(3h + 2min + 1s + std::chrono::duration<int64_t, std::pico>(123456789012))));
+
+ // The number of fractional seconds is 0 according to the Standard
+ // TODO FMT Determine what to do.
+ check(SV("%H='01'\t"
+ "%OH='01'\t"
+ "%I='01'\t"
+ "%OI='01'\t"
+ "%M='01'\t"
+ "%OM='01'\t"
+ "%S='01'\t"
+ "%OS='01'\t"
+ "%p='AM'\t"
+ "%R='01:01'\t"
+ "%T='01:01:01'\t"
+ "%r='01:01:01 AM'\t"
+ "%X='01:01:01'\t"
+ "%EX='01:01:01'\t"
+ "\n"),
+ fmt,
+ std::chrono::hh_mm_ss(std::chrono::duration<double>(3661.123456)));
+
+ // Use the global locale (fr_FR)
+ check(SV("%H='00'\t"
+ "%OH='00'\t"
+ "%I='12'\t"
+ "%OI='12'\t"
+ "%M='00'\t"
+ "%OM='00'\t"
+ "%S='00'\t"
+ "%OS='00'\t"
+#if defined(_AIX)
+ "%p='AM'\t"
+#else
+ "%p=''\t"
+#endif
+ "%R='00:00'\t"
+ "%T='00:00:00'\t"
+#ifdef _WIN32
+ "%r='00:00:00'\t"
+#elif defined(_AIX)
+ "%r='12:00:00 AM'\t"
+#elif defined(__APPLE__)
+ "%r=''\t"
+#else
+ "%r='12:00:00 '\t"
+#endif
+ "%X='00:00:00'\t"
+ "%EX='00:00:00'\t"
+ "\n"),
+ lfmt,
+ std::chrono::hh_mm_ss(0s));
+
+ check(SV("%H='23'\t"
+ "%OH='23'\t"
+ "%I='11'\t"
+ "%OI='11'\t"
+ "%M='31'\t"
+ "%OM='31'\t"
+ "%S='30,123'\t"
+ "%OS='30,123'\t"
+#if defined(_AIX)
+ "%p='PM'\t"
+#else
+ "%p=''\t"
+#endif
+ "%R='23:31'\t"
+ "%T='23:31:30,123'\t"
+#ifdef _WIN32
+ "%r='23:31:30'\t"
+#elif defined(_AIX)
+ "%r='11:31:30 PM'\t"
+#elif defined(__APPLE__)
+ "%r=''\t"
+#else
+ "%r='11:31:30 '\t"
+#endif
+ "%X='23:31:30'\t"
+ "%EX='23:31:30'\t"
+ "\n"),
+ lfmt,
+ std::chrono::hh_mm_ss(23h + 31min + 30s + 123ms));
+
+ check(SV("-%H='03'\t"
+ "%OH='03'\t"
+ "%I='03'\t"
+ "%OI='03'\t"
+ "%M='02'\t"
+ "%OM='02'\t"
+ "%S='01,123456789012'\t"
+ "%OS='01,123456789012'\t"
+#if defined(_AIX)
+ "%p='AM'\t"
+#else
+ "%p=''\t"
+#endif
+ "%R='03:02'\t"
+ "%T='03:02:01,123456789012'\t"
+#ifdef _WIN32
+ "%r='03:02:01'\t"
+#elif defined(_AIX)
+ "%r='03:02:01 AM'\t"
+#elif defined(__APPLE__)
+ "%r=''\t"
+#else
+ "%r='03:02:01 '\t"
+#endif
+ "%X='03:02:01'\t"
+ "%EX='03:02:01'\t"
+ "\n"),
+ lfmt,
+ std::chrono::hh_mm_ss(-(3h + 2min + 1s + std::chrono::duration<int64_t, std::pico>(123456789012))));
+
+ check(SV("%H='01'\t"
+ "%OH='01'\t"
+ "%I='01'\t"
+ "%OI='01'\t"
+ "%M='01'\t"
+ "%OM='01'\t"
+ "%S='01'\t"
+ "%OS='01'\t"
+#if defined(_AIX)
+ "%p='AM'\t"
+#else
+ "%p=''\t"
+#endif
+ "%R='01:01'\t"
+ "%T='01:01:01'\t"
+#ifdef _WIN32
+ "%r='01:01:01'\t"
+#elif defined(_AIX)
+ "%r='01:01:01 AM'\t"
+#elif defined(__APPLE__)
+ "%r=''\t"
+#else
+ "%r='01:01:01 '\t"
+#endif
+ "%X='01:01:01'\t"
+ "%EX='01:01:01'\t"
+ "\n"),
+ lfmt,
+ std::chrono::hh_mm_ss(std::chrono::duration<double>(3661.123456)));
+
+ // Use supplied locale (ja_JP). This locale has a
diff erent alternate.
+#if defined(__APPLE__) || defined(_AIX)
+ check(loc,
+ SV("%H='00'\t"
+ "%OH='00'\t"
+ "%I='12'\t"
+ "%OI='12'\t"
+ "%M='00'\t"
+ "%OM='00'\t"
+ "%S='00'\t"
+ "%OS='00'\t"
+# if defined(__APPLE__)
+ "%p='AM'\t"
+# else
+ "%p='午前'\t"
+# endif
+ "%R='00:00'\t"
+ "%T='00:00:00'\t"
+# if defined(__APPLE__)
+ "%r='12:00:00 AM'\t"
+ "%X='00時00分00秒'\t"
+ "%EX='00時00分00秒'\t"
+# else
+ "%r='午前12:00:00'\t"
+ "%X='00:00:00'\t"
+ "%EX='00:00:00'\t"
+# endif
+ "\n"),
+ lfmt,
+ std::chrono::hh_mm_ss(0s));
+
+ check(loc,
+ SV("%H='23'\t"
+ "%OH='23'\t"
+ "%I='11'\t"
+ "%OI='11'\t"
+ "%M='31'\t"
+ "%OM='31'\t"
+ "%S='30.123'\t"
+ "%OS='30.123'\t"
+# if defined(__APPLE__)
+ "%p='PM'\t"
+# else
+ "%p='午後'\t"
+# endif
+ "%R='23:31'\t"
+ "%T='23:31:30.123'\t"
+# if defined(__APPLE__)
+ "%r='11:31:30 PM'\t"
+ "%X='23時31分30秒'\t"
+ "%EX='23時31分30秒'\t"
+# else
+ "%r='午後11:31:30'\t"
+ "%X='23:31:30'\t"
+ "%EX='23:31:30'\t"
+# endif
+ "\n"),
+ lfmt,
+ std::chrono::hh_mm_ss(23h + 31min + 30s + 123ms));
+
+ check(loc,
+ SV("-%H='03'\t"
+ "%OH='03'\t"
+ "%I='03'\t"
+ "%OI='03'\t"
+ "%M='02'\t"
+ "%OM='02'\t"
+ "%S='01.123456789012'\t"
+ "%OS='01.123456789012'\t"
+# if defined(__APPLE__)
+ "%p='AM'\t"
+# else
+ "%p='午前'\t"
+# endif
+ "%R='03:02'\t"
+ "%T='03:02:01.123456789012'\t"
+# if defined(__APPLE__)
+ "%r='03:02:01 AM'\t"
+ "%X='03時02分01秒'\t"
+ "%EX='03時02分01秒'\t"
+# else
+ "%r='午前03:02:01'\t"
+ "%X='03:02:01'\t"
+ "%EX='03:02:01'\t"
+# endif
+ "\n"),
+ lfmt,
+ std::chrono::hh_mm_ss(-(3h + 2min + 1s + std::chrono::duration<int64_t, std::pico>(123456789012))));
+
+ check(loc,
+ SV("%H='01'\t"
+ "%OH='01'\t"
+ "%I='01'\t"
+ "%OI='01'\t"
+ "%M='01'\t"
+ "%OM='01'\t"
+ "%S='01'\t"
+ "%OS='01'\t"
+# if defined(__APPLE__)
+ "%p='AM'\t"
+# else
+ "%p='午前'\t"
+# endif
+ "%R='01:01'\t"
+ "%T='01:01:01'\t"
+# if defined(__APPLE__)
+ "%r='01:01:01 AM'\t"
+ "%X='01時01分01秒'\t"
+ "%EX='01時01分01秒'\t"
+# else
+ "%r='午前01:01:01'\t"
+ "%X='01:01:01'\t"
+ "%EX='01:01:01'\t"
+# endif
+ "\n"),
+ lfmt,
+ std::chrono::hh_mm_ss(std::chrono::duration<double>(3661.123456)));
+#else // defined(__APPLE__) || defined(_AIX)
+ check(loc,
+ SV("%H='00'\t"
+ "%OH='〇'\t"
+ "%I='12'\t"
+ "%OI='十二'\t"
+ "%M='00'\t"
+ "%OM='〇'\t"
+ "%S='00'\t"
+ "%OS='〇'\t"
+ "%p='午前'\t"
+ "%R='00:00'\t"
+ "%T='00:00:00'\t"
+ "%r='午前12時00分00秒'\t"
+ "%X='00時00分00秒'\t"
+ "%EX='00時00分00秒'\t"
+ "\n"),
+ lfmt,
+ std::chrono::hh_mm_ss(0s));
+
+ // TODO FMT What should fractions be in alternate display mode?
+ check(loc,
+ SV("%H='23'\t"
+ "%OH='二十三'\t"
+ "%I='11'\t"
+ "%OI='十一'\t"
+ "%M='31'\t"
+ "%OM='三十一'\t"
+ "%S='30.123'\t"
+ "%OS='三十.123'\t"
+ "%p='午後'\t"
+ "%R='23:31'\t"
+ "%T='23:31:30.123'\t"
+ "%r='午後11時31分30秒'\t"
+ "%X='23時31分30秒'\t"
+ "%EX='23時31分30秒'\t"
+ "\n"),
+ lfmt,
+ std::chrono::hh_mm_ss(23h + 31min + 30s + 123ms));
+
+ check(loc,
+ SV("-%H='03'\t"
+ "%OH='三'\t"
+ "%I='03'\t"
+ "%OI='三'\t"
+ "%M='02'\t"
+ "%OM='二'\t"
+ "%S='01.123456789012'\t"
+ "%OS='一.123456789012'\t"
+ "%p='午前'\t"
+ "%R='03:02'\t"
+ "%T='03:02:01.123456789012'\t"
+ "%r='午前03時02分01秒'\t"
+ "%X='03時02分01秒'\t"
+ "%EX='03時02分01秒'\t"
+ "\n"),
+ lfmt,
+ std::chrono::hh_mm_ss(-(3h + 2min + 1s + std::chrono::duration<int64_t, std::pico>(123456789012))));
+
+ check(loc,
+ SV("%H='01'\t"
+ "%OH='一'\t"
+ "%I='01'\t"
+ "%OI='一'\t"
+ "%M='01'\t"
+ "%OM='一'\t"
+ "%S='01'\t"
+ "%OS='一'\t"
+ "%p='午前'\t"
+ "%R='01:01'\t"
+ "%T='01:01:01'\t"
+ "%r='午前01時01分01秒'\t"
+ "%X='01時01分01秒'\t"
+ "%EX='01時01分01秒'\t"
+ "\n"),
+ lfmt,
+ std::chrono::hh_mm_ss(std::chrono::duration<double>(3661.123456)));
+#endif // defined(__APPLE__) || defined(_AIX)
+
+ std::locale::global(std::locale::classic());
+}
+
+template <class CharT>
+static void test_invalid_values() {
+ using namespace std::literals::chrono_literals;
+
+ // This looks odd, however the 24 hours is not valid for a 24 hour clock.
+ // TODO FMT discuss what the "proper" behaviour is.
+ check_exception("formatting a hour needs a valid value", SV("{:%H"), std::chrono::hh_mm_ss{24h});
+ check_exception("formatting a hour needs a valid value", SV("{:%OH"), std::chrono::hh_mm_ss{24h});
+ check_exception("formatting a hour needs a valid value", SV("{:%I"), std::chrono::hh_mm_ss{24h});
+ check_exception("formatting a hour needs a valid value", SV("{:%OI"), std::chrono::hh_mm_ss{24h});
+ check(SV("00"), SV("{:%M}"), std::chrono::hh_mm_ss{24h});
+ check(SV("00"), SV("{:%OM}"), std::chrono::hh_mm_ss{24h});
+ check(SV("00"), SV("{:%S}"), std::chrono::hh_mm_ss{24h});
+ check(SV("00"), SV("{:%OS}"), std::chrono::hh_mm_ss{24h});
+ check_exception("formatting a hour needs a valid value", SV("{:%p"), std::chrono::hh_mm_ss{24h});
+ check_exception("formatting a hour needs a valid value", SV("{:%R"), std::chrono::hh_mm_ss{24h});
+ check_exception("formatting a hour needs a valid value", SV("{:%T"), std::chrono::hh_mm_ss{24h});
+ check_exception("formatting a hour needs a valid value", SV("{:%r"), std::chrono::hh_mm_ss{24h});
+ check_exception("formatting a hour needs a valid value", SV("{:%X"), std::chrono::hh_mm_ss{24h});
+ check_exception("formatting a hour needs a valid value", SV("{:%EX"), std::chrono::hh_mm_ss{24h});
+}
+
+template <class CharT>
+static void test() {
+ using namespace std::literals::chrono_literals;
+
+ test_no_chrono_specs<CharT>();
+ test_valid_values<CharT>();
+ test_invalid_values<CharT>();
+ check_invalid_types<CharT>(
+ {SV("H"),
+ SV("I"),
+ SV("M"),
+ SV("S"),
+ SV("p"),
+ SV("r"),
+ SV("R"),
+ SV("T"),
+ SV("X"),
+ SV("OH"),
+ SV("OI"),
+ SV("OM"),
+ SV("OS"),
+ SV("EX")},
+ std::chrono::hh_mm_ss{0ms});
+
+ check_exception("Expected '%' or '}' in the chrono format-string", SV("{:A"), std::chrono::hh_mm_ss{0ms});
+ check_exception("The chrono-specs contains a '{'", SV("{:%%{"), std::chrono::hh_mm_ss{0ms});
+ check_exception(
+ "End of input while parsing the modifier chrono conversion-spec", SV("{:%"), std::chrono::hh_mm_ss{0ms});
+ check_exception("End of input while parsing the modifier E", SV("{:%E"), std::chrono::hh_mm_ss{0ms});
+ check_exception("End of input while parsing the modifier O", SV("{:%O"), std::chrono::hh_mm_ss{0ms});
+
+ check_exception("Expected '%' or '}' in the chrono format-string", SV("{:.3}"), std::chrono::hh_mm_ss{0ms});
+}
+
+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.formattable/concept.formattable.compile.pass.cpp b/libcxx/test/std/utilities/format/format.formattable/concept.formattable.compile.pass.cpp
index b0abc2c5bc172..1dd415abc38f3 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
@@ -161,7 +161,7 @@ void test_P1361() {
assert_is_formattable<std::chrono::year_month_weekday, CharT>();
assert_is_formattable<std::chrono::year_month_weekday_last, CharT>();
- assert_is_not_formattable<std::chrono::hh_mm_ss<std::chrono::microseconds>, CharT>();
+ assert_is_formattable<std::chrono::hh_mm_ss<std::chrono::microseconds>, CharT>();
//assert_is_formattable<std::chrono::sys_info, CharT>();
//assert_is_formattable<std::chrono::local_info, CharT>();
More information about the libcxx-commits
mailing list