brintos

brintos / llvm-project-archived public Read only

0
0
Text · 41.3 KiB · 4975839 Raw
1036 lines · c
1// -*- C++ -*-2//===----------------------------------------------------------------------===//3//4// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.5// See https://llvm.org/LICENSE.txt for license information.6// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception7//8//===----------------------------------------------------------------------===//9 10#ifndef _LIBCPP___CHRONO_FORMATTER_H11#define _LIBCPP___CHRONO_FORMATTER_H12 13#include <__config>14 15#if _LIBCPP_HAS_LOCALIZATION16 17#  include <__algorithm/ranges_copy.h>18#  include <__chrono/calendar.h>19#  include <__chrono/concepts.h>20#  include <__chrono/convert_to_tm.h>21#  include <__chrono/day.h>22#  include <__chrono/duration.h>23#  include <__chrono/file_clock.h>24#  include <__chrono/gps_clock.h>25#  include <__chrono/hh_mm_ss.h>26#  include <__chrono/local_info.h>27#  include <__chrono/month.h>28#  include <__chrono/month_weekday.h>29#  include <__chrono/monthday.h>30#  include <__chrono/ostream.h>31#  include <__chrono/parser_std_format_spec.h>32#  include <__chrono/statically_widen.h>33#  include <__chrono/sys_info.h>34#  include <__chrono/system_clock.h>35#  include <__chrono/tai_clock.h>36#  include <__chrono/time_point.h>37#  include <__chrono/utc_clock.h>38#  include <__chrono/weekday.h>39#  include <__chrono/year.h>40#  include <__chrono/year_month.h>41#  include <__chrono/year_month_day.h>42#  include <__chrono/year_month_weekday.h>43#  include <__chrono/zoned_time.h>44#  include <__concepts/arithmetic.h>45#  include <__concepts/same_as.h>46#  include <__format/concepts.h>47#  include <__format/format_error.h>48#  include <__format/format_functions.h>49#  include <__format/format_parse_context.h>50#  include <__format/formatter.h>51#  include <__format/parser_std_format_spec.h>52#  include <__format/write_escaped.h>53#  include <__iterator/istreambuf_iterator.h>54#  include <__iterator/ostreambuf_iterator.h>55#  include <__locale_dir/time.h>56#  include <__memory/addressof.h>57#  include <__type_traits/is_specialization.h>58#  include <cmath>59#  include <ctime>60#  include <limits>61#  include <sstream>62#  include <string_view>63 64#  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)65#    pragma GCC system_header66#  endif67 68_LIBCPP_BEGIN_NAMESPACE_STD69 70#  if _LIBCPP_STD_VER >= 2071 72namespace __formatter {73 74/// Formats a time based on a tm struct.75///76/// This formatter passes the formatting to time_put which uses strftime. When77/// the value is outside the valid range it's unspecified what strftime will78/// output. For example weekday 8 can print 1 when the day is processed modulo79/// 7 since that handles the Sunday for 0-based weekday. It can also print 8 if80/// 7 is handled as a special case.81///82/// The Standard doesn't specify what to do in this case so the result depends83/// on the result of the underlying code.84///85/// \pre When the (abbreviated) weekday or month name are used, the caller86///      validates whether the value is valid. So the caller handles that87///      requirement of Table 97: Meaning of conversion specifiers88///      [tab:time.format.spec].89///90/// When no chrono-specs are provided it uses the stream formatter.91 92// For tiny ratios it's not possible to convert a duration to a hh_mm_ss. This93// fails compile-time due to the limited precision of the ratio (64-bit is too94// small). Therefore a duration uses its own conversion.95template <class _CharT, class _Rep, class _Period>96_LIBCPP_HIDE_FROM_ABI void97__format_sub_seconds(basic_stringstream<_CharT>& __sstr, const chrono::duration<_Rep, _Period>& __value) {98  __sstr << std::use_facet<numpunct<_CharT>>(__sstr.getloc()).decimal_point();99 100  using __duration = chrono::duration<_Rep, _Period>;101 102  auto __fraction = __value - chrono::duration_cast<chrono::seconds>(__value);103  // Converts a negative fraction to its positive value.104  if (__value < chrono::seconds{0} && __fraction != __duration{0})105    __fraction += chrono::seconds{1};106  if constexpr (chrono::treat_as_floating_point_v<_Rep>)107    // When the floating-point value has digits itself they are ignored based108    // on the wording in [tab:time.format.spec]109    //   If the precision of the input cannot be exactly represented with110    //   seconds, then the format is a decimal floating-point number with a111    //   fixed format and a precision matching that of the precision of the112    //   input (or to a microseconds precision if the conversion to113    //   floating-point decimal seconds cannot be made within 18 fractional114    //   digits).115    //116    // This matches the behaviour of MSVC STL, fmtlib interprets this117    // differently and uses 3 decimals.118    // https://godbolt.org/z/6dsbnW8ba119    std::format_to(std::ostreambuf_iterator<_CharT>{__sstr},120                   _LIBCPP_STATICALLY_WIDEN(_CharT, "{:0{}.0f}"),121                   chrono::duration_cast<typename chrono::hh_mm_ss<__duration>::precision>(__fraction).count(),122                   chrono::hh_mm_ss<__duration>::fractional_width);123  else124    std::format_to(std::ostreambuf_iterator<_CharT>{__sstr},125                   _LIBCPP_STATICALLY_WIDEN(_CharT, "{:0{}}"),126                   chrono::duration_cast<typename chrono::hh_mm_ss<__duration>::precision>(__fraction).count(),127                   chrono::hh_mm_ss<__duration>::fractional_width);128}129 130template <class _CharT, __is_time_point _Tp>131_LIBCPP_HIDE_FROM_ABI void __format_sub_seconds(basic_stringstream<_CharT>& __sstr, const _Tp& __value) {132  __formatter::__format_sub_seconds(__sstr, __value.time_since_epoch());133}134 135template <class _CharT, class _Duration>136_LIBCPP_HIDE_FROM_ABI void137__format_sub_seconds(basic_stringstream<_CharT>& __sstr, const chrono::hh_mm_ss<_Duration>& __value) {138  __sstr << std::use_facet<numpunct<_CharT>>(__sstr.getloc()).decimal_point();139  if constexpr (chrono::treat_as_floating_point_v<typename _Duration::rep>)140    std::format_to(std::ostreambuf_iterator<_CharT>{__sstr},141                   _LIBCPP_STATICALLY_WIDEN(_CharT, "{:0{}.0f}"),142                   __value.subseconds().count(),143                   __value.fractional_width);144  else145    std::format_to(std::ostreambuf_iterator<_CharT>{__sstr},146                   _LIBCPP_STATICALLY_WIDEN(_CharT, "{:0{}}"),147                   __value.subseconds().count(),148                   __value.fractional_width);149}150 151#    if _LIBCPP_HAS_EXPERIMENTAL_TZDB && _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM152template <class _CharT, class _Duration, class _TimeZonePtr>153_LIBCPP_HIDE_FROM_ABI void154__format_sub_seconds(basic_stringstream<_CharT>& __sstr, const chrono::zoned_time<_Duration, _TimeZonePtr>& __value) {155  __formatter::__format_sub_seconds(__sstr, __value.get_local_time().time_since_epoch());156}157#    endif158 159template <class _Tp>160consteval bool __use_fraction() {161  if constexpr (__is_time_point<_Tp>)162    return chrono::hh_mm_ss<typename _Tp::duration>::fractional_width;163#    if _LIBCPP_HAS_EXPERIMENTAL_TZDB && _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM164  else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)165    return chrono::hh_mm_ss<typename _Tp::duration>::fractional_width;166#    endif167  else if constexpr (chrono::__is_duration_v<_Tp>)168    return chrono::hh_mm_ss<_Tp>::fractional_width;169  else if constexpr (__is_hh_mm_ss<_Tp>)170    return _Tp::fractional_width;171  else172    return false;173}174 175template <class _CharT>176_LIBCPP_HIDE_FROM_ABI void __format_year(basic_stringstream<_CharT>& __sstr, int __year) {177  if (__year < 0) {178    __sstr << _CharT('-');179    __year = -__year;180  }181 182  // TODO FMT Write an issue183  //   If the result has less than four digits it is zero-padded with 0 to two digits.184  // is less -> has less185  // left-padded -> zero-padded, otherwise the proper value would be 000-0.186 187  // Note according to the wording it should be left padded, which is odd.188  __sstr << std::format(_LIBCPP_STATICALLY_WIDEN(_CharT, "{:04}"), __year);189}190 191template <class _CharT>192_LIBCPP_HIDE_FROM_ABI void __format_century(basic_stringstream<_CharT>& __sstr, int __year) {193  // TODO FMT Write an issue194  // [tab:time.format.spec]195  //   %C The year divided by 100 using floored division. If the result is a196  //   single decimal digit, it is prefixed with 0.197 198  bool __negative = __year < 0;199  int __century   = (__year - (99 * __negative)) / 100; // floored division200  __sstr << std::format(_LIBCPP_STATICALLY_WIDEN(_CharT, "{:02}"), __century);201}202 203// Implements the %z format specifier according to [tab:time.format.spec], where204// '__modifier' signals %Oz or %Ez were used. (Both modifiers behave the same,205// so there is no need to distinguish between them.)206template <class _CharT>207_LIBCPP_HIDE_FROM_ABI void208__format_zone_offset(basic_stringstream<_CharT>& __sstr, chrono::seconds __offset, bool __modifier) {209  if (__offset < 0s) {210    __sstr << _CharT('-');211    __offset = -__offset;212  } else {213    __sstr << _CharT('+');214  }215 216  chrono::hh_mm_ss __hms{__offset};217  std::ostreambuf_iterator<_CharT> __out_it{__sstr};218  // Note HMS does not allow formatting hours > 23, but the offset is not limited to 24H.219  std::format_to(__out_it, _LIBCPP_STATICALLY_WIDEN(_CharT, "{:02}"), __hms.hours().count());220  if (__modifier)221    __sstr << _CharT(':');222  std::format_to(__out_it, _LIBCPP_STATICALLY_WIDEN(_CharT, "{:02}"), __hms.minutes().count());223}224 225// Helper to store the time zone information needed for formatting.226struct _LIBCPP_HIDE_FROM_ABI __time_zone {227  // Typically these abbreviations are short and fit in the string's internal228  // buffer.229  string __abbrev;230  chrono::seconds __offset;231};232 233template <class _Tp>234_LIBCPP_HIDE_FROM_ABI __time_zone __convert_to_time_zone([[maybe_unused]] const _Tp& __value) {235#    if _LIBCPP_HAS_EXPERIMENTAL_TZDB236  if constexpr (same_as<_Tp, chrono::sys_info>)237    return {__value.abbrev, __value.offset};238#      if _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM239  else if constexpr (__is_time_point<_Tp> && requires { requires same_as<typename _Tp::clock, chrono::tai_clock>; })240    return {"TAI", chrono::seconds{0}};241  else if constexpr (__is_time_point<_Tp> && requires { requires same_as<typename _Tp::clock, chrono::gps_clock>; })242    return {"GPS", chrono::seconds{0}};243  else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)244    return __formatter::__convert_to_time_zone(__value.get_info());245#      endif // _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM246  else247#    endif // _LIBCPP_HAS_EXPERIMENTAL_TZDB248    return {"UTC", chrono::seconds{0}};249}250 251template <class _CharT, class _Tp>252_LIBCPP_HIDE_FROM_ABI void __format_chrono_using_chrono_specs(253    basic_stringstream<_CharT>& __sstr, const _Tp& __value, basic_string_view<_CharT> __chrono_specs) {254  tm __t              = std::__convert_to_tm<tm>(__value);255  __time_zone __z     = __formatter::__convert_to_time_zone(__value);256  const auto& __facet = std::use_facet<time_put<_CharT>>(__sstr.getloc());257  for (auto __it = __chrono_specs.begin(); __it != __chrono_specs.end(); ++__it) {258    if (*__it == _CharT('%')) {259      auto __s = __it;260      ++__it;261      // We only handle the types that can't be directly handled by time_put.262      // (as an optimization n, t, and % are also handled directly.)263      switch (*__it) {264      case _CharT('n'):265        __sstr << _CharT('\n');266        break;267      case _CharT('t'):268        __sstr << _CharT('\t');269        break;270      case _CharT('%'):271        __sstr << *__it;272        break;273 274      case _CharT('C'): {275        // strftime's output is only defined in the range [00, 99].276        int __year = __t.tm_year + 1900;277        if (__year < 1000 || __year > 9999)278          __formatter::__format_century(__sstr, __year);279        else280          __facet.put(281              {__sstr}, __sstr, _CharT(' '), std::addressof(__t), std::to_address(__s), std::to_address(__it + 1));282      } break;283 284      case _CharT('j'):285        if constexpr (chrono::__is_duration_v<_Tp>)286          // Converting a duration where the period has a small ratio to days287          // may fail to compile. This due to loss of precision in the288          // conversion. In order to avoid that issue convert to seconds as289          // an intemediate step.290          __sstr << chrono::duration_cast<chrono::days>(chrono::duration_cast<chrono::seconds>(__value)).count();291        else292          __facet.put(293              {__sstr}, __sstr, _CharT(' '), std::addressof(__t), std::to_address(__s), std::to_address(__it + 1));294        break;295 296      case _CharT('q'):297        if constexpr (chrono::__is_duration_v<_Tp>) {298          __sstr << chrono::__units_suffix<_CharT, typename _Tp::period>();299          break;300        }301        __builtin_unreachable();302 303      case _CharT('Q'):304        // TODO FMT Determine the proper ideas305        // - Should it honour the precision?306        // - Shoult it honour the locale setting for the separators?307        // The wording for Q doesn't use the word locale and the effect of308        // precision is unspecified.309        //310        // MSVC STL ignores precision but uses separator311        // FMT honours precision and has a bug for separator312        // https://godbolt.org/z/78b7sMxns313        if constexpr (chrono::__is_duration_v<_Tp>) {314          __sstr << std::format(_LIBCPP_STATICALLY_WIDEN(_CharT, "{}"), __value.count());315          break;316        }317        __builtin_unreachable();318 319      case _CharT('S'):320      case _CharT('T'):321        __facet.put(322            {__sstr}, __sstr, _CharT(' '), std::addressof(__t), std::to_address(__s), std::to_address(__it + 1));323        if constexpr (__formatter::__use_fraction<_Tp>())324          __formatter::__format_sub_seconds(__sstr, __value);325        break;326 327        // Unlike time_put and strftime the formatting library requires %Y328        //329        // [tab:time.format.spec]330        //   The year as a decimal number. If the result is less than four digits331        //   it is left-padded with 0 to four digits.332        //333        // This means years in the range (-1000, 1000) need manual formatting.334        // It's unclear whether %EY needs the same treatment. For example the335        // Japanese EY contains the era name and year. This is zero-padded to 2336        // digits in time_put (note that older glibc versions didn't do337        // padding.) However most eras won't reach 100 years, let alone 1000.338        // So padding to 4 digits seems unwanted for Japanese.339        //340        // The same applies to %Ex since that too depends on the era.341        //342        // %x the locale's date representation is currently doesn't handle the343        // zero-padding too.344        //345        // The 4 digits can be implemented better at a later time. On POSIX346        // systems the required information can be extracted by nl_langinfo347        // https://man7.org/linux/man-pages/man3/nl_langinfo.3.html348        //349        // Note since year < -1000 is expected to be rare it uses the more350        // expensive year routine.351        //352        // TODO FMT evaluate the comment above.353 354#    if defined(__GLIBC__) || defined(_AIX) || defined(_WIN32)355      case _CharT('y'):356        // Glibc fails for negative values, AIX for positive values too.357        __sstr << std::format(_LIBCPP_STATICALLY_WIDEN(_CharT, "{:02}"), (std::abs(__t.tm_year + 1900)) % 100);358        break;359#    endif // defined(__GLIBC__) || defined(_AIX) || defined(_WIN32)360 361      case _CharT('Y'):362        // Depending on the platform's libc the range of supported years is363        // limited. Instead of of testing all conditions use the internal364        // implementation unconditionally.365        __formatter::__format_year(__sstr, __t.tm_year + 1900);366        break;367 368      case _CharT('F'):369        // Depending on the platform's libc the range of supported years is370        // limited. Instead of testing all conditions use the internal371        // implementation unconditionally.372        __formatter::__format_year(__sstr, __t.tm_year + 1900);373        __sstr << std::format(_LIBCPP_STATICALLY_WIDEN(_CharT, "-{:02}-{:02}"), __t.tm_mon + 1, __t.tm_mday);374        break;375 376      case _CharT('z'):377        __formatter::__format_zone_offset(__sstr, __z.__offset, false);378        break;379 380      case _CharT('Z'):381        // __abbrev is always a char so the copy may convert.382        ranges::copy(__z.__abbrev, std::ostreambuf_iterator<_CharT>{__sstr});383        break;384 385      case _CharT('O'):386        if constexpr (__formatter::__use_fraction<_Tp>()) {387          // Handle OS using the normal representation for the non-fractional388          // part. There seems to be no locale information regarding how the389          // fractional part should be formatted.390          if (*(__it + 1) == 'S') {391            ++__it;392            __facet.put(393                {__sstr}, __sstr, _CharT(' '), std::addressof(__t), std::to_address(__s), std::to_address(__it + 1));394            __formatter::__format_sub_seconds(__sstr, __value);395            break;396          }397        }398 399        // Oz produces the same output as Ez below.400        [[fallthrough]];401      case _CharT('E'):402        ++__it;403        if (*__it == 'z') {404          __formatter::__format_zone_offset(__sstr, __z.__offset, true);405          break;406        }407        [[fallthrough]];408      default:409        __facet.put(410            {__sstr}, __sstr, _CharT(' '), std::addressof(__t), std::to_address(__s), std::to_address(__it + 1));411        break;412      }413    } else {414      __sstr << *__it;415    }416  }417}418 419template <class _Tp>420_LIBCPP_HIDE_FROM_ABI constexpr bool __weekday_ok(const _Tp& __value) {421  if constexpr (__is_time_point<_Tp>)422    return true;423  else if constexpr (same_as<_Tp, chrono::day>)424    return true;425  else if constexpr (same_as<_Tp, chrono::month>)426    return __value.ok();427  else if constexpr (same_as<_Tp, chrono::year>)428    return true;429  else if constexpr (same_as<_Tp, chrono::weekday>)430    return true;431  else if constexpr (same_as<_Tp, chrono::weekday_indexed>)432    return true;433  else if constexpr (same_as<_Tp, chrono::weekday_last>)434    return true;435  else if constexpr (same_as<_Tp, chrono::month_day>)436    return true;437  else if constexpr (same_as<_Tp, chrono::month_day_last>)438    return true;439  else if constexpr (same_as<_Tp, chrono::month_weekday>)440    return true;441  else if constexpr (same_as<_Tp, chrono::month_weekday_last>)442    return true;443  else if constexpr (same_as<_Tp, chrono::year_month>)444    return true;445  else if constexpr (same_as<_Tp, chrono::year_month_day>)446    return __value.ok();447  else if constexpr (same_as<_Tp, chrono::year_month_day_last>)448    return __value.ok();449  else if constexpr (same_as<_Tp, chrono::year_month_weekday>)450    return __value.weekday().ok();451  else if constexpr (same_as<_Tp, chrono::year_month_weekday_last>)452    return __value.weekday().ok();453  else if constexpr (__is_hh_mm_ss<_Tp>)454    return true;455#    if _LIBCPP_HAS_EXPERIMENTAL_TZDB456  else if constexpr (same_as<_Tp, chrono::sys_info>)457    return true;458  else if constexpr (same_as<_Tp, chrono::local_info>)459    return true;460#      if _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM461  else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)462    return true;463#      endif464#    endif // _LIBCPP_HAS_EXPERIMENTAL_TZDB465  else466    static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");467}468 469template <class _Tp>470_LIBCPP_HIDE_FROM_ABI constexpr bool __weekday_name_ok(const _Tp& __value) {471  if constexpr (__is_time_point<_Tp>)472    return true;473  else if constexpr (same_as<_Tp, chrono::day>)474    return true;475  else if constexpr (same_as<_Tp, chrono::month>)476    return __value.ok();477  else if constexpr (same_as<_Tp, chrono::year>)478    return true;479  else if constexpr (same_as<_Tp, chrono::weekday>)480    return __value.ok();481  else if constexpr (same_as<_Tp, chrono::weekday_indexed>)482    return __value.weekday().ok();483  else if constexpr (same_as<_Tp, chrono::weekday_last>)484    return __value.weekday().ok();485  else if constexpr (same_as<_Tp, chrono::month_day>)486    return true;487  else if constexpr (same_as<_Tp, chrono::month_day_last>)488    return true;489  else if constexpr (same_as<_Tp, chrono::month_weekday>)490    return __value.weekday_indexed().ok();491  else if constexpr (same_as<_Tp, chrono::month_weekday_last>)492    return __value.weekday_indexed().ok();493  else if constexpr (same_as<_Tp, chrono::year_month>)494    return true;495  else if constexpr (same_as<_Tp, chrono::year_month_day>)496    return __value.ok();497  else if constexpr (same_as<_Tp, chrono::year_month_day_last>)498    return __value.ok();499  else if constexpr (same_as<_Tp, chrono::year_month_weekday>)500    return __value.weekday().ok();501  else if constexpr (same_as<_Tp, chrono::year_month_weekday_last>)502    return __value.weekday().ok();503  else if constexpr (__is_hh_mm_ss<_Tp>)504    return true;505#    if _LIBCPP_HAS_EXPERIMENTAL_TZDB506  else if constexpr (same_as<_Tp, chrono::sys_info>)507    return true;508  else if constexpr (same_as<_Tp, chrono::local_info>)509    return true;510#      if _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM511  else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)512    return true;513#      endif514#    endif // _LIBCPP_HAS_EXPERIMENTAL_TZDB515  else516    static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");517}518 519template <class _Tp>520_LIBCPP_HIDE_FROM_ABI constexpr bool __date_ok(const _Tp& __value) {521  if constexpr (__is_time_point<_Tp>)522    return true;523  else if constexpr (same_as<_Tp, chrono::day>)524    return true;525  else if constexpr (same_as<_Tp, chrono::month>)526    return __value.ok();527  else if constexpr (same_as<_Tp, chrono::year>)528    return true;529  else if constexpr (same_as<_Tp, chrono::weekday>)530    return true;531  else if constexpr (same_as<_Tp, chrono::weekday_indexed>)532    return true;533  else if constexpr (same_as<_Tp, chrono::weekday_last>)534    return true;535  else if constexpr (same_as<_Tp, chrono::month_day>)536    return true;537  else if constexpr (same_as<_Tp, chrono::month_day_last>)538    return true;539  else if constexpr (same_as<_Tp, chrono::month_weekday>)540    return true;541  else if constexpr (same_as<_Tp, chrono::month_weekday_last>)542    return true;543  else if constexpr (same_as<_Tp, chrono::year_month>)544    return true;545  else if constexpr (same_as<_Tp, chrono::year_month_day>)546    return __value.ok();547  else if constexpr (same_as<_Tp, chrono::year_month_day_last>)548    return __value.ok();549  else if constexpr (same_as<_Tp, chrono::year_month_weekday>)550    return __value.ok();551  else if constexpr (same_as<_Tp, chrono::year_month_weekday_last>)552    return __value.ok();553  else if constexpr (__is_hh_mm_ss<_Tp>)554    return true;555#    if _LIBCPP_HAS_EXPERIMENTAL_TZDB556  else if constexpr (same_as<_Tp, chrono::sys_info>)557    return true;558  else if constexpr (same_as<_Tp, chrono::local_info>)559    return true;560#      if _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM561  else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)562    return true;563#      endif564#    endif // _LIBCPP_HAS_EXPERIMENTAL_TZDB565  else566    static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");567}568 569template <class _Tp>570_LIBCPP_HIDE_FROM_ABI constexpr bool __month_name_ok(const _Tp& __value) {571  if constexpr (__is_time_point<_Tp>)572    return true;573  else if constexpr (same_as<_Tp, chrono::day>)574    return true;575  else if constexpr (same_as<_Tp, chrono::month>)576    return __value.ok();577  else if constexpr (same_as<_Tp, chrono::year>)578    return true;579  else if constexpr (same_as<_Tp, chrono::weekday>)580    return true;581  else if constexpr (same_as<_Tp, chrono::weekday_indexed>)582    return true;583  else if constexpr (same_as<_Tp, chrono::weekday_last>)584    return true;585  else if constexpr (same_as<_Tp, chrono::month_day>)586    return __value.month().ok();587  else if constexpr (same_as<_Tp, chrono::month_day_last>)588    return __value.month().ok();589  else if constexpr (same_as<_Tp, chrono::month_weekday>)590    return __value.month().ok();591  else if constexpr (same_as<_Tp, chrono::month_weekday_last>)592    return __value.month().ok();593  else if constexpr (same_as<_Tp, chrono::year_month>)594    return __value.month().ok();595  else if constexpr (same_as<_Tp, chrono::year_month_day>)596    return __value.month().ok();597  else if constexpr (same_as<_Tp, chrono::year_month_day_last>)598    return __value.month().ok();599  else if constexpr (same_as<_Tp, chrono::year_month_weekday>)600    return __value.month().ok();601  else if constexpr (same_as<_Tp, chrono::year_month_weekday_last>)602    return __value.month().ok();603  else if constexpr (__is_hh_mm_ss<_Tp>)604    return true;605#    if _LIBCPP_HAS_EXPERIMENTAL_TZDB606  else if constexpr (same_as<_Tp, chrono::sys_info>)607    return true;608  else if constexpr (same_as<_Tp, chrono::local_info>)609    return true;610#      if _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM611  else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)612    return true;613#      endif614#    endif // _LIBCPP_HAS_EXPERIMENTAL_TZDB615  else616    static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");617}618 619template <class _CharT, class _Tp, class _FormatContext>620_LIBCPP_HIDE_FROM_ABI auto621__format_chrono(const _Tp& __value,622                _FormatContext& __ctx,623                __format_spec::__parsed_specifications<_CharT> __specs,624                basic_string_view<_CharT> __chrono_specs) {625  basic_stringstream<_CharT> __sstr;626  // [time.format]/2627  // 2.1 - the "C" locale if the L option is not present in chrono-format-spec, otherwise628  // 2.2 - the locale passed to the formatting function if any, otherwise629  // 2.3 - the global locale.630  // Note that the __ctx's locale() call does 2.2 and 2.3.631  if (__specs.__chrono_.__locale_specific_form_)632    __sstr.imbue(__ctx.locale());633  else634    __sstr.imbue(locale::classic());635 636  if (__chrono_specs.empty())637    __sstr << __value;638  else {639    if constexpr (chrono::__is_duration_v<_Tp>) {640      // A duration can be a user defined arithmetic type. Users may specialize641      // numeric_limits, but they may not specialize is_signed.642      if constexpr (numeric_limits<typename _Tp::rep>::is_signed) {643        if (__value < __value.zero()) {644          __sstr << _CharT('-');645          __formatter::__format_chrono_using_chrono_specs(__sstr, -__value, __chrono_specs);646        } else647          __formatter::__format_chrono_using_chrono_specs(__sstr, __value, __chrono_specs);648      } else649        __formatter::__format_chrono_using_chrono_specs(__sstr, __value, __chrono_specs);650      // TODO FMT When keeping the precision it will truncate the string.651      // Note that the behaviour what the precision does isn't specified.652      __specs.__precision_ = -1;653    } else {654      // Test __weekday_name_ before __weekday_ to give a better error.655      if (__specs.__chrono_.__weekday_name_ && !__formatter::__weekday_name_ok(__value))656        std::__throw_format_error("Formatting a weekday name needs a valid weekday");657 658      if (__specs.__chrono_.__weekday_ && !__formatter::__weekday_ok(__value))659        std::__throw_format_error("Formatting a weekday needs a valid weekday");660 661      if (__specs.__chrono_.__day_of_year_ && !__formatter::__date_ok(__value))662        std::__throw_format_error("Formatting a day of year needs a valid date");663 664      if (__specs.__chrono_.__week_of_year_ && !__formatter::__date_ok(__value))665        std::__throw_format_error("Formatting a week of year needs a valid date");666 667      if (__specs.__chrono_.__month_name_ && !__formatter::__month_name_ok(__value))668        std::__throw_format_error("Formatting a month name from an invalid month number");669 670      if constexpr (__is_hh_mm_ss<_Tp>) {671        // Note this is a pedantic intepretation of the Standard. A hh_mm_ss672        // is no longer a time_of_day and can store an arbitrary number of673        // hours. A number of hours in a 12 or 24 hour clock can't represent674        // 24 hours or more. The functions std::chrono::make12 and675        // std::chrono::make24 reaffirm this view point.676        //677        // Interestingly this will be the only output stream function that678        // throws.679        //680        // TODO FMT The wording probably needs to be adapted to681        // - The displayed hours is hh_mm_ss.hours() % 24682        // - It should probably allow %j in the same fashion as duration.683        // - The stream formatter should change its output when hours >= 24684        //   - Write it as not valid,685        //   - or write the number of days.686        if (__specs.__chrono_.__hour_ && __value.hours().count() > 23)687          std::__throw_format_error("Formatting a hour needs a valid value");688 689        if (__value.is_negative())690          __sstr << _CharT('-');691      }692 693      __formatter::__format_chrono_using_chrono_specs(__sstr, __value, __chrono_specs);694    }695  }696 697  return __formatter::__write_string(__sstr.view(), __ctx.out(), __specs);698}699 700} // namespace __formatter701 702template <__fmt_char_type _CharT>703struct __formatter_chrono {704public:705  template <class _ParseContext>706  _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator707  __parse(_ParseContext& __ctx, __format_spec::__fields __fields, __format_spec::__flags __flags) {708    return __parser_.__parse(__ctx, __fields, __flags);709  }710 711  template <class _Tp, class _FormatContext>712  _LIBCPP_HIDE_FROM_ABI typename _FormatContext::iterator format(const _Tp& __value, _FormatContext& __ctx) const {713    return __formatter::__format_chrono(714        __value, __ctx, __parser_.__parser_.__get_parsed_chrono_specifications(__ctx), __parser_.__chrono_specs_);715  }716 717  __format_spec::__parser_chrono<_CharT> __parser_;718};719 720template <class _Duration, __fmt_char_type _CharT>721struct formatter<chrono::sys_time<_Duration>, _CharT> : public __formatter_chrono<_CharT> {722public:723  using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;724 725  template <class _ParseContext>726  _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {727    return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__clock);728  }729};730 731#    if _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM732#      if _LIBCPP_HAS_EXPERIMENTAL_TZDB733 734template <class _Duration, __fmt_char_type _CharT>735struct formatter<chrono::utc_time<_Duration>, _CharT> : public __formatter_chrono<_CharT> {736public:737  using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;738 739  template <class _ParseContext>740  _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {741    return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__clock);742  }743};744 745template <class _Duration, __fmt_char_type _CharT>746struct formatter<chrono::tai_time<_Duration>, _CharT> : public __formatter_chrono<_CharT> {747public:748  using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;749 750  template <class _ParseContext>751  _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {752    return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__clock);753  }754};755 756template <class _Duration, __fmt_char_type _CharT>757struct formatter<chrono::gps_time<_Duration>, _CharT> : public __formatter_chrono<_CharT> {758public:759  using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;760 761  template <class _ParseContext>762  _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {763    return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__clock);764  }765};766 767#      endif // _LIBCPP_HAS_EXPERIMENTAL_TZDB768#    endif   // _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM769 770template <class _Duration, __fmt_char_type _CharT>771struct formatter<chrono::file_time<_Duration>, _CharT> : public __formatter_chrono<_CharT> {772public:773  using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;774 775  template <class _ParseContext>776  _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {777    return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__clock);778  }779};780 781template <class _Duration, __fmt_char_type _CharT>782struct formatter<chrono::local_time<_Duration>, _CharT> : public __formatter_chrono<_CharT> {783public:784  using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;785 786  template <class _ParseContext>787  _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {788    // The flags are not __clock since there is no associated time-zone.789    return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__date_time);790  }791};792 793template <class _Rep, class _Period, __fmt_char_type _CharT>794struct formatter<chrono::duration<_Rep, _Period>, _CharT> : public __formatter_chrono<_CharT> {795public:796  using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;797 798  template <class _ParseContext>799  _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {800    // [time.format]/1801    // Giving a precision specification in the chrono-format-spec is valid only802    // for std::chrono::duration types where the representation type Rep is a803    // floating-point type. For all other Rep types, an exception of type804    // format_error is thrown if the chrono-format-spec contains a precision805    // specification.806    //807    // Note this doesn't refer to chrono::treat_as_floating_point_v<_Rep>.808    if constexpr (std::floating_point<_Rep>)809      return _Base::__parse(__ctx, __format_spec::__fields_chrono_fractional, __format_spec::__flags::__duration);810    else811      return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__duration);812  }813};814 815template <__fmt_char_type _CharT>816struct formatter<chrono::day, _CharT> : public __formatter_chrono<_CharT> {817public:818  using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;819 820  template <class _ParseContext>821  _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {822    return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__day);823  }824};825 826template <__fmt_char_type _CharT>827struct formatter<chrono::month, _CharT> : public __formatter_chrono<_CharT> {828public:829  using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;830 831  template <class _ParseContext>832  _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {833    return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__month);834  }835};836 837template <__fmt_char_type _CharT>838struct formatter<chrono::year, _CharT> : public __formatter_chrono<_CharT> {839public:840  using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;841 842  template <class _ParseContext>843  _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {844    return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__year);845  }846};847 848template <__fmt_char_type _CharT>849struct formatter<chrono::weekday, _CharT> : public __formatter_chrono<_CharT> {850public:851  using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;852 853  template <class _ParseContext>854  _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {855    return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__weekday);856  }857};858 859template <__fmt_char_type _CharT>860struct formatter<chrono::weekday_indexed, _CharT> : public __formatter_chrono<_CharT> {861public:862  using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;863 864  template <class _ParseContext>865  _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {866    return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__weekday);867  }868};869 870template <__fmt_char_type _CharT>871struct formatter<chrono::weekday_last, _CharT> : public __formatter_chrono<_CharT> {872public:873  using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;874 875  template <class _ParseContext>876  _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {877    return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__weekday);878  }879};880 881template <__fmt_char_type _CharT>882struct formatter<chrono::month_day, _CharT> : public __formatter_chrono<_CharT> {883public:884  using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;885 886  template <class _ParseContext>887  _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {888    return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__month_day);889  }890};891 892template <__fmt_char_type _CharT>893struct formatter<chrono::month_day_last, _CharT> : public __formatter_chrono<_CharT> {894public:895  using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;896 897  template <class _ParseContext>898  _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {899    return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__month);900  }901};902 903template <__fmt_char_type _CharT>904struct formatter<chrono::month_weekday, _CharT> : public __formatter_chrono<_CharT> {905public:906  using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;907 908  template <class _ParseContext>909  _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {910    return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__month_weekday);911  }912};913 914template <__fmt_char_type _CharT>915struct formatter<chrono::month_weekday_last, _CharT> : public __formatter_chrono<_CharT> {916public:917  using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;918 919  template <class _ParseContext>920  _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {921    return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__month_weekday);922  }923};924 925template <__fmt_char_type _CharT>926struct formatter<chrono::year_month, _CharT> : public __formatter_chrono<_CharT> {927public:928  using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;929 930  template <class _ParseContext>931  _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {932    return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__year_month);933  }934};935 936template <__fmt_char_type _CharT>937struct formatter<chrono::year_month_day, _CharT> : public __formatter_chrono<_CharT> {938public:939  using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;940 941  template <class _ParseContext>942  _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {943    return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__date);944  }945};946 947template <__fmt_char_type _CharT>948struct formatter<chrono::year_month_day_last, _CharT> : public __formatter_chrono<_CharT> {949public:950  using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;951 952  template <class _ParseContext>953  _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {954    return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__date);955  }956};957 958template <__fmt_char_type _CharT>959struct formatter<chrono::year_month_weekday, _CharT> : public __formatter_chrono<_CharT> {960public:961  using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;962 963  template <class _ParseContext>964  _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {965    return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__date);966  }967};968 969template <__fmt_char_type _CharT>970struct formatter<chrono::year_month_weekday_last, _CharT> : public __formatter_chrono<_CharT> {971public:972  using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;973 974  template <class _ParseContext>975  _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {976    return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__date);977  }978};979 980template <class _Duration, __fmt_char_type _CharT>981struct formatter<chrono::hh_mm_ss<_Duration>, _CharT> : public __formatter_chrono<_CharT> {982public:983  using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;984 985  template <class _ParseContext>986  _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {987    return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__time);988  }989};990 991#    if _LIBCPP_HAS_EXPERIMENTAL_TZDB992template <__fmt_char_type _CharT>993struct formatter<chrono::sys_info, _CharT> : public __formatter_chrono<_CharT> {994public:995  using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;996 997  template <class _ParseContext>998  _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {999    return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__time_zone);1000  }1001};1002 1003template <__fmt_char_type _CharT>1004struct formatter<chrono::local_info, _CharT> : public __formatter_chrono<_CharT> {1005public:1006  using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;1007 1008  template <class _ParseContext>1009  _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {1010    return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags{});1011  }1012};1013#      if _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM1014// Note due to how libc++'s formatters are implemented there is no need to add1015// the exposition only local-time-format-t abstraction.1016template <class _Duration, class _TimeZonePtr, __fmt_char_type _CharT>1017struct formatter<chrono::zoned_time<_Duration, _TimeZonePtr>, _CharT> : public __formatter_chrono<_CharT> {1018public:1019  using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;1020 1021  template <class _ParseContext>1022  _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {1023    return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__clock);1024  }1025};1026#      endif // _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM1027#    endif   // _LIBCPP_HAS_EXPERIMENTAL_TZDB1028 1029#  endif // if _LIBCPP_STD_VER >= 201030 1031_LIBCPP_END_NAMESPACE_STD1032 1033#endif // _LIBCPP_HAS_LOCALIZATION1034 1035#endif //  _LIBCPP___CHRONO_FORMATTER_H1036