Path: blob/main/contrib/llvm-project/libcxx/include/__chrono/formatter.h
35262 views
// -*- C++ -*-1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//89#ifndef _LIBCPP___CHRONO_FORMATTER_H10#define _LIBCPP___CHRONO_FORMATTER_H1112#include <__algorithm/ranges_copy.h>13#include <__chrono/calendar.h>14#include <__chrono/concepts.h>15#include <__chrono/convert_to_tm.h>16#include <__chrono/day.h>17#include <__chrono/duration.h>18#include <__chrono/file_clock.h>19#include <__chrono/hh_mm_ss.h>20#include <__chrono/local_info.h>21#include <__chrono/month.h>22#include <__chrono/month_weekday.h>23#include <__chrono/monthday.h>24#include <__chrono/ostream.h>25#include <__chrono/parser_std_format_spec.h>26#include <__chrono/statically_widen.h>27#include <__chrono/sys_info.h>28#include <__chrono/system_clock.h>29#include <__chrono/time_point.h>30#include <__chrono/weekday.h>31#include <__chrono/year.h>32#include <__chrono/year_month.h>33#include <__chrono/year_month_day.h>34#include <__chrono/year_month_weekday.h>35#include <__chrono/zoned_time.h>36#include <__concepts/arithmetic.h>37#include <__concepts/same_as.h>38#include <__config>39#include <__format/concepts.h>40#include <__format/format_error.h>41#include <__format/format_functions.h>42#include <__format/format_parse_context.h>43#include <__format/formatter.h>44#include <__format/parser_std_format_spec.h>45#include <__format/write_escaped.h>46#include <__memory/addressof.h>47#include <__type_traits/is_specialization.h>48#include <cmath>49#include <ctime>50#include <limits>51#include <sstream>52#include <string_view>5354#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)55# pragma GCC system_header56#endif5758_LIBCPP_BEGIN_NAMESPACE_STD5960#if _LIBCPP_STD_VER >= 206162namespace __formatter {6364/// Formats a time based on a tm struct.65///66/// This formatter passes the formatting to time_put which uses strftime. When67/// the value is outside the valid range it's unspecified what strftime will68/// output. For example weekday 8 can print 1 when the day is processed modulo69/// 7 since that handles the Sunday for 0-based weekday. It can also print 8 if70/// 7 is handled as a special case.71///72/// The Standard doesn't specify what to do in this case so the result depends73/// on the result of the underlying code.74///75/// \pre When the (abbreviated) weekday or month name are used, the caller76/// validates whether the value is valid. So the caller handles that77/// requirement of Table 97: Meaning of conversion specifiers78/// [tab:time.format.spec].79///80/// When no chrono-specs are provided it uses the stream formatter.8182// For tiny ratios it's not possible to convert a duration to a hh_mm_ss. This83// fails compile-time due to the limited precision of the ratio (64-bit is too84// small). Therefore a duration uses its own conversion.85template <class _CharT, class _Rep, class _Period>86_LIBCPP_HIDE_FROM_ABI void87__format_sub_seconds(basic_stringstream<_CharT>& __sstr, const chrono::duration<_Rep, _Period>& __value) {88__sstr << std::use_facet<numpunct<_CharT>>(__sstr.getloc()).decimal_point();8990using __duration = chrono::duration<_Rep, _Period>;9192auto __fraction = __value - chrono::duration_cast<chrono::seconds>(__value);93// Converts a negative fraction to its positive value.94if (__value < chrono::seconds{0} && __fraction != __duration{0})95__fraction += chrono::seconds{1};96if constexpr (chrono::treat_as_floating_point_v<_Rep>)97// When the floating-point value has digits itself they are ignored based98// on the wording in [tab:time.format.spec]99// If the precision of the input cannot be exactly represented with100// seconds, then the format is a decimal floating-point number with a101// fixed format and a precision matching that of the precision of the102// input (or to a microseconds precision if the conversion to103// floating-point decimal seconds cannot be made within 18 fractional104// digits).105//106// This matches the behaviour of MSVC STL, fmtlib interprets this107// differently and uses 3 decimals.108// https://godbolt.org/z/6dsbnW8ba109std::format_to(std::ostreambuf_iterator<_CharT>{__sstr},110_LIBCPP_STATICALLY_WIDEN(_CharT, "{:0{}.0f}"),111chrono::duration_cast<typename chrono::hh_mm_ss<__duration>::precision>(__fraction).count(),112chrono::hh_mm_ss<__duration>::fractional_width);113else114std::format_to(std::ostreambuf_iterator<_CharT>{__sstr},115_LIBCPP_STATICALLY_WIDEN(_CharT, "{:0{}}"),116chrono::duration_cast<typename chrono::hh_mm_ss<__duration>::precision>(__fraction).count(),117chrono::hh_mm_ss<__duration>::fractional_width);118}119120template <class _CharT, __is_time_point _Tp>121_LIBCPP_HIDE_FROM_ABI void __format_sub_seconds(basic_stringstream<_CharT>& __sstr, const _Tp& __value) {122__formatter::__format_sub_seconds(__sstr, __value.time_since_epoch());123}124125template <class _CharT, class _Duration>126_LIBCPP_HIDE_FROM_ABI void127__format_sub_seconds(basic_stringstream<_CharT>& __sstr, const chrono::hh_mm_ss<_Duration>& __value) {128__sstr << std::use_facet<numpunct<_CharT>>(__sstr.getloc()).decimal_point();129if constexpr (chrono::treat_as_floating_point_v<typename _Duration::rep>)130std::format_to(std::ostreambuf_iterator<_CharT>{__sstr},131_LIBCPP_STATICALLY_WIDEN(_CharT, "{:0{}.0f}"),132__value.subseconds().count(),133__value.fractional_width);134else135std::format_to(std::ostreambuf_iterator<_CharT>{__sstr},136_LIBCPP_STATICALLY_WIDEN(_CharT, "{:0{}}"),137__value.subseconds().count(),138__value.fractional_width);139}140141# if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB) && !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && \142!defined(_LIBCPP_HAS_NO_FILESYSTEM) && !defined(_LIBCPP_HAS_NO_LOCALIZATION)143template <class _CharT, class _Duration, class _TimeZonePtr>144_LIBCPP_HIDE_FROM_ABI void145__format_sub_seconds(basic_stringstream<_CharT>& __sstr, const chrono::zoned_time<_Duration, _TimeZonePtr>& __value) {146__formatter::__format_sub_seconds(__sstr, __value.get_local_time().time_since_epoch());147}148# endif149150template <class _Tp>151consteval bool __use_fraction() {152if constexpr (__is_time_point<_Tp>)153return chrono::hh_mm_ss<typename _Tp::duration>::fractional_width;154# if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB) && !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && \155!defined(_LIBCPP_HAS_NO_FILESYSTEM) && !defined(_LIBCPP_HAS_NO_LOCALIZATION)156else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)157return chrono::hh_mm_ss<typename _Tp::duration>::fractional_width;158# endif159else if constexpr (chrono::__is_duration<_Tp>::value)160return chrono::hh_mm_ss<_Tp>::fractional_width;161else if constexpr (__is_hh_mm_ss<_Tp>)162return _Tp::fractional_width;163else164return false;165}166167template <class _CharT>168_LIBCPP_HIDE_FROM_ABI void __format_year(basic_stringstream<_CharT>& __sstr, int __year) {169if (__year < 0) {170__sstr << _CharT('-');171__year = -__year;172}173174// TODO FMT Write an issue175// If the result has less than four digits it is zero-padded with 0 to two digits.176// is less -> has less177// left-padded -> zero-padded, otherwise the proper value would be 000-0.178179// Note according to the wording it should be left padded, which is odd.180__sstr << std::format(_LIBCPP_STATICALLY_WIDEN(_CharT, "{:04}"), __year);181}182183template <class _CharT>184_LIBCPP_HIDE_FROM_ABI void __format_century(basic_stringstream<_CharT>& __sstr, int __year) {185// TODO FMT Write an issue186// [tab:time.format.spec]187// %C The year divided by 100 using floored division. If the result is a188// single decimal digit, it is prefixed with 0.189190bool __negative = __year < 0;191int __century = (__year - (99 * __negative)) / 100; // floored division192__sstr << std::format(_LIBCPP_STATICALLY_WIDEN(_CharT, "{:02}"), __century);193}194195// Implements the %z format specifier according to [tab:time.format.spec], where196// '__modifier' signals %Oz or %Ez were used. (Both modifiers behave the same,197// so there is no need to distinguish between them.)198template <class _CharT>199_LIBCPP_HIDE_FROM_ABI void200__format_zone_offset(basic_stringstream<_CharT>& __sstr, chrono::seconds __offset, bool __modifier) {201if (__offset < 0s) {202__sstr << _CharT('-');203__offset = -__offset;204} else {205__sstr << _CharT('+');206}207208chrono::hh_mm_ss __hms{__offset};209std::ostreambuf_iterator<_CharT> __out_it{__sstr};210// Note HMS does not allow formatting hours > 23, but the offset is not limited to 24H.211std::format_to(__out_it, _LIBCPP_STATICALLY_WIDEN(_CharT, "{:02}"), __hms.hours().count());212if (__modifier)213__sstr << _CharT(':');214std::format_to(__out_it, _LIBCPP_STATICALLY_WIDEN(_CharT, "{:02}"), __hms.minutes().count());215}216217// Helper to store the time zone information needed for formatting.218struct _LIBCPP_HIDE_FROM_ABI __time_zone {219// Typically these abbreviations are short and fit in the string's internal220// buffer.221string __abbrev;222chrono::seconds __offset;223};224225template <class _Tp>226_LIBCPP_HIDE_FROM_ABI __time_zone __convert_to_time_zone([[maybe_unused]] const _Tp& __value) {227# if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)228if constexpr (same_as<_Tp, chrono::sys_info>)229return {__value.abbrev, __value.offset};230# if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \231!defined(_LIBCPP_HAS_NO_LOCALIZATION)232else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)233return __formatter::__convert_to_time_zone(__value.get_info());234# endif235else236# endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)237return {"UTC", chrono::seconds{0}};238}239240template <class _CharT, class _Tp>241_LIBCPP_HIDE_FROM_ABI void __format_chrono_using_chrono_specs(242basic_stringstream<_CharT>& __sstr, const _Tp& __value, basic_string_view<_CharT> __chrono_specs) {243tm __t = std::__convert_to_tm<tm>(__value);244__time_zone __z = __formatter::__convert_to_time_zone(__value);245const auto& __facet = std::use_facet<time_put<_CharT>>(__sstr.getloc());246for (auto __it = __chrono_specs.begin(); __it != __chrono_specs.end(); ++__it) {247if (*__it == _CharT('%')) {248auto __s = __it;249++__it;250// We only handle the types that can't be directly handled by time_put.251// (as an optimization n, t, and % are also handled directly.)252switch (*__it) {253case _CharT('n'):254__sstr << _CharT('\n');255break;256case _CharT('t'):257__sstr << _CharT('\t');258break;259case _CharT('%'):260__sstr << *__it;261break;262263case _CharT('C'): {264// strftime's output is only defined in the range [00, 99].265int __year = __t.tm_year + 1900;266if (__year < 1000 || __year > 9999)267__formatter::__format_century(__sstr, __year);268else269__facet.put(270{__sstr}, __sstr, _CharT(' '), std::addressof(__t), std::to_address(__s), std::to_address(__it + 1));271} break;272273case _CharT('j'):274if constexpr (chrono::__is_duration<_Tp>::value)275// Converting a duration where the period has a small ratio to days276// may fail to compile. This due to loss of precision in the277// conversion. In order to avoid that issue convert to seconds as278// an intemediate step.279__sstr << chrono::duration_cast<chrono::days>(chrono::duration_cast<chrono::seconds>(__value)).count();280else281__facet.put(282{__sstr}, __sstr, _CharT(' '), std::addressof(__t), std::to_address(__s), std::to_address(__it + 1));283break;284285case _CharT('q'):286if constexpr (chrono::__is_duration<_Tp>::value) {287__sstr << chrono::__units_suffix<_CharT, typename _Tp::period>();288break;289}290__builtin_unreachable();291292case _CharT('Q'):293// TODO FMT Determine the proper ideas294// - Should it honour the precision?295// - Shoult it honour the locale setting for the separators?296// The wording for Q doesn't use the word locale and the effect of297// precision is unspecified.298//299// MSVC STL ignores precision but uses separator300// FMT honours precision and has a bug for separator301// https://godbolt.org/z/78b7sMxns302if constexpr (chrono::__is_duration<_Tp>::value) {303__sstr << std::format(_LIBCPP_STATICALLY_WIDEN(_CharT, "{}"), __value.count());304break;305}306__builtin_unreachable();307308case _CharT('S'):309case _CharT('T'):310__facet.put(311{__sstr}, __sstr, _CharT(' '), std::addressof(__t), std::to_address(__s), std::to_address(__it + 1));312if constexpr (__use_fraction<_Tp>())313__formatter::__format_sub_seconds(__sstr, __value);314break;315316// Unlike time_put and strftime the formatting library requires %Y317//318// [tab:time.format.spec]319// The year as a decimal number. If the result is less than four digits320// it is left-padded with 0 to four digits.321//322// This means years in the range (-1000, 1000) need manual formatting.323// It's unclear whether %EY needs the same treatment. For example the324// Japanese EY contains the era name and year. This is zero-padded to 2325// digits in time_put (note that older glibc versions didn't do326// padding.) However most eras won't reach 100 years, let alone 1000.327// So padding to 4 digits seems unwanted for Japanese.328//329// The same applies to %Ex since that too depends on the era.330//331// %x the locale's date representation is currently doesn't handle the332// zero-padding too.333//334// The 4 digits can be implemented better at a later time. On POSIX335// systems the required information can be extracted by nl_langinfo336// https://man7.org/linux/man-pages/man3/nl_langinfo.3.html337//338// Note since year < -1000 is expected to be rare it uses the more339// expensive year routine.340//341// TODO FMT evaluate the comment above.342343# if defined(__GLIBC__) || defined(_AIX) || defined(_WIN32)344case _CharT('y'):345// Glibc fails for negative values, AIX for positive values too.346__sstr << std::format(_LIBCPP_STATICALLY_WIDEN(_CharT, "{:02}"), (std::abs(__t.tm_year + 1900)) % 100);347break;348# endif // defined(__GLIBC__) || defined(_AIX) || defined(_WIN32)349350case _CharT('Y'):351// Depending on the platform's libc the range of supported years is352// limited. Intead of of testing all conditions use the internal353// implementation unconditionally.354__formatter::__format_year(__sstr, __t.tm_year + 1900);355break;356357case _CharT('F'):358// Depending on the platform's libc the range of supported years is359// limited. Instead of testing all conditions use the internal360// implementation unconditionally.361__formatter::__format_year(__sstr, __t.tm_year + 1900);362__sstr << std::format(_LIBCPP_STATICALLY_WIDEN(_CharT, "-{:02}-{:02}"), __t.tm_mon + 1, __t.tm_mday);363break;364365case _CharT('z'):366__formatter::__format_zone_offset(__sstr, __z.__offset, false);367break;368369case _CharT('Z'):370// __abbrev is always a char so the copy may convert.371ranges::copy(__z.__abbrev, std::ostreambuf_iterator<_CharT>{__sstr});372break;373374case _CharT('O'):375if constexpr (__use_fraction<_Tp>()) {376// Handle OS using the normal representation for the non-fractional377// part. There seems to be no locale information regarding how the378// fractional part should be formatted.379if (*(__it + 1) == 'S') {380++__it;381__facet.put(382{__sstr}, __sstr, _CharT(' '), std::addressof(__t), std::to_address(__s), std::to_address(__it + 1));383__formatter::__format_sub_seconds(__sstr, __value);384break;385}386}387388// Oz produces the same output as Ez below.389[[fallthrough]];390case _CharT('E'):391++__it;392if (*__it == 'z') {393__formatter::__format_zone_offset(__sstr, __z.__offset, true);394break;395}396[[fallthrough]];397default:398__facet.put(399{__sstr}, __sstr, _CharT(' '), std::addressof(__t), std::to_address(__s), std::to_address(__it + 1));400break;401}402} else {403__sstr << *__it;404}405}406}407408template <class _Tp>409_LIBCPP_HIDE_FROM_ABI constexpr bool __weekday_ok(const _Tp& __value) {410if constexpr (__is_time_point<_Tp>)411return true;412else if constexpr (same_as<_Tp, chrono::day>)413return true;414else if constexpr (same_as<_Tp, chrono::month>)415return __value.ok();416else if constexpr (same_as<_Tp, chrono::year>)417return true;418else if constexpr (same_as<_Tp, chrono::weekday>)419return true;420else if constexpr (same_as<_Tp, chrono::weekday_indexed>)421return true;422else if constexpr (same_as<_Tp, chrono::weekday_last>)423return true;424else if constexpr (same_as<_Tp, chrono::month_day>)425return true;426else if constexpr (same_as<_Tp, chrono::month_day_last>)427return true;428else if constexpr (same_as<_Tp, chrono::month_weekday>)429return true;430else if constexpr (same_as<_Tp, chrono::month_weekday_last>)431return true;432else if constexpr (same_as<_Tp, chrono::year_month>)433return true;434else if constexpr (same_as<_Tp, chrono::year_month_day>)435return __value.ok();436else if constexpr (same_as<_Tp, chrono::year_month_day_last>)437return __value.ok();438else if constexpr (same_as<_Tp, chrono::year_month_weekday>)439return __value.weekday().ok();440else if constexpr (same_as<_Tp, chrono::year_month_weekday_last>)441return __value.weekday().ok();442else if constexpr (__is_hh_mm_ss<_Tp>)443return true;444# if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)445else if constexpr (same_as<_Tp, chrono::sys_info>)446return true;447else if constexpr (same_as<_Tp, chrono::local_info>)448return true;449# if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \450!defined(_LIBCPP_HAS_NO_LOCALIZATION)451else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)452return true;453# endif454# endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)455else456static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");457}458459template <class _Tp>460_LIBCPP_HIDE_FROM_ABI constexpr bool __weekday_name_ok(const _Tp& __value) {461if constexpr (__is_time_point<_Tp>)462return true;463else if constexpr (same_as<_Tp, chrono::day>)464return true;465else if constexpr (same_as<_Tp, chrono::month>)466return __value.ok();467else if constexpr (same_as<_Tp, chrono::year>)468return true;469else if constexpr (same_as<_Tp, chrono::weekday>)470return __value.ok();471else if constexpr (same_as<_Tp, chrono::weekday_indexed>)472return __value.weekday().ok();473else if constexpr (same_as<_Tp, chrono::weekday_last>)474return __value.weekday().ok();475else if constexpr (same_as<_Tp, chrono::month_day>)476return true;477else if constexpr (same_as<_Tp, chrono::month_day_last>)478return true;479else if constexpr (same_as<_Tp, chrono::month_weekday>)480return __value.weekday_indexed().ok();481else if constexpr (same_as<_Tp, chrono::month_weekday_last>)482return __value.weekday_indexed().ok();483else if constexpr (same_as<_Tp, chrono::year_month>)484return true;485else if constexpr (same_as<_Tp, chrono::year_month_day>)486return __value.ok();487else if constexpr (same_as<_Tp, chrono::year_month_day_last>)488return __value.ok();489else if constexpr (same_as<_Tp, chrono::year_month_weekday>)490return __value.weekday().ok();491else if constexpr (same_as<_Tp, chrono::year_month_weekday_last>)492return __value.weekday().ok();493else if constexpr (__is_hh_mm_ss<_Tp>)494return true;495# if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)496else if constexpr (same_as<_Tp, chrono::sys_info>)497return true;498else if constexpr (same_as<_Tp, chrono::local_info>)499return true;500# if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \501!defined(_LIBCPP_HAS_NO_LOCALIZATION)502else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)503return true;504# endif505# endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)506else507static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");508}509510template <class _Tp>511_LIBCPP_HIDE_FROM_ABI constexpr bool __date_ok(const _Tp& __value) {512if constexpr (__is_time_point<_Tp>)513return true;514else if constexpr (same_as<_Tp, chrono::day>)515return true;516else if constexpr (same_as<_Tp, chrono::month>)517return __value.ok();518else if constexpr (same_as<_Tp, chrono::year>)519return true;520else if constexpr (same_as<_Tp, chrono::weekday>)521return true;522else if constexpr (same_as<_Tp, chrono::weekday_indexed>)523return true;524else if constexpr (same_as<_Tp, chrono::weekday_last>)525return true;526else if constexpr (same_as<_Tp, chrono::month_day>)527return true;528else if constexpr (same_as<_Tp, chrono::month_day_last>)529return true;530else if constexpr (same_as<_Tp, chrono::month_weekday>)531return true;532else if constexpr (same_as<_Tp, chrono::month_weekday_last>)533return true;534else if constexpr (same_as<_Tp, chrono::year_month>)535return true;536else if constexpr (same_as<_Tp, chrono::year_month_day>)537return __value.ok();538else if constexpr (same_as<_Tp, chrono::year_month_day_last>)539return __value.ok();540else if constexpr (same_as<_Tp, chrono::year_month_weekday>)541return __value.ok();542else if constexpr (same_as<_Tp, chrono::year_month_weekday_last>)543return __value.ok();544else if constexpr (__is_hh_mm_ss<_Tp>)545return true;546# if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)547else if constexpr (same_as<_Tp, chrono::sys_info>)548return true;549else if constexpr (same_as<_Tp, chrono::local_info>)550return true;551# if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \552!defined(_LIBCPP_HAS_NO_LOCALIZATION)553else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)554return true;555# endif556# endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)557else558static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");559}560561template <class _Tp>562_LIBCPP_HIDE_FROM_ABI constexpr bool __month_name_ok(const _Tp& __value) {563if constexpr (__is_time_point<_Tp>)564return true;565else if constexpr (same_as<_Tp, chrono::day>)566return true;567else if constexpr (same_as<_Tp, chrono::month>)568return __value.ok();569else if constexpr (same_as<_Tp, chrono::year>)570return true;571else if constexpr (same_as<_Tp, chrono::weekday>)572return true;573else if constexpr (same_as<_Tp, chrono::weekday_indexed>)574return true;575else if constexpr (same_as<_Tp, chrono::weekday_last>)576return true;577else if constexpr (same_as<_Tp, chrono::month_day>)578return __value.month().ok();579else if constexpr (same_as<_Tp, chrono::month_day_last>)580return __value.month().ok();581else if constexpr (same_as<_Tp, chrono::month_weekday>)582return __value.month().ok();583else if constexpr (same_as<_Tp, chrono::month_weekday_last>)584return __value.month().ok();585else if constexpr (same_as<_Tp, chrono::year_month>)586return __value.month().ok();587else if constexpr (same_as<_Tp, chrono::year_month_day>)588return __value.month().ok();589else if constexpr (same_as<_Tp, chrono::year_month_day_last>)590return __value.month().ok();591else if constexpr (same_as<_Tp, chrono::year_month_weekday>)592return __value.month().ok();593else if constexpr (same_as<_Tp, chrono::year_month_weekday_last>)594return __value.month().ok();595else if constexpr (__is_hh_mm_ss<_Tp>)596return true;597# if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)598else if constexpr (same_as<_Tp, chrono::sys_info>)599return true;600else if constexpr (same_as<_Tp, chrono::local_info>)601return true;602# if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \603!defined(_LIBCPP_HAS_NO_LOCALIZATION)604else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)605return true;606# endif607# endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)608else609static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");610}611612template <class _CharT, class _Tp, class _FormatContext>613_LIBCPP_HIDE_FROM_ABI auto614__format_chrono(const _Tp& __value,615_FormatContext& __ctx,616__format_spec::__parsed_specifications<_CharT> __specs,617basic_string_view<_CharT> __chrono_specs) {618basic_stringstream<_CharT> __sstr;619// [time.format]/2620// 2.1 - the "C" locale if the L option is not present in chrono-format-spec, otherwise621// 2.2 - the locale passed to the formatting function if any, otherwise622// 2.3 - the global locale.623// Note that the __ctx's locale() call does 2.2 and 2.3.624if (__specs.__chrono_.__locale_specific_form_)625__sstr.imbue(__ctx.locale());626else627__sstr.imbue(locale::classic());628629if (__chrono_specs.empty())630__sstr << __value;631else {632if constexpr (chrono::__is_duration<_Tp>::value) {633// A duration can be a user defined arithmetic type. Users may specialize634// numeric_limits, but they may not specialize is_signed.635if constexpr (numeric_limits<typename _Tp::rep>::is_signed) {636if (__value < __value.zero()) {637__sstr << _CharT('-');638__formatter::__format_chrono_using_chrono_specs(__sstr, -__value, __chrono_specs);639} else640__formatter::__format_chrono_using_chrono_specs(__sstr, __value, __chrono_specs);641} else642__formatter::__format_chrono_using_chrono_specs(__sstr, __value, __chrono_specs);643// TODO FMT When keeping the precision it will truncate the string.644// Note that the behaviour what the precision does isn't specified.645__specs.__precision_ = -1;646} else {647// Test __weekday_name_ before __weekday_ to give a better error.648if (__specs.__chrono_.__weekday_name_ && !__formatter::__weekday_name_ok(__value))649std::__throw_format_error("Formatting a weekday name needs a valid weekday");650651if (__specs.__chrono_.__weekday_ && !__formatter::__weekday_ok(__value))652std::__throw_format_error("Formatting a weekday needs a valid weekday");653654if (__specs.__chrono_.__day_of_year_ && !__formatter::__date_ok(__value))655std::__throw_format_error("Formatting a day of year needs a valid date");656657if (__specs.__chrono_.__week_of_year_ && !__formatter::__date_ok(__value))658std::__throw_format_error("Formatting a week of year needs a valid date");659660if (__specs.__chrono_.__month_name_ && !__formatter::__month_name_ok(__value))661std::__throw_format_error("Formatting a month name from an invalid month number");662663if constexpr (__is_hh_mm_ss<_Tp>) {664// Note this is a pedantic intepretation of the Standard. A hh_mm_ss665// is no longer a time_of_day and can store an arbitrary number of666// hours. A number of hours in a 12 or 24 hour clock can't represent667// 24 hours or more. The functions std::chrono::make12 and668// std::chrono::make24 reaffirm this view point.669//670// Interestingly this will be the only output stream function that671// throws.672//673// TODO FMT The wording probably needs to be adapted to674// - The displayed hours is hh_mm_ss.hours() % 24675// - It should probably allow %j in the same fashion as duration.676// - The stream formatter should change its output when hours >= 24677// - Write it as not valid,678// - or write the number of days.679if (__specs.__chrono_.__hour_ && __value.hours().count() > 23)680std::__throw_format_error("Formatting a hour needs a valid value");681682if (__value.is_negative())683__sstr << _CharT('-');684}685686__formatter::__format_chrono_using_chrono_specs(__sstr, __value, __chrono_specs);687}688}689690return __formatter::__write_string(__sstr.view(), __ctx.out(), __specs);691}692693} // namespace __formatter694695template <__fmt_char_type _CharT>696struct _LIBCPP_TEMPLATE_VIS __formatter_chrono {697public:698template <class _ParseContext>699_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator700__parse(_ParseContext& __ctx, __format_spec::__fields __fields, __format_spec::__flags __flags) {701return __parser_.__parse(__ctx, __fields, __flags);702}703704template <class _Tp, class _FormatContext>705_LIBCPP_HIDE_FROM_ABI typename _FormatContext::iterator format(const _Tp& __value, _FormatContext& __ctx) const {706return __formatter::__format_chrono(707__value, __ctx, __parser_.__parser_.__get_parsed_chrono_specifications(__ctx), __parser_.__chrono_specs_);708}709710__format_spec::__parser_chrono<_CharT> __parser_;711};712713template <class _Duration, __fmt_char_type _CharT>714struct _LIBCPP_TEMPLATE_VIS formatter<chrono::sys_time<_Duration>, _CharT> : public __formatter_chrono<_CharT> {715public:716using _Base = __formatter_chrono<_CharT>;717718template <class _ParseContext>719_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {720return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__clock);721}722};723724template <class _Duration, __fmt_char_type _CharT>725struct _LIBCPP_TEMPLATE_VIS formatter<chrono::file_time<_Duration>, _CharT> : public __formatter_chrono<_CharT> {726public:727using _Base = __formatter_chrono<_CharT>;728729template <class _ParseContext>730_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {731return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__clock);732}733};734735template <class _Duration, __fmt_char_type _CharT>736struct _LIBCPP_TEMPLATE_VIS formatter<chrono::local_time<_Duration>, _CharT> : public __formatter_chrono<_CharT> {737public:738using _Base = __formatter_chrono<_CharT>;739740template <class _ParseContext>741_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {742// The flags are not __clock since there is no associated time-zone.743return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__date_time);744}745};746747template <class _Rep, class _Period, __fmt_char_type _CharT>748struct formatter<chrono::duration<_Rep, _Period>, _CharT> : public __formatter_chrono<_CharT> {749public:750using _Base = __formatter_chrono<_CharT>;751752template <class _ParseContext>753_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {754// [time.format]/1755// Giving a precision specification in the chrono-format-spec is valid only756// for std::chrono::duration types where the representation type Rep is a757// floating-point type. For all other Rep types, an exception of type758// format_error is thrown if the chrono-format-spec contains a precision759// specification.760//761// Note this doesn't refer to chrono::treat_as_floating_point_v<_Rep>.762if constexpr (std::floating_point<_Rep>)763return _Base::__parse(__ctx, __format_spec::__fields_chrono_fractional, __format_spec::__flags::__duration);764else765return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__duration);766}767};768769template <__fmt_char_type _CharT>770struct _LIBCPP_TEMPLATE_VIS formatter<chrono::day, _CharT> : public __formatter_chrono<_CharT> {771public:772using _Base = __formatter_chrono<_CharT>;773774template <class _ParseContext>775_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {776return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__day);777}778};779780template <__fmt_char_type _CharT>781struct _LIBCPP_TEMPLATE_VIS formatter<chrono::month, _CharT> : public __formatter_chrono<_CharT> {782public:783using _Base = __formatter_chrono<_CharT>;784785template <class _ParseContext>786_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {787return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__month);788}789};790791template <__fmt_char_type _CharT>792struct _LIBCPP_TEMPLATE_VIS formatter<chrono::year, _CharT> : public __formatter_chrono<_CharT> {793public:794using _Base = __formatter_chrono<_CharT>;795796template <class _ParseContext>797_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {798return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__year);799}800};801802template <__fmt_char_type _CharT>803struct _LIBCPP_TEMPLATE_VIS formatter<chrono::weekday, _CharT> : public __formatter_chrono<_CharT> {804public:805using _Base = __formatter_chrono<_CharT>;806807template <class _ParseContext>808_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {809return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__weekday);810}811};812813template <__fmt_char_type _CharT>814struct _LIBCPP_TEMPLATE_VIS formatter<chrono::weekday_indexed, _CharT> : public __formatter_chrono<_CharT> {815public:816using _Base = __formatter_chrono<_CharT>;817818template <class _ParseContext>819_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {820return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__weekday);821}822};823824template <__fmt_char_type _CharT>825struct _LIBCPP_TEMPLATE_VIS formatter<chrono::weekday_last, _CharT> : public __formatter_chrono<_CharT> {826public:827using _Base = __formatter_chrono<_CharT>;828829template <class _ParseContext>830_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {831return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__weekday);832}833};834835template <__fmt_char_type _CharT>836struct _LIBCPP_TEMPLATE_VIS formatter<chrono::month_day, _CharT> : public __formatter_chrono<_CharT> {837public:838using _Base = __formatter_chrono<_CharT>;839840template <class _ParseContext>841_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {842return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__month_day);843}844};845846template <__fmt_char_type _CharT>847struct _LIBCPP_TEMPLATE_VIS formatter<chrono::month_day_last, _CharT> : public __formatter_chrono<_CharT> {848public:849using _Base = __formatter_chrono<_CharT>;850851template <class _ParseContext>852_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {853return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__month);854}855};856857template <__fmt_char_type _CharT>858struct _LIBCPP_TEMPLATE_VIS formatter<chrono::month_weekday, _CharT> : public __formatter_chrono<_CharT> {859public:860using _Base = __formatter_chrono<_CharT>;861862template <class _ParseContext>863_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {864return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__month_weekday);865}866};867868template <__fmt_char_type _CharT>869struct _LIBCPP_TEMPLATE_VIS formatter<chrono::month_weekday_last, _CharT> : public __formatter_chrono<_CharT> {870public:871using _Base = __formatter_chrono<_CharT>;872873template <class _ParseContext>874_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {875return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__month_weekday);876}877};878879template <__fmt_char_type _CharT>880struct _LIBCPP_TEMPLATE_VIS formatter<chrono::year_month, _CharT> : public __formatter_chrono<_CharT> {881public:882using _Base = __formatter_chrono<_CharT>;883884template <class _ParseContext>885_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {886return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__year_month);887}888};889890template <__fmt_char_type _CharT>891struct _LIBCPP_TEMPLATE_VIS formatter<chrono::year_month_day, _CharT> : public __formatter_chrono<_CharT> {892public:893using _Base = __formatter_chrono<_CharT>;894895template <class _ParseContext>896_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {897return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__date);898}899};900901template <__fmt_char_type _CharT>902struct _LIBCPP_TEMPLATE_VIS formatter<chrono::year_month_day_last, _CharT> : public __formatter_chrono<_CharT> {903public:904using _Base = __formatter_chrono<_CharT>;905906template <class _ParseContext>907_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {908return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__date);909}910};911912template <__fmt_char_type _CharT>913struct _LIBCPP_TEMPLATE_VIS formatter<chrono::year_month_weekday, _CharT> : public __formatter_chrono<_CharT> {914public:915using _Base = __formatter_chrono<_CharT>;916917template <class _ParseContext>918_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {919return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__date);920}921};922923template <__fmt_char_type _CharT>924struct _LIBCPP_TEMPLATE_VIS formatter<chrono::year_month_weekday_last, _CharT> : public __formatter_chrono<_CharT> {925public:926using _Base = __formatter_chrono<_CharT>;927928template <class _ParseContext>929_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {930return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__date);931}932};933934template <class _Duration, __fmt_char_type _CharT>935struct formatter<chrono::hh_mm_ss<_Duration>, _CharT> : public __formatter_chrono<_CharT> {936public:937using _Base = __formatter_chrono<_CharT>;938939template <class _ParseContext>940_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {941return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__time);942}943};944945# if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)946template <__fmt_char_type _CharT>947struct formatter<chrono::sys_info, _CharT> : public __formatter_chrono<_CharT> {948public:949using _Base = __formatter_chrono<_CharT>;950951template <class _ParseContext>952_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {953return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__time_zone);954}955};956957template <__fmt_char_type _CharT>958struct formatter<chrono::local_info, _CharT> : public __formatter_chrono<_CharT> {959public:960using _Base = __formatter_chrono<_CharT>;961962template <class _ParseContext>963_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {964return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags{});965}966};967# if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \968!defined(_LIBCPP_HAS_NO_LOCALIZATION)969// Note due to how libc++'s formatters are implemented there is no need to add970// the exposition only local-time-format-t abstraction.971template <class _Duration, class _TimeZonePtr, __fmt_char_type _CharT>972struct formatter<chrono::zoned_time<_Duration, _TimeZonePtr>, _CharT> : public __formatter_chrono<_CharT> {973public:974using _Base = __formatter_chrono<_CharT>;975976template <class _ParseContext>977_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {978return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__clock);979}980};981# endif // !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) &&982// !defined(_LIBCPP_HAS_NO_LOCALIZATION)983# endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)984985#endif // if _LIBCPP_STD_VER >= 20986987_LIBCPP_END_NAMESPACE_STD988989#endif // _LIBCPP___CHRONO_FORMATTER_H990991992