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({__sstr}, __sstr, _CharT(' '), std::addressof(__t), std::to_address(__s), std::to_address(__it + 1)); 202 } break; 203 204 case _CharT('j'): 205 if constexpr (chrono::__is_duration<_Tp>::value) 206 // Converting a duration where the period has a small ratio to days 207 // may fail to compile. This due to loss of precision in the 208 // conversion. In order to avoid that issue convert to seconds as 209 // an intemediate step. 210 __sstr << chrono::duration_cast<chrono::days>(chrono::duration_cast<chrono::seconds>(__value)).count(); 211 else 212 __facet.put({__sstr}, __sstr, _CharT(' '), std::addressof(__t), std::to_address(__s), std::to_address(__it + 1)); 213 break; 214 215 case _CharT('q'): 216 if constexpr (chrono::__is_duration<_Tp>::value) { 217 __sstr << chrono::__units_suffix<_CharT, typename _Tp::period>(); 218 break; 219 } 220 __builtin_unreachable(); 221 222 case _CharT('Q'): 223 // TODO FMT Determine the proper ideas 224 // - Should it honour the precision? 225 // - Shoult it honour the locale setting for the separators? 226 // The wording for Q doesn't use the word locale and the effect of 227 // precision is unspecified. 228 // 229 // MSVC STL ignores precision but uses separator 230 // FMT honours precision and has a bug for separator 231 // https://godbolt.org/z/78b7sMxns 232 if constexpr (chrono::__is_duration<_Tp>::value) { 233 __sstr << std::format(_LIBCPP_STATICALLY_WIDEN(_CharT, "{}"), __value.count()); 234 break; 235 } 236 __builtin_unreachable(); 237 238 case _CharT('S'): 239 case _CharT('T'): 240 __facet.put({__sstr}, __sstr, _CharT(' '), std::addressof(__t), std::to_address(__s), std::to_address(__it + 1)); 241 if constexpr (__use_fraction<_Tp>()) 242 __formatter::__format_sub_seconds(__value, __sstr); 243 break; 244 245 // Unlike time_put and strftime the formatting library requires %Y 246 // 247 // [tab:time.format.spec] 248 // The year as a decimal number. If the result is less than four digits 249 // it is left-padded with 0 to four digits. 250 // 251 // This means years in the range (-1000, 1000) need manual formatting. 252 // It's unclear whether %EY needs the same treatment. For example the 253 // Japanese EY contains the era name and year. This is zero-padded to 2 254 // digits in time_put (note that older glibc versions didn't do 255 // padding.) However most eras won't reach 100 years, let alone 1000. 256 // So padding to 4 digits seems unwanted for Japanese. 257 // 258 // The same applies to %Ex since that too depends on the era. 259 // 260 // %x the locale's date representation is currently doesn't handle the 261 // zero-padding too. 262 // 263 // The 4 digits can be implemented better at a later time. On POSIX 264 // systems the required information can be extracted by nl_langinfo 265 // https://man7.org/linux/man-pages/man3/nl_langinfo.3.html 266 // 267 // Note since year < -1000 is expected to be rare it uses the more 268 // expensive year routine. 269 // 270 // TODO FMT evaluate the comment above. 271 272 # if defined(__GLIBC__) || defined(_AIX) || defined(_WIN32) 273 case _CharT('y'): 274 // Glibc fails for negative values, AIX for positive values too. 275 __sstr << std::format(_LIBCPP_STATICALLY_WIDEN(_CharT, "{:02}"), (std::abs(__t.tm_year + 1900)) % 100); 276 break; 277 # endif // defined(__GLIBC__) || defined(_AIX) || defined(_WIN32) 278 279 case _CharT('Y'): 280 // Depending on the platform's libc the range of supported years is 281 // limited. Intead of of testing all conditions use the internal 282 // implementation unconditionally. 283 __formatter::__format_year(__t.tm_year + 1900, __sstr); 284 break; 285 286 case _CharT('F'): { 287 int __year = __t.tm_year + 1900; 288 if (__year < 1000) { 289 __formatter::__format_year(__year, __sstr); 290 __sstr << std::format(_LIBCPP_STATICALLY_WIDEN(_CharT, "-{:02}-{:02}"), __t.tm_mon + 1, __t.tm_mday); 291 } else 292 __facet.put({__sstr}, __sstr, _CharT(' '), std::addressof(__t), std::to_address(__s), std::to_address(__it + 1)); 293 } break; 294 295 case _CharT('Z'): 296 // TODO FMT Add proper timezone support. 297 __sstr << _LIBCPP_STATICALLY_WIDEN(_CharT, "UTC"); 298 break; 299 300 case _CharT('O'): 301 if constexpr (__use_fraction<_Tp>()) { 302 // Handle OS using the normal representation for the non-fractional 303 // part. There seems to be no locale information regarding how the 304 // fractional part should be formatted. 305 if (*(__it + 1) == 'S') { 306 ++__it; 307 __facet.put({__sstr}, __sstr, _CharT(' '), std::addressof(__t), std::to_address(__s), std::to_address(__it + 1)); 308 __formatter::__format_sub_seconds(__value, __sstr); 309 break; 310 } 311 } 312 [[fallthrough]]; 313 case _CharT('E'): 314 ++__it; 315 [[fallthrough]]; 316 default: 317 __facet.put({__sstr}, __sstr, _CharT(' '), std::addressof(__t), std::to_address(__s), std::to_address(__it + 1)); 318 break; 319 } 320 } else { 321 __sstr << *__it; 322 } 323 } 324 } 325 326 template <class _Tp> 327 _LIBCPP_HIDE_FROM_ABI constexpr bool __weekday_ok(const _Tp& __value) { 328 if constexpr (__is_time_point<_Tp>) 329 return true; 330 else if constexpr (same_as<_Tp, chrono::day>) 331 return true; 332 else if constexpr (same_as<_Tp, chrono::month>) 333 return __value.ok(); 334 else if constexpr (same_as<_Tp, chrono::year>) 335 return true; 336 else if constexpr (same_as<_Tp, chrono::weekday>) 337 return true; 338 else if constexpr (same_as<_Tp, chrono::weekday_indexed>) 339 return true; 340 else if constexpr (same_as<_Tp, chrono::weekday_last>) 341 return true; 342 else if constexpr (same_as<_Tp, chrono::month_day>) 343 return true; 344 else if constexpr (same_as<_Tp, chrono::month_day_last>) 345 return true; 346 else if constexpr (same_as<_Tp, chrono::month_weekday>) 347 return true; 348 else if constexpr (same_as<_Tp, chrono::month_weekday_last>) 349 return true; 350 else if constexpr (same_as<_Tp, chrono::year_month>) 351 return true; 352 else if constexpr (same_as<_Tp, chrono::year_month_day>) 353 return __value.ok(); 354 else if constexpr (same_as<_Tp, chrono::year_month_day_last>) 355 return __value.ok(); 356 else if constexpr (same_as<_Tp, chrono::year_month_weekday>) 357 return __value.weekday().ok(); 358 else if constexpr (same_as<_Tp, chrono::year_month_weekday_last>) 359 return __value.weekday().ok(); 360 else if constexpr (__is_hh_mm_ss<_Tp>) 361 return true; 362 else 363 static_assert(sizeof(_Tp) == 0, "Add the missing type specialization"); 364 } 365 366 template <class _Tp> 367 _LIBCPP_HIDE_FROM_ABI constexpr bool __weekday_name_ok(const _Tp& __value) { 368 if constexpr (__is_time_point<_Tp>) 369 return true; 370 else if constexpr (same_as<_Tp, chrono::day>) 371 return true; 372 else if constexpr (same_as<_Tp, chrono::month>) 373 return __value.ok(); 374 else if constexpr (same_as<_Tp, chrono::year>) 375 return true; 376 else if constexpr (same_as<_Tp, chrono::weekday>) 377 return __value.ok(); 378 else if constexpr (same_as<_Tp, chrono::weekday_indexed>) 379 return __value.weekday().ok(); 380 else if constexpr (same_as<_Tp, chrono::weekday_last>) 381 return __value.weekday().ok(); 382 else if constexpr (same_as<_Tp, chrono::month_day>) 383 return true; 384 else if constexpr (same_as<_Tp, chrono::month_day_last>) 385 return true; 386 else if constexpr (same_as<_Tp, chrono::month_weekday>) 387 return __value.weekday_indexed().ok(); 388 else if constexpr (same_as<_Tp, chrono::month_weekday_last>) 389 return __value.weekday_indexed().ok(); 390 else if constexpr (same_as<_Tp, chrono::year_month>) 391 return true; 392 else if constexpr (same_as<_Tp, chrono::year_month_day>) 393 return __value.ok(); 394 else if constexpr (same_as<_Tp, chrono::year_month_day_last>) 395 return __value.ok(); 396 else if constexpr (same_as<_Tp, chrono::year_month_weekday>) 397 return __value.weekday().ok(); 398 else if constexpr (same_as<_Tp, chrono::year_month_weekday_last>) 399 return __value.weekday().ok(); 400 else if constexpr (__is_hh_mm_ss<_Tp>) 401 return true; 402 else 403 static_assert(sizeof(_Tp) == 0, "Add the missing type specialization"); 404 } 405 406 template <class _Tp> 407 _LIBCPP_HIDE_FROM_ABI constexpr bool __date_ok(const _Tp& __value) { 408 if constexpr (__is_time_point<_Tp>) 409 return true; 410 else if constexpr (same_as<_Tp, chrono::day>) 411 return true; 412 else if constexpr (same_as<_Tp, chrono::month>) 413 return __value.ok(); 414 else if constexpr (same_as<_Tp, chrono::year>) 415 return true; 416 else if constexpr (same_as<_Tp, chrono::weekday>) 417 return true; 418 else if constexpr (same_as<_Tp, chrono::weekday_indexed>) 419 return true; 420 else if constexpr (same_as<_Tp, chrono::weekday_last>) 421 return true; 422 else if constexpr (same_as<_Tp, chrono::month_day>) 423 return true; 424 else if constexpr (same_as<_Tp, chrono::month_day_last>) 425 return true; 426 else if constexpr (same_as<_Tp, chrono::month_weekday>) 427 return true; 428 else if constexpr (same_as<_Tp, chrono::month_weekday_last>) 429 return true; 430 else if constexpr (same_as<_Tp, chrono::year_month>) 431 return true; 432 else if constexpr (same_as<_Tp, chrono::year_month_day>) 433 return __value.ok(); 434 else if constexpr (same_as<_Tp, chrono::year_month_day_last>) 435 return __value.ok(); 436 else if constexpr (same_as<_Tp, chrono::year_month_weekday>) 437 return __value.ok(); 438 else if constexpr (same_as<_Tp, chrono::year_month_weekday_last>) 439 return __value.ok(); 440 else if constexpr (__is_hh_mm_ss<_Tp>) 441 return true; 442 else 443 static_assert(sizeof(_Tp) == 0, "Add the missing type specialization"); 444 } 445 446 template <class _Tp> 447 _LIBCPP_HIDE_FROM_ABI constexpr bool __month_name_ok(const _Tp& __value) { 448 if constexpr (__is_time_point<_Tp>) 449 return true; 450 else if constexpr (same_as<_Tp, chrono::day>) 451 return true; 452 else if constexpr (same_as<_Tp, chrono::month>) 453 return __value.ok(); 454 else if constexpr (same_as<_Tp, chrono::year>) 455 return true; 456 else if constexpr (same_as<_Tp, chrono::weekday>) 457 return true; 458 else if constexpr (same_as<_Tp, chrono::weekday_indexed>) 459 return true; 460 else if constexpr (same_as<_Tp, chrono::weekday_last>) 461 return true; 462 else if constexpr (same_as<_Tp, chrono::month_day>) 463 return __value.month().ok(); 464 else if constexpr (same_as<_Tp, chrono::month_day_last>) 465 return __value.month().ok(); 466 else if constexpr (same_as<_Tp, chrono::month_weekday>) 467 return __value.month().ok(); 468 else if constexpr (same_as<_Tp, chrono::month_weekday_last>) 469 return __value.month().ok(); 470 else if constexpr (same_as<_Tp, chrono::year_month>) 471 return __value.month().ok(); 472 else if constexpr (same_as<_Tp, chrono::year_month_day>) 473 return __value.month().ok(); 474 else if constexpr (same_as<_Tp, chrono::year_month_day_last>) 475 return __value.month().ok(); 476 else if constexpr (same_as<_Tp, chrono::year_month_weekday>) 477 return __value.month().ok(); 478 else if constexpr (same_as<_Tp, chrono::year_month_weekday_last>) 479 return __value.month().ok(); 480 else if constexpr (__is_hh_mm_ss<_Tp>) 481 return true; 482 else 483 static_assert(sizeof(_Tp) == 0, "Add the missing type specialization"); 484 } 485 486 template <class _CharT, class _Tp, class _FormatContext> 487 _LIBCPP_HIDE_FROM_ABI auto 488 __format_chrono(const _Tp& __value, 489 _FormatContext& __ctx, 490 __format_spec::__parsed_specifications<_CharT> __specs, 491 basic_string_view<_CharT> __chrono_specs) { 492 basic_stringstream<_CharT> __sstr; 493 // [time.format]/2 494 // 2.1 - the "C" locale if the L option is not present in chrono-format-spec, otherwise 495 // 2.2 - the locale passed to the formatting function if any, otherwise 496 // 2.3 - the global locale. 497 // Note that the __ctx's locale() call does 2.2 and 2.3. 498 if (__specs.__chrono_.__locale_specific_form_) 499 __sstr.imbue(__ctx.locale()); 500 else 501 __sstr.imbue(locale::classic()); 502 503 if (__chrono_specs.empty()) 504 __sstr << __value; 505 else { 506 if constexpr (chrono::__is_duration<_Tp>::value) { 507 if (__value < __value.zero()) 508 __sstr << _CharT('-'); 509 __formatter::__format_chrono_using_chrono_specs(chrono::abs(__value), __sstr, __chrono_specs); 510 // TODO FMT When keeping the precision it will truncate the string. 511 // Note that the behaviour what the precision does isn't specified. 512 __specs.__precision_ = -1; 513 } else { 514 // Test __weekday_name_ before __weekday_ to give a better error. 515 if (__specs.__chrono_.__weekday_name_ && !__formatter::__weekday_name_ok(__value)) 516 std::__throw_format_error("Formatting a weekday name needs a valid weekday"); 517 518 if (__specs.__chrono_.__weekday_ && !__formatter::__weekday_ok(__value)) 519 std::__throw_format_error("Formatting a weekday needs a valid weekday"); 520 521 if (__specs.__chrono_.__day_of_year_ && !__formatter::__date_ok(__value)) 522 std::__throw_format_error("Formatting a day of year needs a valid date"); 523 524 if (__specs.__chrono_.__week_of_year_ && !__formatter::__date_ok(__value)) 525 std::__throw_format_error("Formatting a week of year needs a valid date"); 526 527 if (__specs.__chrono_.__month_name_ && !__formatter::__month_name_ok(__value)) 528 std::__throw_format_error("Formatting a month name from an invalid month number"); 529 530 if constexpr (__is_hh_mm_ss<_Tp>) { 531 // Note this is a pedantic intepretation of the Standard. A hh_mm_ss 532 // is no longer a time_of_day and can store an arbitrary number of 533 // hours. A number of hours in a 12 or 24 hour clock can't represent 534 // 24 hours or more. The functions std::chrono::make12 and 535 // std::chrono::make24 reaffirm this view point. 536 // 537 // Interestingly this will be the only output stream function that 538 // throws. 539 // 540 // TODO FMT The wording probably needs to be adapted to 541 // - The displayed hours is hh_mm_ss.hours() % 24 542 // - It should probably allow %j in the same fashion as duration. 543 // - The stream formatter should change its output when hours >= 24 544 // - Write it as not valid, 545 // - or write the number of days. 546 if (__specs.__chrono_.__hour_ && __value.hours().count() > 23) 547 std::__throw_format_error("Formatting a hour needs a valid value"); 548 549 if (__value.is_negative()) 550 __sstr << _CharT('-'); 551 } 552 553 __formatter::__format_chrono_using_chrono_specs(__value, __sstr, __chrono_specs); 554 } 555 } 556 557 return __formatter::__write_string(__sstr.view(), __ctx.out(), __specs); 558 } 559 560 } // namespace __formatter 561 562 template <__fmt_char_type _CharT> 563 struct _LIBCPP_TEMPLATE_VIS __formatter_chrono { 564 public: 565 template <class _ParseContext> 566 _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator 567 __parse(_ParseContext& __ctx, __format_spec::__fields __fields, __format_spec::__flags __flags) { 568 return __parser_.__parse(__ctx, __fields, __flags); 569 } 570 571 template <class _Tp, class _FormatContext> 572 _LIBCPP_HIDE_FROM_ABI typename _FormatContext::iterator format(const _Tp& __value, _FormatContext& __ctx) const { 573 return __formatter::__format_chrono( 574 __value, __ctx, __parser_.__parser_.__get_parsed_chrono_specifications(__ctx), __parser_.__chrono_specs_); 575 } 576 577 __format_spec::__parser_chrono<_CharT> __parser_; 578 }; 579 580 template <class _Duration, __fmt_char_type _CharT> 581 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::sys_time<_Duration>, _CharT> : public __formatter_chrono<_CharT> { 582 public: 583 using _Base = __formatter_chrono<_CharT>; 584 585 template <class _ParseContext> 586 _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) { 587 return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__clock); 588 } 589 }; 590 591 template <class _Duration, __fmt_char_type _CharT> 592 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::file_time<_Duration>, _CharT> : public __formatter_chrono<_CharT> { 593 public: 594 using _Base = __formatter_chrono<_CharT>; 595 596 template <class _ParseContext> 597 _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) { 598 return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__clock); 599 } 600 }; 601 602 template <class _Duration, __fmt_char_type _CharT> 603 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::local_time<_Duration>, _CharT> : public __formatter_chrono<_CharT> { 604 public: 605 using _Base = __formatter_chrono<_CharT>; 606 607 template <class _ParseContext> 608 _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) { 609 // The flags are not __clock since there is no associated time-zone. 610 return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__date_time); 611 } 612 }; 613 614 template <class _Rep, class _Period, __fmt_char_type _CharT> 615 struct formatter<chrono::duration<_Rep, _Period>, _CharT> : public __formatter_chrono<_CharT> { 616 public: 617 using _Base = __formatter_chrono<_CharT>; 618 619 template <class _ParseContext> 620 _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) { 621 // [time.format]/1 622 // Giving a precision specification in the chrono-format-spec is valid only 623 // for std::chrono::duration types where the representation type Rep is a 624 // floating-point type. For all other Rep types, an exception of type 625 // format_error is thrown if the chrono-format-spec contains a precision 626 // specification. 627 // 628 // Note this doesn't refer to chrono::treat_as_floating_point_v<_Rep>. 629 if constexpr (std::floating_point<_Rep>) 630 return _Base::__parse(__ctx, __format_spec::__fields_chrono_fractional, __format_spec::__flags::__duration); 631 else 632 return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__duration); 633 } 634 }; 635 636 template <__fmt_char_type _CharT> 637 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::day, _CharT> 638 : public __formatter_chrono<_CharT> { 639 public: 640 using _Base = __formatter_chrono<_CharT>; 641 642 template <class _ParseContext> 643 _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) { 644 return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__day); 645 } 646 }; 647 648 template <__fmt_char_type _CharT> 649 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::month, _CharT> 650 : public __formatter_chrono<_CharT> { 651 public: 652 using _Base = __formatter_chrono<_CharT>; 653 654 template <class _ParseContext> 655 _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) { 656 return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__month); 657 } 658 }; 659 660 template <__fmt_char_type _CharT> 661 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::year, _CharT> 662 : public __formatter_chrono<_CharT> { 663 public: 664 using _Base = __formatter_chrono<_CharT>; 665 666 template <class _ParseContext> 667 _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) { 668 return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__year); 669 } 670 }; 671 672 template <__fmt_char_type _CharT> 673 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::weekday, _CharT> 674 : public __formatter_chrono<_CharT> { 675 public: 676 using _Base = __formatter_chrono<_CharT>; 677 678 template <class _ParseContext> 679 _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) { 680 return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__weekday); 681 } 682 }; 683 684 template <__fmt_char_type _CharT> 685 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::weekday_indexed, _CharT> 686 : public __formatter_chrono<_CharT> { 687 public: 688 using _Base = __formatter_chrono<_CharT>; 689 690 template <class _ParseContext> 691 _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) { 692 return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__weekday); 693 } 694 }; 695 696 template <__fmt_char_type _CharT> 697 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::weekday_last, _CharT> 698 : 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> 710 : public __formatter_chrono<_CharT> { 711 public: 712 using _Base = __formatter_chrono<_CharT>; 713 714 template <class _ParseContext> 715 _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) { 716 return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__month_day); 717 } 718 }; 719 720 template <__fmt_char_type _CharT> 721 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::month_day_last, _CharT> 722 : public __formatter_chrono<_CharT> { 723 public: 724 using _Base = __formatter_chrono<_CharT>; 725 726 template <class _ParseContext> 727 _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) { 728 return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__month); 729 } 730 }; 731 732 template <__fmt_char_type _CharT> 733 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::month_weekday, _CharT> 734 : public __formatter_chrono<_CharT> { 735 public: 736 using _Base = __formatter_chrono<_CharT>; 737 738 template <class _ParseContext> 739 _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) { 740 return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__month_weekday); 741 } 742 }; 743 744 template <__fmt_char_type _CharT> 745 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::month_weekday_last, _CharT> 746 : public __formatter_chrono<_CharT> { 747 public: 748 using _Base = __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::__month_weekday); 753 } 754 }; 755 756 template <__fmt_char_type _CharT> 757 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::year_month, _CharT> 758 : public __formatter_chrono<_CharT> { 759 public: 760 using _Base = __formatter_chrono<_CharT>; 761 762 template <class _ParseContext> 763 _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) { 764 return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__year_month); 765 } 766 }; 767 768 template <__fmt_char_type _CharT> 769 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::year_month_day, _CharT> 770 : public __formatter_chrono<_CharT> { 771 public: 772 using _Base = __formatter_chrono<_CharT>; 773 774 template <class _ParseContext> 775 _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) { 776 return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__date); 777 } 778 }; 779 780 template <__fmt_char_type _CharT> 781 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::year_month_day_last, _CharT> 782 : public __formatter_chrono<_CharT> { 783 public: 784 using _Base = __formatter_chrono<_CharT>; 785 786 template <class _ParseContext> 787 _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) { 788 return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__date); 789 } 790 }; 791 792 template <__fmt_char_type _CharT> 793 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::year_month_weekday, _CharT> 794 : public __formatter_chrono<_CharT> { 795 public: 796 using _Base = __formatter_chrono<_CharT>; 797 798 template <class _ParseContext> 799 _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) { 800 return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__date); 801 } 802 }; 803 804 template <__fmt_char_type _CharT> 805 struct _LIBCPP_TEMPLATE_VIS formatter<chrono::year_month_weekday_last, _CharT> 806 : public __formatter_chrono<_CharT> { 807 public: 808 using _Base = __formatter_chrono<_CharT>; 809 810 template <class _ParseContext> 811 _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) { 812 return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__date); 813 } 814 }; 815 816 template <class _Duration, __fmt_char_type _CharT> 817 struct formatter<chrono::hh_mm_ss<_Duration>, _CharT> : public __formatter_chrono<_CharT> { 818 public: 819 using _Base = __formatter_chrono<_CharT>; 820 821 template <class _ParseContext> 822 _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) { 823 return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__time); 824 } 825 }; 826 #endif // if _LIBCPP_STD_VER >= 20 827 828 _LIBCPP_END_NAMESPACE_STD 829 830 #endif // _LIBCPP___CHRONO_FORMATTER_H 831