Changeset 267108 in webkit
- Timestamp:
- Sep 15, 2020 3:56:32 PM (4 years ago)
- Location:
- trunk
- Files:
-
- 1 added
- 6 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/JSTests/ChangeLog
r267102 r267108 1 2020-09-15 Yusuke Suzuki <ysuzuki@apple.com> 2 3 [JSC] Apply Intl.DateTimeFormat hour-cycle correctly when timeStyle is used 4 https://bugs.webkit.org/show_bug.cgi?id=216521 5 6 Reviewed by Ross Kirsling. 7 8 JSTests/stress/intl-date-time-format-date-time-style-hour-cycle.js result is now matching against V8 and SpiderMonkey, 9 and this new behavior is more reasonable. 10 11 * stress/intl-date-time-format-date-time-style-hour-cycle.js: 12 (shouldBe.o.format): 13 (shouldBe): 14 * stress/intl-datetimeformat-hour-cycle.js: Added. 15 (shouldBe): 16 (throw.new.Error): 17 * test262/expectations.yaml: 18 1 19 2020-09-14 Yusuke Suzuki <ysuzuki@apple.com> 2 20 -
trunk/JSTests/stress/intl-date-time-format-date-time-style-hour-cycle.js
r266035 r267108 6 6 let now1 = 1592870440000; 7 7 let now2 = 1592827240000; 8 9 { 10 let o = new Intl.DateTimeFormat("en" , { 11 timeStyle: "short", 12 timeZone: "UTC", 13 }); 14 shouldBe(o.format(now1), `12:00 AM`); 15 } 16 8 17 { 9 18 let o = new Intl.DateTimeFormat("en" , { … … 12 21 hourCycle: "h23", 13 22 }); 14 shouldBe(o.format(now1), `0 :00 AM`);23 shouldBe(o.format(now1), `00:00`); 15 24 } 16 25 … … 21 30 hourCycle: "h24", 22 31 }); 23 shouldBe(o.format(now1), `24:00 AM`);32 shouldBe(o.format(now1), `24:00`); 24 33 } 25 34 … … 46 55 timeStyle: "short", 47 56 timeZone: "UTC", 48 hourCycle: "h23", 57 hour12: true, 58 }); 59 shouldBe(o.format(now1), `12:00 AM`); 60 } 61 62 { 63 let o = new Intl.DateTimeFormat("en" , { 64 timeStyle: "short", 65 timeZone: "UTC", 66 hour12: false, 67 }); 68 shouldBe(o.format(now1), `00:00`); 69 } 70 71 { 72 let o = new Intl.DateTimeFormat("en" , { 73 timeStyle: "short", 74 timeZone: "UTC", 49 75 }); 50 76 shouldBe(o.format(now2), `12:00 PM`); … … 55 81 timeStyle: "short", 56 82 timeZone: "UTC", 83 hourCycle: "h23", 84 }); 85 shouldBe(o.format(now2), `12:00`); 86 } 87 88 { 89 let o = new Intl.DateTimeFormat("en" , { 90 timeStyle: "short", 91 timeZone: "UTC", 57 92 hourCycle: "h24", 58 93 }); 59 shouldBe(o.format(now2), `12:00 PM`);94 shouldBe(o.format(now2), `12:00`); 60 95 } 61 96 … … 77 112 shouldBe(o.format(now2), `12:00 PM`); 78 113 } 114 115 { 116 let o = new Intl.DateTimeFormat("en" , { 117 timeStyle: "short", 118 timeZone: "UTC", 119 hour12: true, 120 }); 121 shouldBe(o.format(now2), `12:00 PM`); 122 } 123 124 { 125 let o = new Intl.DateTimeFormat("en" , { 126 timeStyle: "short", 127 timeZone: "UTC", 128 hour12: false, 129 }); 130 shouldBe(o.format(now2), `12:00`); 131 } -
trunk/JSTests/test262/expectations.yaml
r267040 r267108 1409 1409 strict mode: 'TypeError: TypedArray.of requires its this argument to subclass a TypedArray constructor (Testing with Float64Array.)' 1410 1410 test/intl402/DateTimeFormat/prototype/format/timedatestyle-en.js: 1411 default: 'Test262Error: Result for full with {} Expected SameValue(«14:12:47 PM Coordinated Universal Time», «14:12:47Coordinated Universal Time») to be true'1412 strict mode: 'Test262Error: Result for full with {} Expected SameValue(«14:12:47 PM Coordinated Universal Time», «14:12:47Coordinated Universal Time») to be true'1411 default: 'Test262Error: Result for date=medium and time=full Expected SameValue(«May 1, 1886 at 2:12:47 PM Coordinated Universal Time», «May 1, 1886, 2:12:47 PM Coordinated Universal Time») to be true' 1412 strict mode: 'Test262Error: Result for date=medium and time=full Expected SameValue(«May 1, 1886 at 2:12:47 PM Coordinated Universal Time», «May 1, 1886, 2:12:47 PM Coordinated Universal Time») to be true' 1413 1413 test/intl402/DateTimeFormat/prototype/formatRange/en-US.js: 1414 1414 default: 'Test262Error: Expected SameValue(«1/3/2019 – 1/5/2019», «1/3/2019 – 1/5/2019») to be true' -
trunk/Source/JavaScriptCore/ChangeLog
r267102 r267108 1 2020-09-15 Yusuke Suzuki <ysuzuki@apple.com> 2 3 [JSC] Apply Intl.DateTimeFormat hour-cycle correctly when timeStyle is used 4 https://bugs.webkit.org/show_bug.cgi?id=216521 5 6 Reviewed by Ross Kirsling. 7 8 When specifying timeStyle in Intl.DateTimeFormat, we need to check that the generated format also follows to the hourCycle / hour12 options 9 specified in the constructor. Because dayPeriod can be included automatically, just replacing symbols after generating a pattern can dump strange result. 10 For example, the generated one is something like "02:12:47 PM Coordinated Universal Time". And we adjust the pattern to make it "14:12:47 PM Coordinated Universal Time" 11 when hourCycle H23 / H24 is specified. But this looks strange since dayPeriod "PM" should not exist when using H23 / H24. 12 13 In this patch, we revise our hour-cycle handling in Intl.DateTimeFormat. We align our behavior to SpiderMonkey's one[1] rather than the spec's one: when hour12 is specified, 14 we will just use 'H' or 'h' skeleton and do not enforce hour-cycle after generating pattern in hour12 case. If hour12 is not specified, then we use 'h' or 'H' skeleton 15 symbols based on hour-cycle, and later we modify the pattern based on hour-cycle. If both are not offered, we use 'j' which allows ICU to pick preferable one. 16 This is slightly different behavior to the spec (hcDefault etc.) but the spec's behavior can cause a bit surprising result[2,3], and SpiderMonkey like behavior will be 17 integrated into the spec eventually[4]. 18 19 [1]: https://github.com/tc39/ecma402/issues/402#issuecomment-623628320 20 [2]: https://github.com/tc39/ecma402/issues/402 21 [3]: https://bugs.chromium.org/p/chromium/issues/detail?id=1045791 22 [4]: https://github.com/tc39/ecma402/pull/436 23 24 * runtime/IntlDateTimeFormat.cpp: 25 (JSC::IntlDateTimeFormat::setFormatsFromPattern): 26 (JSC::IntlDateTimeFormat::parseHourCycle): 27 (JSC::IntlDateTimeFormat::hourCycleFromPattern): 28 (JSC::IntlDateTimeFormat::replaceHourCycleInSkeleton): 29 (JSC::IntlDateTimeFormat::replaceHourCycleInPattern): 30 (JSC::IntlDateTimeFormat::initializeDateTimeFormat): 31 (JSC::IntlDateTimeFormat::hourCycleString): 32 (JSC::IntlDateTimeFormat::resolvedOptions const): 33 (JSC::IntlDateTimeFormat::createDateIntervalFormatIfNecessary): 34 * runtime/IntlDateTimeFormat.h: 35 1 36 2020-09-14 Yusuke Suzuki <ysuzuki@apple.com> 2 37 -
trunk/Source/JavaScriptCore/runtime/IntlDateTimeFormat.cpp
r266323 r267108 330 330 } 331 331 332 // If hourCycle was null, this sets it to the locale default.333 if (m_hourCycle.isNull()) {334 if (currentCharacter == 'h')335 m_hourCycle = "h12"_s;336 else if (currentCharacter == 'H')337 m_hourCycle = "h23"_s;338 else if (currentCharacter == 'k')339 m_hourCycle = "h24"_s;340 else if (currentCharacter == 'K')341 m_hourCycle = "h11"_s;342 }343 344 332 switch (currentCharacter) { 345 333 case 'G': … … 399 387 case 'H': 400 388 case 'k': 401 case 'K': 389 case 'K': { 390 // Populate hourCycle from actually generated patterns. It is possible that locale or option is specifying hourCycle explicitly, 391 // but the generated pattern does not include related part since the pattern does not include hours. 392 // This is tested in test262/test/intl402/DateTimeFormat/prototype/resolvedOptions/hourCycle-dateStyle.js and our stress tests. 393 // Example: 394 // new Intl.DateTimeFormat(`de-u-hc-h11`, { 395 // dateStyle: "full" 396 // }).resolvedOptions().hourCycle === undefined 397 m_hourCycle = hourCycleFromSymbol(currentCharacter); 402 398 if (count == 1) 403 399 m_hour = Hour::Numeric; … … 405 401 m_hour = Hour::TwoDigit; 406 402 break; 403 } 407 404 case 'm': 408 405 if (count == 1) … … 432 429 } 433 430 431 IntlDateTimeFormat::HourCycle IntlDateTimeFormat::parseHourCycle(const String& hourCycle) 432 { 433 if (hourCycle == "h11"_s) 434 return HourCycle::H11; 435 if (hourCycle == "h12"_s) 436 return HourCycle::H12; 437 if (hourCycle == "h23"_s) 438 return HourCycle::H23; 439 if (hourCycle == "h24"_s) 440 return HourCycle::H24; 441 return HourCycle::None; 442 } 443 444 inline IntlDateTimeFormat::HourCycle IntlDateTimeFormat::hourCycleFromSymbol(UChar symbol) 445 { 446 switch (symbol) { 447 case 'K': 448 return HourCycle::H11; 449 case 'h': 450 return HourCycle::H12; 451 case 'H': 452 return HourCycle::H23; 453 case 'k': 454 return HourCycle::H24; 455 } 456 return HourCycle::None; 457 } 458 459 inline IntlDateTimeFormat::HourCycle IntlDateTimeFormat::hourCycleFromPattern(const Vector<UChar, 32>& pattern) 460 { 461 for (auto character : pattern) { 462 switch (character) { 463 case 'K': 464 case 'h': 465 case 'H': 466 case 'k': 467 return hourCycleFromSymbol(character); 468 } 469 } 470 return HourCycle::None; 471 } 472 473 inline void IntlDateTimeFormat::replaceHourCycleInSkeleton(Vector<UChar, 32>& skeleton, bool isHour12) 474 { 475 UChar skeletonCharacter = 'H'; 476 if (isHour12) 477 skeletonCharacter = 'h'; 478 for (auto& character : skeleton) { 479 switch (character) { 480 case 'h': 481 case 'H': 482 case 'j': 483 character = skeletonCharacter; 484 break; 485 } 486 } 487 } 488 489 inline void IntlDateTimeFormat::replaceHourCycleInPattern(Vector<UChar, 32>& pattern, HourCycle hourCycle) 490 { 491 UChar hourFromHourCycle = 'H'; 492 switch (hourCycle) { 493 case HourCycle::H11: 494 hourFromHourCycle = 'K'; 495 break; 496 case HourCycle::H12: 497 hourFromHourCycle = 'h'; 498 break; 499 case HourCycle::H23: 500 hourFromHourCycle = 'H'; 501 break; 502 case HourCycle::H24: 503 hourFromHourCycle = 'k'; 504 break; 505 case HourCycle::None: 506 return; 507 } 508 509 for (auto& character : pattern) { 510 switch (character) { 511 case 'K': 512 case 'h': 513 case 'H': 514 case 'k': 515 character = hourFromHourCycle; 516 break; 517 } 518 } 519 } 520 434 521 // https://tc39.github.io/ecma402/#sec-initializedatetimeformat 435 522 void IntlDateTimeFormat::initializeDateTimeFormat(JSGlobalObject* globalObject, JSValue locales, JSValue originalOptions) … … 471 558 TriState hour12 = intlBooleanOption(globalObject, options, vm.propertyNames->hour12); 472 559 RETURN_IF_EXCEPTION(scope, void()); 473 bool isHour12Undefined = (hour12 == TriState::Indeterminate); 474 475 String hourCycle = intlStringOption(globalObject, options, vm.propertyNames->hourCycle, { "h11", "h12", "h23", "h24" }, "hourCycle must be \"h11\", \"h12\", \"h23\", or \"h24\"", nullptr); 476 RETURN_IF_EXCEPTION(scope, void()); 477 if (isHour12Undefined) { 478 // Set hour12 here to simplify hour logic later. 479 hour12 = triState(hourCycle == "h11" || hourCycle == "h12"); 480 if (!hourCycle.isNull()) 481 localeOptions[static_cast<unsigned>(RelevantExtensionKey::Hc)] = hourCycle; 482 } else 560 561 HourCycle hourCycle = intlOption<HourCycle>(globalObject, options, vm.propertyNames->hourCycle, { { "h11"_s, HourCycle::H11 }, { "h12"_s, HourCycle::H12 }, { "h23"_s, HourCycle::H23 }, { "h24"_s, HourCycle::H24 } }, "hourCycle must be \"h11\", \"h12\", \"h23\", or \"h24\""_s, HourCycle::None); 562 RETURN_IF_EXCEPTION(scope, void()); 563 if (hour12 == TriState::Indeterminate) { 564 if (hourCycle != HourCycle::None) 565 localeOptions[static_cast<unsigned>(RelevantExtensionKey::Hc)] = String(hourCycleString(hourCycle)); 566 } else { 567 // If there is hour12, hourCycle is ignored. 568 // We are setting null String explicitly here (localeOptions' entries are Optional<String>). This leads us to use HourCycle::None later. 483 569 localeOptions[static_cast<unsigned>(RelevantExtensionKey::Hc)] = String(); 570 } 484 571 485 572 const HashSet<String>& availableLocales = intlDateTimeFormatAvailableLocales(); … … 500 587 m_calendar = "ethiopic-amete-alem"_s; 501 588 502 m_hourCycle = resolved.extensions[static_cast<unsigned>(RelevantExtensionKey::Hc)];589 hourCycle = parseHourCycle(resolved.extensions[static_cast<unsigned>(RelevantExtensionKey::Hc)]); 503 590 m_numberingSystem = resolved.extensions[static_cast<unsigned>(RelevantExtensionKey::Nu)]; 504 591 m_dataLocale = resolved.dataLocale; … … 610 697 Hour hour = intlOption<Hour>(globalObject, options, vm.propertyNames->hour, { { "2-digit"_s, Hour::TwoDigit }, { "numeric"_s, Hour::Numeric } }, "hour must be \"2-digit\" or \"numeric\""_s, Hour::None); 611 698 RETURN_IF_EXCEPTION(scope, void()); 612 switch (hour) { 613 case Hour::TwoDigit: 614 if (isHour12Undefined && m_hourCycle.isNull()) 615 skeletonBuilder.appendLiteral("jj"); 616 else if (hour12 == TriState::True) 617 skeletonBuilder.appendLiteral("hh"); 618 else 619 skeletonBuilder.appendLiteral("HH"); 620 break; 621 case Hour::Numeric: 622 if (isHour12Undefined && m_hourCycle.isNull()) 623 skeletonBuilder.append('j'); 624 else if (hour12 == TriState::True) 625 skeletonBuilder.append('h'); 626 else 627 skeletonBuilder.append('H'); 628 break; 629 case Hour::None: 630 break; 699 { 700 // Specifically, this hour-cycle / hour12 behavior is slightly different from the spec. 701 // But the spec behavior is known to cause surprising behaviors, and the spec change is ongoing. 702 // We implement SpiderMonkey's behavior. 703 // 704 // > No option present: "j" 705 // > hour12 = true: "h" 706 // > hour12 = false: "H" 707 // > hourCycle = h11: "h", plus modifying the resolved pattern to use the hour symbol "K". 708 // > hourCycle = h12: "h", plus modifying the resolved pattern to use the hour symbol "h". 709 // > hourCycle = h23: "H", plus modifying the resolved pattern to use the hour symbol "H". 710 // > hourCycle = h24: "H", plus modifying the resolved pattern to use the hour symbol "k". 711 // 712 UChar skeletonCharacter = 'j'; 713 if (hour12 == TriState::Indeterminate) { 714 switch (hourCycle) { 715 case HourCycle::None: 716 break; 717 case HourCycle::H11: 718 case HourCycle::H12: 719 skeletonCharacter = 'h'; 720 break; 721 case HourCycle::H23: 722 case HourCycle::H24: 723 skeletonCharacter = 'H'; 724 break; 725 } 726 } else { 727 if (hour12 == TriState::True) 728 skeletonCharacter = 'h'; 729 else 730 skeletonCharacter = 'H'; 731 } 732 733 switch (hour) { 734 case Hour::TwoDigit: 735 skeletonBuilder.append(skeletonCharacter); 736 skeletonBuilder.append(skeletonCharacter); 737 break; 738 case Hour::Numeric: 739 skeletonBuilder.append(skeletonCharacter); 740 break; 741 case Hour::None: 742 break; 743 } 631 744 } 632 745 … … 702 815 RETURN_IF_EXCEPTION(scope, void()); 703 816 704 bool canIncludeHour = true; 817 818 auto patternFromSkeleton = [&](const UChar* skeleton, unsigned skeletonSize, Vector<UChar, 32>& patternBuffer, UErrorCode& status) { 819 // Always use ICU date format generator, rather than our own pattern list and matcher. 820 auto generator = std::unique_ptr<UDateTimePatternGenerator, ICUDeleter<udatpg_close>>(udatpg_open(dataLocaleWithExtensions.data(), &status)); 821 if (U_FAILURE(status)) 822 return; 823 824 status = callBufferProducingFunction(udatpg_getBestPatternWithOptions, generator.get(), skeleton, skeletonSize, UDATPG_MATCH_HOUR_FIELD_LENGTH, patternBuffer); 825 if (U_FAILURE(status)) 826 return; 827 }; 828 705 829 Vector<UChar, 32> patternBuffer; 706 830 if (m_dateStyle != DateTimeStyle::None || m_timeStyle != DateTimeStyle::None) { … … 713 837 throwTypeError(globalObject, scope, "dateStyle and timeStyle may not be used with other DateTimeFormat options"_s); 714 838 return; 715 }716 717 // If timeStyle is not specified, m_hourCycle does not matter. Let's skip enforcing step.718 if (m_timeStyle == DateTimeStyle::None) {719 canIncludeHour = false;720 m_hourCycle = String();721 839 } 722 840 … … 739 857 // We cannot use this UDateFormat directly yet because we need to enforce specified hourCycle. 740 858 // First, we create UDateFormat via dateStyle and timeStyle. And then convert it to pattern string. 741 // After updating this pattern string with m_hourCycle, we create a final UDateFormat with the updated pattern string.859 // After updating this pattern string with hourCycle, we create a final UDateFormat with the updated pattern string. 742 860 UErrorCode status = U_ZERO_ERROR; 743 861 StringView timeZoneView(m_timeZone); … … 753 871 return; 754 872 } 873 874 // It is possible that timeStyle includes dayPeriod, which is sensitive to hour-cycle. 875 // If dayPeriod is included, just replacing hour based on hourCycle / hour12 produces strange results. 876 // Let's consider about the example. The formatted result looks like "02:12:47 PM Coordinated Universal Time" 877 // If we simply replace 02 to 14, this becomes "14:12:47 PM Coordinated Universal Time", this looks strange since "PM" is unnecessary! 878 // 879 // If the generated pattern's hour12 does not match against the option's one, we retrieve skeleton from the pattern, enforcing hour-cycle, 880 // and re-generating the best pattern from the modified skeleton. ICU will look into the generated skeleton, and pick the best format for the request. 881 // We do not care about h11 vs. h12 and h23 vs. h24 difference here since this will be later adjusted by replaceHourCycleInPattern. 882 // 883 // test262/test/intl402/DateTimeFormat/prototype/format/timedatestyle-en.js includes the test for this behavior. 884 if (m_timeStyle != DateTimeStyle::None && (hourCycle != HourCycle::None || hour12 != TriState::Indeterminate)) { 885 auto isHour12 = [](HourCycle hourCycle) { 886 return hourCycle == HourCycle::H11 || hourCycle == HourCycle::H12; 887 }; 888 bool specifiedHour12 = false; 889 // If hour12 is specified, we prefer it and ignore hourCycle. 890 if (hour12 != TriState::Indeterminate) 891 specifiedHour12 = hour12 == TriState::True; 892 else 893 specifiedHour12 = isHour12(hourCycle); 894 HourCycle extractedHourCycle = hourCycleFromPattern(patternBuffer); 895 if (extractedHourCycle != HourCycle::None && isHour12(extractedHourCycle) != specifiedHour12) { 896 Vector<UChar, 32> skeleton; 897 auto status = callBufferProducingFunction(udatpg_getSkeleton, nullptr, patternBuffer.data(), patternBuffer.size(), skeleton); 898 if (U_FAILURE(status)) { 899 throwTypeError(globalObject, scope, "failed to initialize DateTimeFormat"_s); 900 return; 901 } 902 replaceHourCycleInSkeleton(skeleton, specifiedHour12); 903 dataLogLnIf(IntlDateTimeFormatInternal::verbose, "replaced:(", StringView(skeleton.data(), skeleton.size()), ")"); 904 905 patternBuffer.clear(); 906 patternFromSkeleton(skeleton.data(), skeleton.size(), patternBuffer, status); 907 if (U_FAILURE(status)) { 908 throwTypeError(globalObject, scope, "failed to initialize DateTimeFormat"_s); 909 return; 910 } 911 } 912 } 755 913 } else { 756 // If hour is not specified, m_hourCycle does not matter. Let's skip enforcing step.757 if (hour == Hour::None) {758 canIncludeHour = false;759 m_hourCycle = String();760 }761 762 // Always use ICU date format generator, rather than our own pattern list and matcher.763 914 UErrorCode status = U_ZERO_ERROR; 764 auto* generator = udatpg_open(dataLocaleWithExtensions.data(), &status); 915 String skeleton = skeletonBuilder.toString(); 916 patternFromSkeleton(StringView(skeleton).upconvertedCharacters().get(), skeleton.length(), patternBuffer, status); 765 917 if (U_FAILURE(status)) { 766 918 throwTypeError(globalObject, scope, "failed to initialize DateTimeFormat"_s); 767 919 return; 768 920 } 769 770 String skeleton = skeletonBuilder.toString(); 771 StringView skeletonView(skeleton); 772 status = callBufferProducingFunction(udatpg_getBestPatternWithOptions, generator, skeletonView.upconvertedCharacters().get(), skeletonView.length(), UDATPG_MATCH_HOUR_FIELD_LENGTH, patternBuffer); 773 udatpg_close(generator); 774 if (U_FAILURE(status)) { 775 throwTypeError(globalObject, scope, "failed to initialize DateTimeFormat"_s); 776 return; 777 } 778 } 779 780 // Enforce our hourCycle, replacing hour characters in pattern. ICU can pick format which does not agree with specified hourCycle. 781 // This step modifies generated pattern to use specified hourCycle, and it is specified in 1.1.1 InitializeDateTimeFormat step 33. 782 // FIXME: We should cache `hourCycleDefault` value obtained by generating "jjmm" pattern from UDateTimePatternGenerator. 783 // https://bugs.webkit.org/show_bug.cgi?id=213459 784 if (canIncludeHour) { 785 bool hasHour = false; 786 if (!m_hourCycle.isNull() || !isHour12Undefined) { 787 UChar hourFromHourCycle = 'H'; // H23 788 if (m_hourCycle == "h11") 789 hourFromHourCycle = 'K'; 790 else if (m_hourCycle == "h12") 791 hourFromHourCycle = 'h'; 792 else if (m_hourCycle == "h24") 793 hourFromHourCycle = 'k'; 794 795 bool isEscaped = false; 796 for (auto& c : patternBuffer) { 797 if (c == '\'') 798 isEscaped = !isEscaped; 799 else if (!isEscaped && (c == 'h' || c == 'H' || c == 'k' || c == 'K')) { 800 switch (c) { 801 case 'K': 802 case 'H': { 803 if (isHour12Undefined) 804 c = hourFromHourCycle; 805 else if (hour12 == TriState::True) 806 c = 'K'; 807 else 808 c = 'H'; 809 break; 810 } 811 case 'h': 812 case 'k': { 813 if (isHour12Undefined) 814 c = hourFromHourCycle; 815 else if (hour12 == TriState::True) 816 c = 'h'; 817 else 818 c = 'k'; 819 break; 820 } 821 default: 822 ASSERT_NOT_REACHED(); 823 } 824 hasHour = true; 825 } 826 } 827 } 828 if (!hasHour) 829 m_hourCycle = String(); 830 } 921 } 922 923 // After generating pattern from skeleton, we need to change h11 vs. h12 and h23 vs. h24 if hourCycle is specified. 924 if (hourCycle != HourCycle::None) 925 replaceHourCycleInPattern(patternBuffer, hourCycle); 831 926 832 927 StringView pattern(patternBuffer.data(), patternBuffer.size()); … … 847 942 UCalendar* cal = const_cast<UCalendar*>(udat_getCalendar(m_dateFormat.get())); 848 943 ucal_setGregorianChange(cal, minECMAScriptTime, &status); 944 } 945 946 ASCIILiteral IntlDateTimeFormat::hourCycleString(HourCycle hourCycle) 947 { 948 switch (hourCycle) { 949 case HourCycle::H11: 950 return "h11"_s; 951 case HourCycle::H12: 952 return "h12"_s; 953 case HourCycle::H23: 954 return "h23"_s; 955 case HourCycle::H24: 956 return "h24"_s; 957 case HourCycle::None: 958 ASSERT_NOT_REACHED(); 959 return ASCIILiteral::null(); 960 } 961 ASSERT_NOT_REACHED(); 962 return ASCIILiteral::null(); 849 963 } 850 964 … … 1041 1155 options->putDirect(vm, vm.propertyNames->timeZone, jsNontrivialString(vm, m_timeZone)); 1042 1156 1043 if ( !m_hourCycle.isNull()) {1044 options->putDirect(vm, vm.propertyNames->hourCycle, jsNontrivialString(vm, m_hourCycle));1045 options->putDirect(vm, vm.propertyNames->hour12, jsBoolean(m_hourCycle == "h11" || m_hourCycle == "h12"));1157 if (m_hourCycle != HourCycle::None) { 1158 options->putDirect(vm, vm.propertyNames->hourCycle, jsNontrivialString(vm, hourCycleString(m_hourCycle))); 1159 options->putDirect(vm, vm.propertyNames->hour12, jsBoolean(m_hourCycle == HourCycle::H11 || m_hourCycle == HourCycle::H12)); 1046 1160 } 1047 1161 … … 1268 1382 StringBuilder localeBuilder; 1269 1383 localeBuilder.append(m_dataLocale, "-u-ca-", m_calendar, "-nu-", m_numberingSystem); 1270 if ( !m_hourCycle.isNull())1271 localeBuilder.append("-hc-", m_hourCycle);1384 if (m_hourCycle != HourCycle::None) 1385 localeBuilder.append("-hc-", hourCycleString(m_hourCycle)); 1272 1386 CString dataLocaleWithExtensions = localeBuilder.toString().utf8(); 1273 1387 -
trunk/Source/JavaScriptCore/runtime/IntlDateTimeFormat.h
r266655 r267108 79 79 UDateIntervalFormat* createDateIntervalFormatIfNecessary(JSGlobalObject*); 80 80 81 enum class HourCycle : uint8_t { None, H11, H12, H23, H24 }; 81 82 enum class Weekday : uint8_t { None, Narrow, Short, Long }; 82 83 enum class Era : uint8_t { None, Narrow, Short, Long }; … … 92 93 93 94 void setFormatsFromPattern(const StringView&); 95 static ASCIILiteral hourCycleString(HourCycle); 94 96 static ASCIILiteral weekdayString(Weekday); 95 97 static ASCIILiteral eraString(Era); … … 103 105 static ASCIILiteral timeZoneNameString(TimeZoneName); 104 106 static ASCIILiteral formatStyleString(DateTimeStyle); 107 108 static HourCycle hourCycleFromSymbol(UChar); 109 static HourCycle parseHourCycle(const String&); 110 static HourCycle hourCycleFromPattern(const Vector<UChar, 32>&); 111 static void replaceHourCycleInSkeleton(Vector<UChar, 32>&, bool hour12); 112 static void replaceHourCycleInPattern(Vector<UChar, 32>&, HourCycle); 105 113 106 114 using UDateFormatDeleter = ICUDeleter<udat_close>; … … 116 124 String m_numberingSystem; 117 125 String m_timeZone; 118 String m_hourCycle;126 HourCycle m_hourCycle { HourCycle::None }; 119 127 Weekday m_weekday { Weekday::None }; 120 128 Era m_era { Era::None };
Note: See TracChangeset
for help on using the changeset viewer.