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