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