1 //===----------------------------------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 // UNSUPPORTED: c++03, c++11, c++14, c++17 10 // UNSUPPORTED: no-localization 11 // UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME 12 13 // XFAIL: availability-fp_to_chars-missing 14 15 // REQUIRES: locale.fr_FR.UTF-8 16 // REQUIRES: locale.ja_JP.UTF-8 17 18 // <chrono> 19 20 // template<class Rep, class Period, class charT> 21 // struct formatter<chrono::hh_mm_ss<duration<Rep, Period>>, charT>; 22 23 #include <chrono> 24 #include <format> 25 26 #include <cassert> 27 #include <concepts> 28 #include <locale> 29 #include <iostream> 30 #include <ratio> 31 #include <type_traits> 32 33 #include "formatter_tests.h" 34 #include "make_string.h" 35 #include "platform_support.h" // locale name macros 36 #include "string_literal.h" 37 #include "test_macros.h" 38 39 template <class CharT> 40 static void test_no_chrono_specs() { 41 using namespace std::literals::chrono_literals; 42 43 std::locale::global(std::locale(LOCALE_fr_FR_UTF_8)); 44 45 // Non localized output 46 check(SV("00:00:00.000"), SV("{}"), std::chrono::hh_mm_ss{0ms}); 47 check(SV("*00:00:00.000*"), SV("{:*^14}"), std::chrono::hh_mm_ss{0ms}); 48 check(SV("*00:00:00.000"), SV("{:*>13}"), std::chrono::hh_mm_ss{0ms}); 49 50 std::locale::global(std::locale::classic()); 51 } 52 53 template <class CharT> 54 static void test_valid_values() { 55 using namespace std::literals::chrono_literals; 56 57 constexpr std::basic_string_view<CharT> fmt = SV( 58 "{:" 59 "%%H='%H'%t" 60 "%%OH='%OH'%t" 61 "%%I='%I'%t" 62 "%%OI='%OI'%t" 63 "%%M='%M'%t" 64 "%%OM='%OM'%t" 65 "%%S='%S'%t" 66 "%%OS='%OS'%t" 67 "%%p='%p'%t" 68 "%%R='%R'%t" 69 "%%T='%T'%t" 70 "%%r='%r'%t" 71 "%%X='%X'%t" 72 "%%EX='%EX'%t" 73 "%n}"); 74 constexpr std::basic_string_view<CharT> lfmt = SV( 75 "{:L" 76 "%%H='%H'%t" 77 "%%OH='%OH'%t" 78 "%%I='%I'%t" 79 "%%OI='%OI'%t" 80 "%%M='%M'%t" 81 "%%OM='%OM'%t" 82 "%%S='%S'%t" 83 "%%OS='%OS'%t" 84 "%%p='%p'%t" 85 "%%R='%R'%t" 86 "%%T='%T'%t" 87 "%%r='%r'%t" 88 "%%X='%X'%t" 89 "%%EX='%EX'%t" 90 "%n}"); 91 92 const std::locale loc(LOCALE_ja_JP_UTF_8); 93 std::locale::global(std::locale(LOCALE_fr_FR_UTF_8)); 94 95 // Non localized output using C-locale 96 check(SV("%H='00'\t" 97 "%OH='00'\t" 98 "%I='12'\t" 99 "%OI='12'\t" 100 "%M='00'\t" 101 "%OM='00'\t" 102 "%S='00'\t" 103 "%OS='00'\t" 104 "%p='AM'\t" 105 "%R='00:00'\t" 106 "%T='00:00:00'\t" 107 "%r='12:00:00 AM'\t" 108 "%X='00:00:00'\t" 109 "%EX='00:00:00'\t" 110 "\n"), 111 fmt, 112 std::chrono::hh_mm_ss(0s)); 113 114 check(SV("%H='23'\t" 115 "%OH='23'\t" 116 "%I='11'\t" 117 "%OI='11'\t" 118 "%M='31'\t" 119 "%OM='31'\t" 120 "%S='30.123'\t" 121 "%OS='30.123'\t" 122 "%p='PM'\t" 123 "%R='23:31'\t" 124 "%T='23:31:30.123'\t" 125 "%r='11:31:30 PM'\t" 126 "%X='23:31:30'\t" 127 "%EX='23:31:30'\t" 128 "\n"), 129 fmt, 130 std::chrono::hh_mm_ss(23h + 31min + 30s + 123ms)); 131 132 check(SV("-%H='03'\t" 133 "%OH='03'\t" 134 "%I='03'\t" 135 "%OI='03'\t" 136 "%M='02'\t" 137 "%OM='02'\t" 138 "%S='01.123456789012'\t" 139 "%OS='01.123456789012'\t" 140 "%p='AM'\t" 141 "%R='03:02'\t" 142 "%T='03:02:01.123456789012'\t" 143 "%r='03:02:01 AM'\t" 144 "%X='03:02:01'\t" 145 "%EX='03:02:01'\t" 146 "\n"), 147 fmt, 148 std::chrono::hh_mm_ss(-(3h + 2min + 1s + std::chrono::duration<std::int64_t, std::pico>(123456789012)))); 149 150 // The number of fractional seconds is 0 according to the Standard 151 // TODO FMT Determine what to do. 152 check(SV("%H='01'\t" 153 "%OH='01'\t" 154 "%I='01'\t" 155 "%OI='01'\t" 156 "%M='01'\t" 157 "%OM='01'\t" 158 "%S='01'\t" 159 "%OS='01'\t" 160 "%p='AM'\t" 161 "%R='01:01'\t" 162 "%T='01:01:01'\t" 163 "%r='01:01:01 AM'\t" 164 "%X='01:01:01'\t" 165 "%EX='01:01:01'\t" 166 "\n"), 167 fmt, 168 std::chrono::hh_mm_ss(std::chrono::duration<double>(3661.123456))); 169 170 // Use the global locale (fr_FR) 171 check(SV("%H='00'\t" 172 "%OH='00'\t" 173 "%I='12'\t" 174 "%OI='12'\t" 175 "%M='00'\t" 176 "%OM='00'\t" 177 "%S='00'\t" 178 "%OS='00'\t" 179 #if defined(_AIX) 180 "%p='AM'\t" 181 #else 182 "%p=''\t" 183 #endif 184 "%R='00:00'\t" 185 "%T='00:00:00'\t" 186 #ifdef _WIN32 187 "%r='00:00:00'\t" 188 #elif defined(_AIX) 189 "%r='12:00:00 AM'\t" 190 #elif defined(__APPLE__) || defined(__FreeBSD__) 191 "%r=''\t" 192 #else 193 "%r='12:00:00 '\t" 194 #endif 195 "%X='00:00:00'\t" 196 "%EX='00:00:00'\t" 197 "\n"), 198 lfmt, 199 std::chrono::hh_mm_ss(0s)); 200 201 check(SV("%H='23'\t" 202 "%OH='23'\t" 203 "%I='11'\t" 204 "%OI='11'\t" 205 "%M='31'\t" 206 "%OM='31'\t" 207 "%S='30,123'\t" 208 "%OS='30,123'\t" 209 #if defined(_AIX) 210 "%p='PM'\t" 211 #else 212 "%p=''\t" 213 #endif 214 "%R='23:31'\t" 215 "%T='23:31:30,123'\t" 216 #ifdef _WIN32 217 "%r='23:31:30'\t" 218 #elif defined(_AIX) 219 "%r='11:31:30 PM'\t" 220 #elif defined(__APPLE__) || defined(__FreeBSD__) 221 "%r=''\t" 222 #else 223 "%r='11:31:30 '\t" 224 #endif 225 "%X='23:31:30'\t" 226 "%EX='23:31:30'\t" 227 "\n"), 228 lfmt, 229 std::chrono::hh_mm_ss(23h + 31min + 30s + 123ms)); 230 231 check(SV("-%H='03'\t" 232 "%OH='03'\t" 233 "%I='03'\t" 234 "%OI='03'\t" 235 "%M='02'\t" 236 "%OM='02'\t" 237 "%S='01,123456789012'\t" 238 "%OS='01,123456789012'\t" 239 #if defined(_AIX) 240 "%p='AM'\t" 241 #else 242 "%p=''\t" 243 #endif 244 "%R='03:02'\t" 245 "%T='03:02:01,123456789012'\t" 246 #ifdef _WIN32 247 "%r='03:02:01'\t" 248 #elif defined(_AIX) 249 "%r='03:02:01 AM'\t" 250 #elif defined(__APPLE__) || defined(__FreeBSD__) 251 "%r=''\t" 252 #else 253 "%r='03:02:01 '\t" 254 #endif 255 "%X='03:02:01'\t" 256 "%EX='03:02:01'\t" 257 "\n"), 258 lfmt, 259 std::chrono::hh_mm_ss(-(3h + 2min + 1s + std::chrono::duration<std::int64_t, std::pico>(123456789012)))); 260 261 check(SV("%H='01'\t" 262 "%OH='01'\t" 263 "%I='01'\t" 264 "%OI='01'\t" 265 "%M='01'\t" 266 "%OM='01'\t" 267 "%S='01'\t" 268 "%OS='01'\t" 269 #if defined(_AIX) 270 "%p='AM'\t" 271 #else 272 "%p=''\t" 273 #endif 274 "%R='01:01'\t" 275 "%T='01:01:01'\t" 276 #ifdef _WIN32 277 "%r='01:01:01'\t" 278 #elif defined(_AIX) 279 "%r='01:01:01 AM'\t" 280 #elif defined(__APPLE__) || defined(__FreeBSD__) 281 "%r=''\t" 282 #else 283 "%r='01:01:01 '\t" 284 #endif 285 "%X='01:01:01'\t" 286 "%EX='01:01:01'\t" 287 "\n"), 288 lfmt, 289 std::chrono::hh_mm_ss(std::chrono::duration<double>(3661.123456))); 290 291 // Use supplied locale (ja_JP). This locale has a different alternate. 292 #if defined(__APPLE__) || defined(_AIX) || defined(_WIN32) || defined(__FreeBSD__) 293 check(loc, 294 SV("%H='00'\t" 295 "%OH='00'\t" 296 "%I='12'\t" 297 "%OI='12'\t" 298 "%M='00'\t" 299 "%OM='00'\t" 300 "%S='00'\t" 301 "%OS='00'\t" 302 # if defined(__APPLE__) 303 "%p='AM'\t" 304 # else 305 "%p='午前'\t" 306 # endif 307 "%R='00:00'\t" 308 "%T='00:00:00'\t" 309 # if defined(__APPLE__) || defined(__FreeBSD__) 310 # if defined(__APPLE__) 311 "%r='12:00:00 AM'\t" 312 # else 313 "%r='12:00:00 午前'\t" 314 # endif 315 "%X='00時00分00秒'\t" 316 "%EX='00時00分00秒'\t" 317 # elif defined(_WIN32) 318 "%r='0:00:00'\t" 319 "%X='0:00:00'\t" 320 "%EX='0:00:00'\t" 321 # else 322 "%r='午前12:00:00'\t" 323 "%X='00:00:00'\t" 324 "%EX='00:00:00'\t" 325 # endif 326 "\n"), 327 lfmt, 328 std::chrono::hh_mm_ss(0s)); 329 330 check(loc, 331 SV("%H='23'\t" 332 "%OH='23'\t" 333 "%I='11'\t" 334 "%OI='11'\t" 335 "%M='31'\t" 336 "%OM='31'\t" 337 "%S='30.123'\t" 338 "%OS='30.123'\t" 339 # if defined(__APPLE__) 340 "%p='PM'\t" 341 # else 342 "%p='午後'\t" 343 # endif 344 "%R='23:31'\t" 345 "%T='23:31:30.123'\t" 346 # if defined(__APPLE__) || defined(__FreeBSD__) 347 # if defined(__APPLE__) 348 "%r='11:31:30 PM'\t" 349 # else 350 "%r='11:31:30 午後'\t" 351 # endif 352 "%X='23時31分30秒'\t" 353 "%EX='23時31分30秒'\t" 354 # elif defined(_WIN32) 355 "%r='23:31:30'\t" 356 "%X='23:31:30'\t" 357 "%EX='23:31:30'\t" 358 # else 359 "%r='午後11:31:30'\t" 360 "%X='23:31:30'\t" 361 "%EX='23:31:30'\t" 362 # endif 363 "\n"), 364 lfmt, 365 std::chrono::hh_mm_ss(23h + 31min + 30s + 123ms)); 366 367 check(loc, 368 SV("-%H='03'\t" 369 "%OH='03'\t" 370 "%I='03'\t" 371 "%OI='03'\t" 372 "%M='02'\t" 373 "%OM='02'\t" 374 "%S='01.123456789012'\t" 375 "%OS='01.123456789012'\t" 376 # if defined(__APPLE__) 377 "%p='AM'\t" 378 # else 379 "%p='午前'\t" 380 # endif 381 "%R='03:02'\t" 382 "%T='03:02:01.123456789012'\t" 383 # if defined(__APPLE__) || defined(__FreeBSD__) 384 # if defined(__APPLE__) 385 "%r='03:02:01 AM'\t" 386 # else 387 "%r='03:02:01 午前'\t" 388 # endif 389 "%X='03時02分01秒'\t" 390 "%EX='03時02分01秒'\t" 391 # elif defined(_WIN32) 392 "%r='3:02:01'\t" 393 "%X='3:02:01'\t" 394 "%EX='3:02:01'\t" 395 # else 396 "%r='午前03:02:01'\t" 397 "%X='03:02:01'\t" 398 "%EX='03:02:01'\t" 399 # endif 400 "\n"), 401 lfmt, 402 std::chrono::hh_mm_ss(-(3h + 2min + 1s + std::chrono::duration<std::int64_t, std::pico>(123456789012)))); 403 404 check(loc, 405 SV("%H='01'\t" 406 "%OH='01'\t" 407 "%I='01'\t" 408 "%OI='01'\t" 409 "%M='01'\t" 410 "%OM='01'\t" 411 "%S='01'\t" 412 "%OS='01'\t" 413 # if defined(__APPLE__) 414 "%p='AM'\t" 415 # else 416 "%p='午前'\t" 417 # endif 418 "%R='01:01'\t" 419 "%T='01:01:01'\t" 420 # if defined(__APPLE__) || defined(__FreeBSD__) 421 # if defined(__APPLE__) 422 "%r='01:01:01 AM'\t" 423 # else 424 "%r='01:01:01 午前'\t" 425 # endif 426 "%X='01時01分01秒'\t" 427 "%EX='01時01分01秒'\t" 428 # elif defined(_WIN32) 429 "%r='1:01:01'\t" 430 "%X='1:01:01'\t" 431 "%EX='1:01:01'\t" 432 # else 433 "%r='午前01:01:01'\t" 434 "%X='01:01:01'\t" 435 "%EX='01:01:01'\t" 436 # endif 437 "\n"), 438 lfmt, 439 std::chrono::hh_mm_ss(std::chrono::duration<double>(3661.123456))); 440 #else // defined(__APPLE__) || defined(_AIX) || defined(_WIN32) 441 check(loc, 442 SV("%H='00'\t" 443 "%OH='〇'\t" 444 "%I='12'\t" 445 "%OI='十二'\t" 446 "%M='00'\t" 447 "%OM='〇'\t" 448 "%S='00'\t" 449 "%OS='〇'\t" 450 "%p='午前'\t" 451 "%R='00:00'\t" 452 "%T='00:00:00'\t" 453 "%r='午前12時00分00秒'\t" 454 "%X='00時00分00秒'\t" 455 "%EX='00時00分00秒'\t" 456 "\n"), 457 lfmt, 458 std::chrono::hh_mm_ss(0s)); 459 460 // TODO FMT What should fractions be in alternate display mode? 461 check(loc, 462 SV("%H='23'\t" 463 "%OH='二十三'\t" 464 "%I='11'\t" 465 "%OI='十一'\t" 466 "%M='31'\t" 467 "%OM='三十一'\t" 468 "%S='30.123'\t" 469 "%OS='三十.123'\t" 470 "%p='午後'\t" 471 "%R='23:31'\t" 472 "%T='23:31:30.123'\t" 473 "%r='午後11時31分30秒'\t" 474 "%X='23時31分30秒'\t" 475 "%EX='23時31分30秒'\t" 476 "\n"), 477 lfmt, 478 std::chrono::hh_mm_ss(23h + 31min + 30s + 123ms)); 479 480 check(loc, 481 SV("-%H='03'\t" 482 "%OH='三'\t" 483 "%I='03'\t" 484 "%OI='三'\t" 485 "%M='02'\t" 486 "%OM='二'\t" 487 "%S='01.123456789012'\t" 488 "%OS='一.123456789012'\t" 489 "%p='午前'\t" 490 "%R='03:02'\t" 491 "%T='03:02:01.123456789012'\t" 492 "%r='午前03時02分01秒'\t" 493 "%X='03時02分01秒'\t" 494 "%EX='03時02分01秒'\t" 495 "\n"), 496 lfmt, 497 std::chrono::hh_mm_ss(-(3h + 2min + 1s + std::chrono::duration<std::int64_t, std::pico>(123456789012)))); 498 499 check(loc, 500 SV("%H='01'\t" 501 "%OH='一'\t" 502 "%I='01'\t" 503 "%OI='一'\t" 504 "%M='01'\t" 505 "%OM='一'\t" 506 "%S='01'\t" 507 "%OS='一'\t" 508 "%p='午前'\t" 509 "%R='01:01'\t" 510 "%T='01:01:01'\t" 511 "%r='午前01時01分01秒'\t" 512 "%X='01時01分01秒'\t" 513 "%EX='01時01分01秒'\t" 514 "\n"), 515 lfmt, 516 std::chrono::hh_mm_ss(std::chrono::duration<double>(3661.123456))); 517 #endif // defined(__APPLE__) || defined(_AIX) || defined(_WIN32) 518 519 std::locale::global(std::locale::classic()); 520 } 521 522 template <class CharT> 523 static void test_invalid_values() { 524 using namespace std::literals::chrono_literals; 525 526 // This looks odd, however the 24 hours is not valid for a 24 hour clock. 527 // TODO FMT discuss what the "proper" behaviour is. 528 check_exception("Formatting a hour needs a valid value", SV("{:%H"), std::chrono::hh_mm_ss{24h}); 529 check_exception("Formatting a hour needs a valid value", SV("{:%OH"), std::chrono::hh_mm_ss{24h}); 530 check_exception("Formatting a hour needs a valid value", SV("{:%I"), std::chrono::hh_mm_ss{24h}); 531 check_exception("Formatting a hour needs a valid value", SV("{:%OI"), std::chrono::hh_mm_ss{24h}); 532 check(SV("00"), SV("{:%M}"), std::chrono::hh_mm_ss{24h}); 533 check(SV("00"), SV("{:%OM}"), std::chrono::hh_mm_ss{24h}); 534 check(SV("00"), SV("{:%S}"), std::chrono::hh_mm_ss{24h}); 535 check(SV("00"), SV("{:%OS}"), std::chrono::hh_mm_ss{24h}); 536 check_exception("Formatting a hour needs a valid value", SV("{:%p"), std::chrono::hh_mm_ss{24h}); 537 check_exception("Formatting a hour needs a valid value", SV("{:%R"), std::chrono::hh_mm_ss{24h}); 538 check_exception("Formatting a hour needs a valid value", SV("{:%T"), std::chrono::hh_mm_ss{24h}); 539 check_exception("Formatting a hour needs a valid value", SV("{:%r"), std::chrono::hh_mm_ss{24h}); 540 check_exception("Formatting a hour needs a valid value", SV("{:%X"), std::chrono::hh_mm_ss{24h}); 541 check_exception("Formatting a hour needs a valid value", SV("{:%EX"), std::chrono::hh_mm_ss{24h}); 542 } 543 544 template <class CharT> 545 static void test() { 546 using namespace std::literals::chrono_literals; 547 548 test_no_chrono_specs<CharT>(); 549 test_valid_values<CharT>(); 550 test_invalid_values<CharT>(); 551 check_invalid_types<CharT>( 552 {SV("H"), 553 SV("I"), 554 SV("M"), 555 SV("S"), 556 SV("p"), 557 SV("r"), 558 SV("R"), 559 SV("T"), 560 SV("X"), 561 SV("OH"), 562 SV("OI"), 563 SV("OM"), 564 SV("OS"), 565 SV("EX")}, 566 std::chrono::hh_mm_ss{0ms}); 567 568 check_exception("The format specifier expects a '%' or a '}'", SV("{:A"), std::chrono::hh_mm_ss{0ms}); 569 check_exception("The chrono specifiers contain a '{'", SV("{:%%{"), std::chrono::hh_mm_ss{0ms}); 570 check_exception("End of input while parsing a conversion specifier", SV("{:%"), std::chrono::hh_mm_ss{0ms}); 571 check_exception("End of input while parsing the modifier E", SV("{:%E"), std::chrono::hh_mm_ss{0ms}); 572 check_exception("End of input while parsing the modifier O", SV("{:%O"), std::chrono::hh_mm_ss{0ms}); 573 574 check_exception("The format specifier expects a '%' or a '}'", SV("{:.3}"), std::chrono::hh_mm_ss{0ms}); 575 } 576 577 int main(int, char**) { 578 test<char>(); 579 580 #ifndef TEST_HAS_NO_WIDE_CHARACTERS 581 test<wchar_t>(); 582 #endif 583 584 return 0; 585 } 586