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-exception 7 // 8 //===----------------------------------------------------------------------===// 9 10 #ifndef _LIBCPP___CHRONO_FORMATTER_H 11 #define _LIBCPP___CHRONO_FORMATTER_H 12 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/month.h> 21 #include <__chrono/month_weekday.h> 22 #include <__chrono/monthday.h> 23 #include <__chrono/ostream.h> 24 #include <__chrono/parser_std_format_spec.h> 25 #include <__chrono/statically_widen.h> 26 #include <__chrono/system_clock.h> 27 #include <__chrono/time_point.h> 28 #include <__chrono/weekday.h> 29 #include <__chrono/year.h> 30 #include <__chrono/year_month.h> 31 #include <__chrono/year_month_day.h> 32 #include <__chrono/year_month_weekday.h> 33 #include <__concepts/arithmetic.h> 34 #include <__concepts/same_as.h> 35 #include <__config> 36 #include <__format/concepts.h> 37 #include <__format/format_error.h> 38 #include <__format/format_functions.h> 39 #include <__format/format_parse_context.h> 40 #include <__format/formatter.h> 41 #include <__format/parser_std_format_spec.h> 42 #include <__format/write_escaped.h> 43 #include <__memory/addressof.h> 44 #include <cmath> 45 #include <ctime> 46 #include <sstream> 47 #include <string_view> 48 49 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) 50 # pragma GCC system_header 51 #endif 52 53 _LIBCPP_BEGIN_NAMESPACE_STD 54 55 #if _LIBCPP_STD_VER >= 20 56 57 namespace __formatter { 58 59 /// Formats a time based on a tm struct. 60 /// 61 /// This formatter passes the formatting to time_put which uses strftime. When 62 /// the value is outside the valid range it's unspecified what strftime will 63 /// output. For example weekday 8 can print 1 when the day is processed modulo 64 /// 7 since that handles the Sunday for 0-based weekday. It can also print 8 if 65 /// 7 is handled as a special case. 66 /// 67 /// The Standard doesn't specify what to do in this case so the result depends 68 /// on the result of the underlying code. 69 /// 70 /// \pre When the (abbreviated) weekday or month name are used, the caller 71 /// validates whether the value is valid. So the caller handles that 72 /// requirement of Table 97: Meaning of conversion specifiers 73 /// [tab:time.format.spec]. 74 /// 75 /// When no chrono-specs are provided it uses the stream formatter. 76 77 // For tiny ratios it's not possible to convert a duration to a hh_mm_ss. This 78 // fails compile-time due to the limited precision of the ratio (64-bit is too 79 // small). Therefore a duration uses its own conversion. 80 template <class _CharT, class _Rep, class _Period> 81 _LIBCPP_HIDE_FROM_ABI void 82 __format_sub_seconds(const chrono::duration<_Rep, _Period>& __value, basic_stringstream<_CharT>& __sstr) { 83 __sstr << std::use_facet<numpunct<_CharT>>(__sstr.getloc()).decimal_point(); 84 85 using __duration = chrono::duration<_Rep, _Period>; 86 87 auto __fraction = __value - chrono::duration_cast<chrono::seconds>(__value); 88 if constexpr (chrono::treat_as_floating_point_v<_Rep>) 89 // When the floating-point value has digits itself they are ignored based 90 // on the wording in [tab:time.format.spec] 91 // If the precision of the input cannot be exactly represented with 92 // seconds, then the format is a decimal floating-point number with a 93 // fixed format and a precision matching that of the precision of the 94 // input (or to a microseconds precision if the conversion to 95 // floating-point decimal seconds cannot be made within 18 fractional 96 // digits). 97 // 98 // This matches the behaviour of MSVC STL, fmtlib interprets this 99 // differently and uses 3 decimals. 100 // https://godbolt.org/z/6dsbnW8ba 101 std::format_to(std::ostreambuf_iterator<_CharT>{__sstr}, 102 _LIBCPP_STATICALLY_WIDEN(_CharT, "{:0{}.0f}"), 103 chrono::duration_cast<typename chrono::hh_mm_ss<__duration>::precision>(__fraction).count(), 104 chrono::hh_mm_ss<__duration>::fractional_width); 105 else 106 std::format_to(std::ostreambuf_iterator<_CharT>{__sstr}, 107 _LIBCPP_STATICALLY_WIDEN(_CharT, "{:0{}}"), 108 chrono::duration_cast<typename chrono::hh_mm_ss<__duration>::precision>(__fraction).count(), 109 chrono::hh_mm_ss<__duration>::fractional_width); 110 } 111 112 template <class _CharT, __is_time_point _Tp> 113 _LIBCPP_HIDE_FROM_ABI void __format_sub_seconds(const _Tp& __value, basic_stringstream<_CharT>& __sstr) { 114 __formatter::__format_sub_seconds(__value.time_since_epoch(), __sstr); 115 } 116 117 template <class _CharT, class _Duration> 118 _LIBCPP_HIDE_FROM_ABI void 119 __format_sub_seconds(const chrono::hh_mm_ss<_Duration>& __value, basic_stringstream<_CharT>& __sstr) { 120 __sstr << std::use_facet<numpunct<_CharT>>(__sstr.getloc()).decimal_point(); 121 if constexpr (chrono::treat_as_floating_point_v<typename _Duration::rep>) 122 std::format_to(std::ostreambuf_iterator<_CharT>{__sstr}, 123 _LIBCPP_STATICALLY_WIDEN(_CharT, "{:0{}.0f}"), 124 __value.subseconds().count(), 125 __value.fractional_width); 126 else 127 std::format_to(std::ostreambuf_iterator<_CharT>{__sstr}, 128 _LIBCPP_STATICALLY_WIDEN(_CharT, "{:0{}}"), 129 __value.subseconds().count(), 130 __value.fractional_width); 131 } 132 133 template <class _Tp> 134 consteval bool __use_fraction() { 135 if constexpr (__is_time_point<_Tp>) 136 return chrono::hh_mm_ss<typename _Tp::duration>::fractional_width; 137 else if constexpr (chrono::__is_duration<_Tp>::value) 138 return chrono::hh_mm_ss<_Tp>::fractional_width; 139 else if constexpr (__is_hh_mm_ss<_Tp>) 140 return _Tp::fractional_width; 141 else 142 return false; 143 } 144 145 template <class _CharT> 146 _LIBCPP_HIDE_FROM_ABI void __format_year(int __year, basic_stringstream<_CharT>& __sstr) { 147 if (__year < 0) { 148 __sstr << _CharT('-'); 149 __year = -__year; 150 } 151 152 // TODO FMT Write an issue 153 // If the result has less than four digits it is zero-padded with 0 to two digits. 154 // is less -> has less 155 // left-padded -> zero-padded, otherwise the proper value would be 000-0. 156 157 // Note according to the wording it should be left padded, which is odd. 158 __sstr << std::format(_LIBCPP_STATICALLY_WIDEN(_CharT, "{:04}"), __year); 159 } 160 161 template <class _CharT> 162 _LIBCPP_HIDE_FROM_ABI void __format_century(int __year, basic_stringstream<_CharT>& __sstr) { 163 // TODO FMT Write an issue 164 // [tab:time.format.spec] 165 // %C The year divided by 100 using floored division. If the result is a 166 // single decimal digit, it is prefixed with 0. 167 168 bool __negative = __year < 0; 169 int __century = (__year - (99 * __negative)) / 100; // floored division 170 __sstr << std::format(_LIBCPP_STATICALLY_WIDEN(_CharT, "{:02}"), __century); 171 } 172 173 template <class _CharT, class _Tp> 174 _LIBCPP_HIDE_FROM_ABI void __format_chrono_using_chrono_specs( 175 const _Tp& __value, basic_stringstream<_CharT>& __sstr, basic_string_view<_CharT> __chrono_specs) { 176 tm __t = std::__convert_to_tm<tm>(__value); 177 const auto& __facet = std::use_facet<time_put<_CharT>>(__sstr.getloc()); 178 for (auto __it = __chrono_specs.begin(); __it != __chrono_specs.end(); ++__it) { 179 if (*__it == _CharT('%')) { 180 auto __s = __it; 181 ++__it; 182 // We only handle the types that can't be directly handled by time_put. 183 // (as an optimization n, t, and % are also handled directly.) 184 switch (*__it) { 185 case _CharT('n'): 186 __sstr << _CharT('\n'); 187 break; 188 case _CharT('t'): 189 __sstr << _CharT('\t'); 190 break; 191 case _CharT('%'): 192 __sstr << *__it; 193 break; 194 195 case _CharT('C'): { 196 // strftime's output is only defined in the range [00, 99]. 197 int __year = __t.tm_year + 1900; 198 if (__year < 1000 || __year > 9999) 199 __formatter::__format_century(__year, __sstr); 200 else 201 __facet.put( 202 {__sstr}, __sstr, _CharT(' '), std::addressof(__t), std::to_address(__s), std::to_address(__it + 1)); 203 } break; 204 205 case _CharT('j'): 206 if constexpr (chrono::__is_duration<_Tp>::value) 207 // Converting a duration where the period has a small ratio to days 208 // may fail to compile. This due to loss of precision in the 209 // conversion. In order to avoid that issue convert to seconds as 210 // an intemediate step. 211 __sstr << chrono::duration_cast<chrono::days>(chrono::duration_cast<chrono::seconds>(__value)).count(); 212 else 213 __facet.put( 214 {__sstr}, __sstr, _CharT(' '), std::addressof(__t), std::to_address(__s), std::to_address(__it + 1)); 215 break; 216 217 case _CharT('q'): 218 if constexpr (chrono::__is_duration<_Tp>::value) { 219 __sstr << chrono::__units_suffix<_CharT, typename _Tp::period>(); 220 break; 221 } 222 __builtin_unreachable(); 223 224 case _CharT('Q'): 225 // TODO FMT Determine the proper ideas 226 // - Should it honour the precision? 227 // - Shoult it honour the locale setting for the separators? 228 // The wording for Q doesn't use the word locale and the effect of 229 // precision is unspecified. 230 // 231 // MSVC STL ignores precision but uses separator 232 // FMT honours precision and has a bug for separator 233 // https://godbolt.org/z/78b7sMxns 234 if constexpr (chrono::__is_duration<_Tp>::value) { 235 __sstr << std::format(_LIBCPP_STATICALLY_WIDEN(_CharT, "{}"), __value.count()); 236 break; 237 } 238 __builtin_unreachable(); 239 240 case _CharT('S'): 241 case _CharT('T'): 242 __facet.put( 243 {__sstr}, __sstr, _CharT(' '), std::addressof(__t), std::to_address(__s), std::to_address(__it + 1)); 244 if constexpr (__use_fraction<_Tp>()) 245 __formatter::__format_sub_seconds(__value, __sstr); 246 break; 247 248 // Unlike time_put and strftime the formatting library requires %Y 249 // 250 // [tab:time.format.spec] 251 // The year as a decimal number. If the result is less than four digits 252 // it is left-padded with 0 to four digits. 253 // 254 // This means years in the range (-1000, 1000) need manual formatting. 255 // It's unclear whether %EY needs the same treatment. For example the 256 // Japanese EY contains the era name and year. This is zero-padded to 2 257 // digits in time_put (note that older glibc versions didn't do 258 // padding.) However most eras won't reach 100 years, let alone 1000. 259 // So padding to 4 digits seems unwanted for Japanese. 260 // 261 // The same applies to %Ex since that too depends on the era. 262 // 263 // %x the locale's date representation is currently doesn't handle the 264 // zero-padding too. 265 // 266 // The 4 digits can be implemented better at a later time. On POSIX 267 // systems the required information can be extracted by nl_langinfo 268 // https://man7.org/linux/man-pages/man3/nl_langinfo.3.html 269 // 270 // Note since year < -1000 is expected to be rare it uses the more 271 // expensive year routine. 272 // 273 // TODO FMT evaluate the comment above. 274 275 # if defined(__GLIBC__) || defined(_AIX) || defined(_WIN32) 276 case _CharT('y'): 277 // Glibc fails for negative values, AIX for positive values too. 278 __sstr << std::format(_LIBCPP_STATICALLY_WIDEN(_CharT, "{:02}"), (std::abs(__t.tm_year + 1900)) % 100); 279 break; 280 # endif // defined(__GLIBC__) || defined(_AIX) || defined(_WIN32) 281 282 case _CharT('Y'): 283 // Depending on the platform's libc the range of supported years is 284 // limited. Intead of of testing all conditions use the internal 285 // implementation unconditionally. 286 __formatter::__format_year(__t.tm_year + 1900, __sstr); 287 break; 288 289 case _CharT('F'): { 290 int __year = __t.tm_year + 1900; 291 if (__year < 1000) { 292 __formatter::__format_year(__year, __sstr); 293 __sstr << std::format(_LIBCPP_STATICALLY_WIDEN(_CharT, "-{:02}-{:02}"), __t.tm_mon + 1, __t.tm_mday); 294 } else 295 __facet.put( 296 {__sstr}, __sstr, _CharT(' '), std::addressof(__t), std::to_address(__s), std::to_address(__it + 1)); 297 } break; 298 299 case _CharT('Z'): 300 // TODO FMT Add proper timezone support. 301 __sstr << _LIBCPP_STATICALLY_WIDEN(_CharT, "UTC"); 302 break; 303 304 case _CharT('O'): 305 if constexpr (__use_fraction<_Tp>()) { 306 // Handle OS using the normal representation for the non-fractional 307 // part. There seems to be no locale information regarding how the 308 // fractional part should be formatted. 309 if (*(__it + 1) == 'S') { 310 ++__it; 311 __facet.put( 312 {__sstr}, __sstr, _CharT(' '), std::addressof(__t), std::to_address(__s), std::to_address(__it + 1)); 313 __formatter::__format_sub_seconds(__value, __sstr); 314 break; 315 } 316 } 317 [[fallthrough]]; 318 case _CharT('E'): 319 ++__it; 320 [[fallthrough]]; 321 default: 322 __facet.put( 323 {__sstr}, __sstr, _CharT(' '), std::addressof(__t), std::to_address(__s), std::to_address(__it + 1)); 324 break; 325 } 326 } else { 327 __sstr << *__it; 328 } 329 } 330 } 331 332 template <class _Tp> 333 _LIBCPP_HIDE_FROM_ABI constexpr bool __weekday_ok(const _Tp& __value) { 334 if constexpr (__is_time_point<_Tp>) 335 return true; 336 else if constexpr (same_as<_Tp, chrono::day>) 337 return true; 338 else if constexpr (same_as<_Tp, chrono::month>) 339 return __value.ok(); 340 else if constexpr (same_as<_Tp, chrono::year>) 341 return true; 342 else if constexpr (same_as<_Tp, chrono::weekday>) 343 return true; 344 else if constexpr (same_as<_Tp, chrono::weekday_indexed>) 345 return true; 346 else if constexpr (same_as<_Tp, chrono::weekday_last>) 347 return true; 348 else if constexpr (same_as<_Tp, chrono::month_day>) 349 return true; 350 else if constexpr (same_as<_Tp, chrono::month_day_last>) 351 return true; 352 else if constexpr (same_as<_Tp, chrono::month_weekday>) 353 return true; 354 else if constexpr (same_as<_Tp, chrono::month_weekday_last>) 355 return true; 356 else if constexpr (same_as<_Tp, chrono::year_month>) 357 return true; 358 else if constexpr (same_as<_Tp, chrono::year_month_day>) 359 return __value.ok(); 360 else if constexpr (same_as<_Tp, chrono::year_month_day_last>) 361 return __value.ok(); 362 else if constexpr (same_as<_Tp, chrono::year_month_weekday>) 363 return __value.weekday().ok(); 364 else if constexpr (same_as<_Tp, chrono::year_month_weekday_last>) 365 return __value.weekday().ok(); 366 else if constexpr (__is_hh_mm_ss<_Tp>) 367 return true; 368 else 369 static_assert(sizeof(_Tp) == 0, "Add the missing type specialization"); 370 } 371 372 template <class _Tp> 373 _LIBCPP_HIDE_FROM_ABI constexpr bool __weekday_name_ok(const _Tp& __value) { 374 if constexpr (__is_time_point<_Tp>) 375 return true; 376 else if constexpr (same_as<_Tp, chrono::day>) 377 return true; 378 else if constexpr (same_as<_Tp, chrono::month>) 379 return __value.ok(); 380 else if constexpr (same_as<_Tp, chrono::year>) 381 return true; 382 else if constexpr (same_as<_Tp, chrono::weekday>) 383 return __value.ok(); 384 else if constexpr (same_as<_Tp, chrono::weekday_indexed>) 385 return __value.weekday().ok(); 386 else if constexpr (same_as<_Tp, chrono::weekday_last>) 387 return __value.weekday().ok(); 388 else if constexpr (same_as<_Tp, chrono::month_day>) 389 return true; 390 else if constexpr (same_as<_Tp, chrono::month_day_last>) 391 return true; 392 else if constexpr (same_as<_Tp, chrono::month_weekday>) 393 return __value.weekday_indexed().ok(); 394 else if constexpr (same_as<_Tp, chrono::month_weekday_last>) 395 return __value.weekday_indexed().ok(); 396 else if constexpr (same_as<_Tp, chrono::year_month>) 397 return true; 398 else if constexpr (same_as<_Tp, chrono::year_month_day>) 399 return __value.ok(); 400 else if constexpr (same_as<_Tp, chrono::year_month_day_last>) 401 return __value.ok(); 402 else if constexpr (same_as<_Tp, chrono::year_month_weekday>) 403 return __value.weekday().ok(); 404 else if constexpr (same_as<_Tp, chrono::year_month_weekday_last>) 405 return __value.weekday().ok(); 406 else if constexpr (__is_hh_mm_ss<_Tp>) 407 return true; 408 else 409 static_assert(sizeof(_Tp) == 0, "Add the missing type specialization"); 410 } 411 412 template <class _Tp> 413 _LIBCPP_HIDE_FROM_ABI constexpr bool __date_ok(const _Tp& __value) { 414 if constexpr (__is_time_point<_Tp>) 415 return true; 416 else if constexpr (same_as<_Tp, chrono::day>) 417 return true; 418 else if constexpr (same_as<_Tp, chrono::month>) 419 return __value.ok(); 420 else if constexpr (same_as<_Tp, chrono::year>) 421 return true; 422 else if constexpr (same_as<_Tp, chrono::weekday>) 423 return true; 424 else if constexpr (same_as<_Tp, chrono::weekday_indexed>) 425 return true; 426 else if constexpr (same_as<_Tp, chrono::weekday_last>) 427 return true; 428 else if constexpr (same_as<_Tp, chrono::month_day>) 429 return true; 430 else if constexpr (same_as<_Tp, chrono::month_day_last>) 431 return true; 432 else if constexpr (same_as<_Tp, chrono::month_weekday>) 433 return true; 434 else if constexpr (same_as<_Tp, chrono::month_weekday_last>) 435 return true; 436 else if constexpr (same_as<_Tp, chrono::year_month>) 437 return true; 438 else if constexpr (same_as<_Tp, chrono::year_month_day>) 439 return __value.ok(); 440 else if constexpr (same_as<_Tp, chrono::year_month_day_last>) 441 return __value.ok(); 442 else if constexpr (same_as<_Tp, chrono::year_month_weekday>) 443 return __value.ok(); 444 else if constexpr (same_as<_Tp, chrono::year_month_weekday_last>) 445 return __value.ok(); 446 else if constexpr (__is_hh_mm_ss<_Tp>) 447 return true; 448 else 449 static_assert(sizeof(_Tp) == 0, "Add the missing type specialization"); 450 } 451 452 template <class _Tp> 453 _LIBCPP_HIDE_FROM_ABI constexpr bool __month_name_ok(const _Tp& __value) { 454 if constexpr (__is_time_point<_Tp>) 455 return true; 456 else if constexpr (same_as<_Tp, chrono::day>) 457 return true; 458 else if constexpr (same_as<_Tp, chrono::month>) 459 return __value.ok(); 460 else if constexpr (same_as<_Tp, chrono::year>) 461 return true; 462 else if constexpr (same_as<_Tp, chrono::weekday>) 463 return true; 464 else if constexpr (same_as<_Tp, chrono::weekday_indexed>) 465 return true; 466 else if constexpr (same_as<_Tp, chrono::weekday_last>) 467 return true; 468 else if constexpr (same_as<_Tp, chrono::month_day>) 469 return __value.month().ok(); 470 else if constexpr (same_as<_Tp, chrono::month_day_last>) 471 return __value.month().ok(); 472 else if constexpr (same_as<_Tp, chrono::month_weekday>) 473 return __value.month().ok(); 474 else if constexpr (same_as<_Tp, chrono::month_weekday_last>) 475 return __value.month().ok(); 476 else if constexpr (same_as<_Tp, chrono::year_month>) 477 return __value.month().ok(); 478 else if constexpr (same_as<_Tp, chrono::year_month_day>) 479 return __value.month().ok(); 480 else if constexpr (same_as<_Tp, chrono::year_month_day_last>) 481 return __value.month().ok(); 482 else if constexpr (same_as<_Tp, chrono::year_month_weekday>) 483 return __value.month().ok(); 484 else if constexpr (same_as<_Tp, chrono::year_month_weekday_last>) 485 return __value.month().ok(); 486 else if constexpr (__is_hh_mm_ss<_Tp>) 487 return true; 488 else 489 static_assert(sizeof(_Tp) == 0, "Add the missing type specialization"); 490 } 491 492 template <class _CharT, class _Tp, class _FormatContext> 493 _LIBCPP_HIDE_FROM_ABI auto 494 __format_chrono(const _Tp& __value, 495 _FormatContext& __ctx, 496 __format_spec::__parsed_specifications<_CharT> __specs, 497 basic_string_view<_CharT> __chrono_specs) { 498 basic_stringstream<_CharT> __sstr; 499 // [time.format]/2 500 // 2.1 - the "C" locale if the L option is not present in chrono-format-spec, otherwise 501 // 2.2 - the locale passed to the formatting function if any, otherwise 502 // 2.3 - the global locale. 503 // Note that the __ctx's locale() call does 2.2 and 2.3. 504 if (__specs.__chrono_.__locale_specific_form_) 505 __sstr.imbue(__ctx.locale()); 506 else 507 __sstr.imbue(locale::classic()); 508 509 if (__chrono_specs.empty()) 510 __sstr << __value; 511 else { 512 if constexpr (chrono::__is_duration<_Tp>::value) { 513 if (__value < __value.zero()) 514 __sstr << _CharT('-'); 515 __formatter::__format_chrono_using_chrono_specs(chrono::abs(__value), __sstr, __chrono_specs); 516 // TODO FMT When keeping the precision it will truncate the string. 517 // Note that the behaviour what the precision does isn't specified. 518 __specs.__precision_ = -1; 519 } else { 520 // Test __weekday_name_ before __weekday_ to give a better error. 521 if (__specs.__chrono_.__weekday_name_ && !__formatter::__weekday_name_ok(__value)) 522 std::__throw_format_error("Formatting a weekday name needs a valid weekday"); 523 524 if (__specs.__chrono_.__weekday_ && !__formatter::__weekday_ok(__value)) 525 std::__throw_format_error("Formatting a weekday needs a valid weekday"); 526 527 if (__specs.__chrono_.__day_of_year_ && !__formatter::__date_ok(__value)) 528 std::__throw_format_error("Formatting a day of year needs a valid date"); 529 530 if (__specs.__chrono_.__week_of_year_ && !__formatter::__date_ok(__value)) 531 std::__throw_format_error("Formatting a week of year needs a valid date"); 532 533 if (__specs.__chrono_.__month_name_ && !__formatter::__month_name_ok(__value)) 534 std::__throw_format_error("Formatting a month name from an invalid month number"); 535 536 if constexpr (__is_hh_mm_ss<_Tp>) { 537 // Note this is a pedantic intepretation of the Standard. A hh_mm_ss 538 // is no longer a time_of_day and can store an arbitrary number of 539 // hours. A number of hours in a 12 or 24 hour clock can't represent 540 // 24 hours or more. The functions std::chrono::make12 and 541 // std::chrono::make24 reaffirm this view point. 542 // 543 // Interestingly this will be the only output stream function that 544 // throws. 545 // 546 // TODO FMT The wording probably needs to be adapted to 547 // - The displayed hours is hh_mm_ss.hours() % 24 548 // - It should probably allow %j in the same fashion as duration. 549 // - The stream formatter should change its output when hours >= 24 550 // - Write it as not valid, 551 // - or write the number of days. 552 if (__specs.__chrono_.__hour_ && __value.hours().count() > 23) 553 std::__throw_format_error("Formatting a hour needs a valid value"); 554 555 if (__value.is_negative()) 556 __sstr << _CharT('-'); 557 } 558 559 __formatter::__format_chrono_using_chrono_specs(__value, __sstr, __chrono_specs); 560 } 561 } 562 563 return __formatter::__write_string(__sstr.view(), __ctx.out(), __specs); 564 } 565 566 } // namespace __formatter 567 568 template <__fmt_char_type _CharT> 569 struct _LIBCPP_TEMPLATE_VIS __formatter_chrono { 570 public: 571 template <class _ParseContext> 572 _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator 573 __parse(_ParseContext& __ctx, __format_spec::__fields __fields, __format_spec::__flags __flags) { 574 return __parser_.__parse(__ctx, __fields, __flags); 575 } 576 577 template <class _Tp, class _FormatContext> 578 _LIBCPP_HIDE_FROM_ABI typename _FormatContext::iterator format(const _Tp& __value, _FormatContext& __ctx) const { 579 return __formatter::__format_chrono( 580 __value, __ctx, __parser_.__parser_.__get_parsed_chrono_specifications(__ctx), __parser_.__chrono_specs_); 581 } 582 583 __format_spec::__parser_chrono<_CharT> __parser_; 584 }; 585 586 template <class _Duration, __fmt_char_type _CharT> 587 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::sys_time<_Duration>, _CharT> : public __formatter_chrono<_CharT> { 588 public: 589 using _Base = __formatter_chrono<_CharT>; 590 591 template <class _ParseContext> 592 _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) { 593 return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__clock); 594 } 595 }; 596 597 template <class _Duration, __fmt_char_type _CharT> 598 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::file_time<_Duration>, _CharT> : public __formatter_chrono<_CharT> { 599 public: 600 using _Base = __formatter_chrono<_CharT>; 601 602 template <class _ParseContext> 603 _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) { 604 return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__clock); 605 } 606 }; 607 608 template <class _Duration, __fmt_char_type _CharT> 609 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::local_time<_Duration>, _CharT> : public __formatter_chrono<_CharT> { 610 public: 611 using _Base = __formatter_chrono<_CharT>; 612 613 template <class _ParseContext> 614 _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) { 615 // The flags are not __clock since there is no associated time-zone. 616 return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__date_time); 617 } 618 }; 619 620 template <class _Rep, class _Period, __fmt_char_type _CharT> 621 struct formatter<chrono::duration<_Rep, _Period>, _CharT> : public __formatter_chrono<_CharT> { 622 public: 623 using _Base = __formatter_chrono<_CharT>; 624 625 template <class _ParseContext> 626 _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) { 627 // [time.format]/1 628 // Giving a precision specification in the chrono-format-spec is valid only 629 // for std::chrono::duration types where the representation type Rep is a 630 // floating-point type. For all other Rep types, an exception of type 631 // format_error is thrown if the chrono-format-spec contains a precision 632 // specification. 633 // 634 // Note this doesn't refer to chrono::treat_as_floating_point_v<_Rep>. 635 if constexpr (std::floating_point<_Rep>) 636 return _Base::__parse(__ctx, __format_spec::__fields_chrono_fractional, __format_spec::__flags::__duration); 637 else 638 return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__duration); 639 } 640 }; 641 642 template <__fmt_char_type _CharT> 643 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::day, _CharT> : public __formatter_chrono<_CharT> { 644 public: 645 using _Base = __formatter_chrono<_CharT>; 646 647 template <class _ParseContext> 648 _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) { 649 return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__day); 650 } 651 }; 652 653 template <__fmt_char_type _CharT> 654 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::month, _CharT> : public __formatter_chrono<_CharT> { 655 public: 656 using _Base = __formatter_chrono<_CharT>; 657 658 template <class _ParseContext> 659 _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) { 660 return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__month); 661 } 662 }; 663 664 template <__fmt_char_type _CharT> 665 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::year, _CharT> : public __formatter_chrono<_CharT> { 666 public: 667 using _Base = __formatter_chrono<_CharT>; 668 669 template <class _ParseContext> 670 _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) { 671 return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__year); 672 } 673 }; 674 675 template <__fmt_char_type _CharT> 676 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::weekday, _CharT> : public __formatter_chrono<_CharT> { 677 public: 678 using _Base = __formatter_chrono<_CharT>; 679 680 template <class _ParseContext> 681 _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) { 682 return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__weekday); 683 } 684 }; 685 686 template <__fmt_char_type _CharT> 687 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::weekday_indexed, _CharT> : public __formatter_chrono<_CharT> { 688 public: 689 using _Base = __formatter_chrono<_CharT>; 690 691 template <class _ParseContext> 692 _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) { 693 return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__weekday); 694 } 695 }; 696 697 template <__fmt_char_type _CharT> 698 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::weekday_last, _CharT> : public __formatter_chrono<_CharT> { 699 public: 700 using _Base = __formatter_chrono<_CharT>; 701 702 template <class _ParseContext> 703 _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) { 704 return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__weekday); 705 } 706 }; 707 708 template <__fmt_char_type _CharT> 709 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::month_day, _CharT> : public __formatter_chrono<_CharT> { 710 public: 711 using _Base = __formatter_chrono<_CharT>; 712 713 template <class _ParseContext> 714 _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) { 715 return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__month_day); 716 } 717 }; 718 719 template <__fmt_char_type _CharT> 720 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::month_day_last, _CharT> : public __formatter_chrono<_CharT> { 721 public: 722 using _Base = __formatter_chrono<_CharT>; 723 724 template <class _ParseContext> 725 _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) { 726 return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__month); 727 } 728 }; 729 730 template <__fmt_char_type _CharT> 731 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::month_weekday, _CharT> : public __formatter_chrono<_CharT> { 732 public: 733 using _Base = __formatter_chrono<_CharT>; 734 735 template <class _ParseContext> 736 _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) { 737 return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__month_weekday); 738 } 739 }; 740 741 template <__fmt_char_type _CharT> 742 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::month_weekday_last, _CharT> : public __formatter_chrono<_CharT> { 743 public: 744 using _Base = __formatter_chrono<_CharT>; 745 746 template <class _ParseContext> 747 _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) { 748 return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__month_weekday); 749 } 750 }; 751 752 template <__fmt_char_type _CharT> 753 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::year_month, _CharT> : public __formatter_chrono<_CharT> { 754 public: 755 using _Base = __formatter_chrono<_CharT>; 756 757 template <class _ParseContext> 758 _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) { 759 return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__year_month); 760 } 761 }; 762 763 template <__fmt_char_type _CharT> 764 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::year_month_day, _CharT> : public __formatter_chrono<_CharT> { 765 public: 766 using _Base = __formatter_chrono<_CharT>; 767 768 template <class _ParseContext> 769 _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) { 770 return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__date); 771 } 772 }; 773 774 template <__fmt_char_type _CharT> 775 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::year_month_day_last, _CharT> : public __formatter_chrono<_CharT> { 776 public: 777 using _Base = __formatter_chrono<_CharT>; 778 779 template <class _ParseContext> 780 _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) { 781 return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__date); 782 } 783 }; 784 785 template <__fmt_char_type _CharT> 786 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::year_month_weekday, _CharT> : public __formatter_chrono<_CharT> { 787 public: 788 using _Base = __formatter_chrono<_CharT>; 789 790 template <class _ParseContext> 791 _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) { 792 return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__date); 793 } 794 }; 795 796 template <__fmt_char_type _CharT> 797 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::year_month_weekday_last, _CharT> : public __formatter_chrono<_CharT> { 798 public: 799 using _Base = __formatter_chrono<_CharT>; 800 801 template <class _ParseContext> 802 _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) { 803 return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__date); 804 } 805 }; 806 807 template <class _Duration, __fmt_char_type _CharT> 808 struct formatter<chrono::hh_mm_ss<_Duration>, _CharT> : public __formatter_chrono<_CharT> { 809 public: 810 using _Base = __formatter_chrono<_CharT>; 811 812 template <class _ParseContext> 813 _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) { 814 return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__time); 815 } 816 }; 817 #endif // if _LIBCPP_STD_VER >= 20 818 819 _LIBCPP_END_NAMESPACE_STD 820 821 #endif // _LIBCPP___CHRONO_FORMATTER_H 822