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