LCOV - code coverage report
Current view: top level - src/objects - js-date-time-format.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 393 433 90.8 %
Date: 2019-03-21 Functions: 44 53 83.0 %

          Line data    Source code
       1             : // Copyright 2018 the V8 project authors. All rights reserved.
       2             : // Use of this source code is governed by a BSD-style license that can be
       3             : // found in the LICENSE file.
       4             : 
       5             : #ifndef V8_INTL_SUPPORT
       6             : #error Internationalization is expected to be enabled.
       7             : #endif  // V8_INTL_SUPPORT
       8             : 
       9             : #include "src/objects/js-date-time-format.h"
      10             : 
      11             : #include <memory>
      12             : #include <string>
      13             : #include <vector>
      14             : 
      15             : #include "src/date.h"
      16             : #include "src/heap/factory.h"
      17             : #include "src/isolate.h"
      18             : #include "src/objects/intl-objects.h"
      19             : #include "src/objects/js-date-time-format-inl.h"
      20             : 
      21             : #include "unicode/calendar.h"
      22             : #include "unicode/dtptngen.h"
      23             : #include "unicode/gregocal.h"
      24             : #include "unicode/smpdtfmt.h"
      25             : #include "unicode/unistr.h"
      26             : 
      27             : namespace v8 {
      28             : namespace internal {
      29             : 
      30             : namespace {
      31             : 
      32       92670 : class PatternMap {
      33             :  public:
      34       10855 :   PatternMap(std::string pattern, std::string value)
      35       21710 :       : pattern(std::move(pattern)), value(std::move(value)) {}
      36       63140 :   virtual ~PatternMap() = default;
      37             :   std::string pattern;
      38             :   std::string value;
      39             : };
      40             : 
      41        4950 : class PatternItem {
      42             :  public:
      43        2475 :   PatternItem(const std::string property, std::vector<PatternMap> pairs,
      44             :               std::vector<const char*> allowed_values)
      45             :       : property(std::move(property)),
      46             :         pairs(std::move(pairs)),
      47        4950 :         allowed_values(allowed_values) {}
      48        7425 :   virtual ~PatternItem() = default;
      49             : 
      50             :   const std::string property;
      51             :   // It is important for the pattern in the pairs from longer one to shorter one
      52             :   // if the longer one contains substring of an shorter one.
      53             :   std::vector<PatternMap> pairs;
      54             :   std::vector<const char*> allowed_values;
      55             : };
      56             : 
      57         275 : static const std::vector<PatternItem> BuildPatternItems() {
      58             :   const std::vector<const char*> kLongShort = {"long", "short"};
      59             :   const std::vector<const char*> kNarrowLongShort = {"narrow", "long", "short"};
      60             :   const std::vector<const char*> k2DigitNumeric = {"2-digit", "numeric"};
      61             :   const std::vector<const char*> kNarrowLongShort2DigitNumeric = {
      62             :       "narrow", "long", "short", "2-digit", "numeric"};
      63             :   const std::vector<PatternItem> kPatternItems = {
      64             :       PatternItem("weekday",
      65             :                   {{"EEEEE", "narrow"},
      66             :                    {"EEEE", "long"},
      67             :                    {"EEE", "short"},
      68             :                    {"ccccc", "narrow"},
      69             :                    {"cccc", "long"},
      70             :                    {"ccc", "short"}},
      71        1650 :                   kNarrowLongShort),
      72             :       PatternItem("era",
      73             :                   {{"GGGGG", "narrow"}, {"GGGG", "long"}, {"GGG", "short"}},
      74         825 :                   kNarrowLongShort),
      75             :       PatternItem("year", {{"yy", "2-digit"}, {"y", "numeric"}},
      76         550 :                   k2DigitNumeric),
      77             :       // Sometimes we get L instead of M for month - standalone name.
      78             :       PatternItem("month",
      79             :                   {{"MMMMM", "narrow"},
      80             :                    {"MMMM", "long"},
      81             :                    {"MMM", "short"},
      82             :                    {"MM", "2-digit"},
      83             :                    {"M", "numeric"},
      84             :                    {"LLLLL", "narrow"},
      85             :                    {"LLLL", "long"},
      86             :                    {"LLL", "short"},
      87             :                    {"LL", "2-digit"},
      88             :                    {"L", "numeric"}},
      89        2750 :                   kNarrowLongShort2DigitNumeric),
      90         550 :       PatternItem("day", {{"dd", "2-digit"}, {"d", "numeric"}}, k2DigitNumeric),
      91             :       PatternItem("hour",
      92             :                   {{"HH", "2-digit"},
      93             :                    {"H", "numeric"},
      94             :                    {"hh", "2-digit"},
      95             :                    {"h", "numeric"},
      96             :                    {"kk", "2-digit"},
      97             :                    {"k", "numeric"},
      98             :                    {"KK", "2-digit"},
      99             :                    {"K", "numeric"}},
     100        2200 :                   k2DigitNumeric),
     101             :       PatternItem("minute", {{"mm", "2-digit"}, {"m", "numeric"}},
     102         550 :                   k2DigitNumeric),
     103             :       PatternItem("second", {{"ss", "2-digit"}, {"s", "numeric"}},
     104         550 :                   k2DigitNumeric),
     105             :       PatternItem("timeZoneName", {{"zzzz", "long"}, {"z", "short"}},
     106       32725 :                   kLongShort)};
     107         275 :   return kPatternItems;
     108             : }
     109             : 
     110             : class PatternItems {
     111             :  public:
     112         275 :   PatternItems() : data(BuildPatternItems()) {}
     113           0 :   virtual ~PatternItems() {}
     114             :   const std::vector<PatternItem>& Get() const { return data; }
     115             : 
     116             :  private:
     117             :   const std::vector<PatternItem> data;
     118             : };
     119             : 
     120             : static const std::vector<PatternItem>& GetPatternItems() {
     121             :   static base::LazyInstance<PatternItems>::type items =
     122             :       LAZY_INSTANCE_INITIALIZER;
     123             :   return items.Pointer()->Get();
     124             : }
     125             : 
     126       16320 : class PatternData {
     127             :  public:
     128        3060 :   PatternData(const std::string property, std::vector<PatternMap> pairs,
     129             :               std::vector<const char*> allowed_values)
     130        6120 :       : property(std::move(property)), allowed_values(allowed_values) {
     131       13600 :     for (const auto& pair : pairs) {
     132       21080 :       map.insert(std::make_pair(pair.value, pair.pattern));
     133             :     }
     134        3060 :   }
     135       16320 :   virtual ~PatternData() = default;
     136             : 
     137             :   const std::string property;
     138             :   std::map<const std::string, const std::string> map;
     139             :   std::vector<const char*> allowed_values;
     140             : };
     141             : 
     142         340 : const std::vector<PatternData> CreateCommonData(const PatternData& hour_data) {
     143             :   std::vector<PatternData> build;
     144        3400 :   for (const PatternItem& item : GetPatternItems()) {
     145        6120 :     if (item.property == "hour") {
     146         340 :       build.push_back(hour_data);
     147             :     } else {
     148       13600 :       build.push_back(
     149             :           PatternData(item.property, item.pairs, item.allowed_values));
     150             :     }
     151             :   }
     152         340 :   return build;
     153             : }
     154             : 
     155         340 : const std::vector<PatternData> CreateData(const char* digit2,
     156             :                                           const char* numeric) {
     157             :   return CreateCommonData(
     158             :       PatternData("hour", {{digit2, "2-digit"}, {numeric, "numeric"}},
     159        2720 :                   {"2-digit", "numeric"}));
     160             : }
     161             : 
     162             : // According to "Date Field Symbol Table" in
     163             : // http://userguide.icu-project.org/formatparse/datetime
     164             : // Symbol | Meaning              | Example(s)
     165             : //   h      hour in am/pm (1~12)    h    7
     166             : //                                  hh   07
     167             : //   H      hour in day (0~23)      H    0
     168             : //                                  HH   00
     169             : //   k      hour in day (1~24)      k    24
     170             : //                                  kk   24
     171             : //   K      hour in am/pm (0~11)    K    0
     172             : //                                  KK   00
     173             : 
     174             : class Pattern {
     175             :  public:
     176         340 :   Pattern(const char* d1, const char* d2) : data(CreateData(d1, d2)) {}
     177           0 :   virtual ~Pattern() {}
     178           0 :   virtual const std::vector<PatternData>& Get() const { return data; }
     179             : 
     180             :  private:
     181             :   std::vector<PatternData> data;
     182             : };
     183             : 
     184             : #define DEFFINE_TRAIT(name, d1, d2)              \
     185             :   struct name {                                  \
     186             :     static void Construct(void* allocated_ptr) { \
     187             :       new (allocated_ptr) Pattern(d1, d2);       \
     188             :     }                                            \
     189             :   };
     190          20 : DEFFINE_TRAIT(H11Trait, "KK", "K")
     191         245 : DEFFINE_TRAIT(H12Trait, "hh", "h")
     192          60 : DEFFINE_TRAIT(H23Trait, "HH", "H")
     193          15 : DEFFINE_TRAIT(H24Trait, "kk", "k")
     194           0 : DEFFINE_TRAIT(HDefaultTrait, "jj", "j")
     195             : #undef DEFFINE_TRAIT
     196             : 
     197        3099 : const std::vector<PatternData>& GetPatternData(Intl::HourCycle hour_cycle) {
     198        3099 :   switch (hour_cycle) {
     199             :     case Intl::HourCycle::kH11: {
     200             :       static base::LazyInstance<Pattern, H11Trait>::type h11 =
     201             :           LAZY_INSTANCE_INITIALIZER;
     202          45 :       return h11.Pointer()->Get();
     203             :     }
     204             :     case Intl::HourCycle::kH12: {
     205             :       static base::LazyInstance<Pattern, H12Trait>::type h12 =
     206             :           LAZY_INSTANCE_INITIALIZER;
     207        2739 :       return h12.Pointer()->Get();
     208             :     }
     209             :     case Intl::HourCycle::kH23: {
     210             :       static base::LazyInstance<Pattern, H23Trait>::type h23 =
     211             :           LAZY_INSTANCE_INITIALIZER;
     212         279 :       return h23.Pointer()->Get();
     213             :     }
     214             :     case Intl::HourCycle::kH24: {
     215             :       static base::LazyInstance<Pattern, H24Trait>::type h24 =
     216             :           LAZY_INSTANCE_INITIALIZER;
     217          36 :       return h24.Pointer()->Get();
     218             :     }
     219             :     case Intl::HourCycle::kUndefined: {
     220             :       static base::LazyInstance<Pattern, HDefaultTrait>::type hDefault =
     221             :           LAZY_INSTANCE_INITIALIZER;
     222           0 :       return hDefault.Pointer()->Get();
     223             :     }
     224             :     default:
     225           0 :       UNREACHABLE();
     226             :   }
     227             : }
     228             : 
     229          72 : std::string GetGMTTzID(Isolate* isolate, const std::string& input) {
     230          72 :   std::string ret = "Etc/GMT";
     231          72 :   switch (input.length()) {
     232             :     case 8:
     233           9 :       if (input[7] == '0') return ret + '0';
     234             :       break;
     235             :     case 9:
     236          72 :       if ((input[7] == '+' || input[7] == '-') &&
     237          36 :           IsInRange(input[8], '0', '9')) {
     238          72 :         return ret + input[7] + input[8];
     239             :       }
     240             :       break;
     241             :     case 10:
     242          54 :       if ((input[7] == '+' || input[7] == '-') && (input[8] == '1') &&
     243          27 :           IsInRange(input[9], '0', '4')) {
     244          81 :         return ret + input[7] + input[8] + input[9];
     245             :       }
     246             :       break;
     247             :   }
     248           0 :   return "";
     249             : }
     250             : 
     251             : // Locale independenty version of isalpha for ascii range. This will return
     252             : // false if the ch is alpha but not in ascii range.
     253             : bool IsAsciiAlpha(char ch) {
     254       11943 :   return IsInRange(ch, 'A', 'Z') || IsInRange(ch, 'a', 'z');
     255             : }
     256             : 
     257             : // Locale independent toupper for ascii range. This will not return İ (dotted I)
     258             : // for i under Turkish locale while std::toupper may.
     259        7623 : char LocaleIndependentAsciiToUpper(char ch) {
     260        8640 :   return (IsInRange(ch, 'a', 'z')) ? (ch - 'a' + 'A') : ch;
     261             : }
     262             : 
     263             : // Locale independent tolower for ascii range.
     264             : char LocaleIndependentAsciiToLower(char ch) {
     265        4743 :   return (IsInRange(ch, 'A', 'Z')) ? (ch - 'A' + 'a') : ch;
     266             : }
     267             : 
     268             : // Returns titlecased location, bueNos_airES -> Buenos_Aires
     269             : // or ho_cHi_minH -> Ho_Chi_Minh. It is locale-agnostic and only
     270             : // deals with ASCII only characters.
     271             : // 'of', 'au' and 'es' are special-cased and lowercased.
     272             : // Also "Antarctica/DumontDUrville" is special case.
     273             : // ICU's timezone parsing is case sensitive, but ECMAScript is case insensitive
     274         405 : std::string ToTitleCaseTimezoneLocation(Isolate* isolate,
     275             :                                         const std::string& input) {
     276             :   std::string title_cased;
     277             :   int word_length = 0;
     278        6804 :   for (char ch : input) {
     279             :     // Convert first char to upper case, the rest to lower case
     280        6453 :     if (IsAsciiAlpha(ch)) {
     281       11448 :       title_cased += word_length == 0 ? LocaleIndependentAsciiToUpper(ch)
     282             :                                       : LocaleIndependentAsciiToLower(ch);
     283        5724 :       word_length++;
     284         729 :     } else if (ch == '_' || ch == '-' || ch == '/') {
     285             :       // Special case Au/Es/Of to be lower case.
     286         675 :       if (word_length == 2) {
     287          45 :         size_t pos = title_cased.length() - 2;
     288          45 :         std::string substr = title_cased.substr(pos, 2);
     289          90 :         if (substr == "Of" || substr == "Es" || substr == "Au") {
     290          72 :           title_cased[pos] = LocaleIndependentAsciiToLower(title_cased[pos]);
     291             :         }
     292             :       }
     293             :       title_cased += ch;
     294             :       word_length = 0;
     295             :     } else {
     296             :       // Invalid input
     297             :       return std::string();
     298             :     }
     299             :   }
     300             :   // Special case
     301         351 :   if (title_cased == "Antarctica/Dumontdurville") {
     302          18 :     return "Antarctica/DumontDUrville";
     303             :   }
     304             :   return title_cased;
     305             : }
     306             : 
     307             : }  // namespace
     308             : 
     309         567 : std::string JSDateTimeFormat::CanonicalizeTimeZoneID(Isolate* isolate,
     310             :                                                      const std::string& input) {
     311             :   std::string upper = input;
     312             :   transform(upper.begin(), upper.end(), upper.begin(),
     313             :             LocaleIndependentAsciiToUpper);
     314        2052 :   if (upper == "UTC" || upper == "GMT" || upper == "ETC/UTC" ||
     315             :       upper == "ETC/GMT") {
     316          90 :     return "UTC";
     317             :   }
     318             :   // We expect only _, '-' and / beside ASCII letters.
     319             :   // All inputs should conform to Area/Location(/Location)*, or Etc/GMT* .
     320             :   // TODO(jshin): 1. Support 'GB-Eire", 'EST5EDT", "ROK', 'US/*', 'NZ' and many
     321             :   // other aliases/linked names when moving timezone validation code to C++.
     322             :   // See crbug.com/364374 and crbug.com/v8/8007 .
     323             :   // 2. Resolve the difference betwee CLDR/ICU and IANA time zone db.
     324             :   // See http://unicode.org/cldr/trac/ticket/9892 and crbug.com/645807 .
     325         477 :   if (strncmp(upper.c_str(), "ETC/GMT", 7) == 0) {
     326          72 :     return GetGMTTzID(isolate, input);
     327             :   }
     328         405 :   return ToTitleCaseTimezoneLocation(isolate, input);
     329             : }
     330             : 
     331             : namespace {
     332             : 
     333          36 : Handle<String> DateTimeStyleAsString(Isolate* isolate,
     334             :                                      JSDateTimeFormat::DateTimeStyle style) {
     335          36 :   switch (style) {
     336             :     case JSDateTimeFormat::DateTimeStyle::kFull:
     337             :       return ReadOnlyRoots(isolate).full_string_handle();
     338             :     case JSDateTimeFormat::DateTimeStyle::kLong:
     339             :       return ReadOnlyRoots(isolate).long_string_handle();
     340             :     case JSDateTimeFormat::DateTimeStyle::kMedium:
     341             :       return ReadOnlyRoots(isolate).medium_string_handle();
     342             :     case JSDateTimeFormat::DateTimeStyle::kShort:
     343             :       return ReadOnlyRoots(isolate).short_string_handle();
     344             :     case JSDateTimeFormat::DateTimeStyle::kUndefined:
     345           0 :       UNREACHABLE();
     346             :   }
     347           0 : }
     348             : 
     349             : }  // namespace
     350             : 
     351             : // ecma402 #sec-intl.datetimeformat.prototype.resolvedoptions
     352        2529 : MaybeHandle<JSObject> JSDateTimeFormat::ResolvedOptions(
     353             :     Isolate* isolate, Handle<JSDateTimeFormat> date_time_format) {
     354             :   Factory* factory = isolate->factory();
     355             :   // 4. Let options be ! ObjectCreate(%ObjectPrototype%).
     356        2529 :   Handle<JSObject> options = factory->NewJSObject(isolate->object_function());
     357             : 
     358             :   Handle<Object> resolved_obj;
     359             : 
     360        2529 :   CHECK(!date_time_format->icu_locale().is_null());
     361        2529 :   CHECK_NOT_NULL(date_time_format->icu_locale()->raw());
     362             :   icu::Locale* icu_locale = date_time_format->icu_locale()->raw();
     363        2529 :   Maybe<std::string> maybe_locale_str = Intl::ToLanguageTag(*icu_locale);
     364        2529 :   MAYBE_RETURN(maybe_locale_str, MaybeHandle<JSObject>());
     365             :   std::string locale_str = maybe_locale_str.FromJust();
     366             :   Handle<String> locale =
     367        2529 :       factory->NewStringFromAsciiChecked(locale_str.c_str());
     368             : 
     369             :   icu::SimpleDateFormat* icu_simple_date_format =
     370             :       date_time_format->icu_simple_date_format()->raw();
     371             :   // calendar
     372        2529 :   const icu::Calendar* calendar = icu_simple_date_format->getCalendar();
     373             :   // getType() returns legacy calendar type name instead of LDML/BCP47 calendar
     374             :   // key values. intl.js maps them to BCP47 values for key "ca".
     375             :   // TODO(jshin): Consider doing it here, instead.
     376        2529 :   std::string calendar_str = calendar->getType();
     377             : 
     378             :   // Maps ICU calendar names to LDML/BCP47 types for key 'ca'.
     379             :   // See typeMap section in third_party/icu/source/data/misc/keyTypeData.txt
     380             :   // and
     381             :   // http://www.unicode.org/repos/cldr/tags/latest/common/bcp47/calendar.xml
     382        2529 :   if (calendar_str == "gregorian") {
     383             :     calendar_str = "gregory";
     384         612 :   } else if (calendar_str == "ethiopic-amete-alem") {
     385             :     calendar_str = "ethioaa";
     386             :   }
     387             : 
     388        2529 :   const icu::TimeZone& tz = calendar->getTimeZone();
     389        2529 :   icu::UnicodeString time_zone;
     390             :   tz.getID(time_zone);
     391        2529 :   UErrorCode status = U_ZERO_ERROR;
     392        2529 :   icu::UnicodeString canonical_time_zone;
     393        2529 :   icu::TimeZone::getCanonicalID(time_zone, canonical_time_zone, status);
     394             :   Handle<Object> timezone_value;
     395        2529 :   if (U_SUCCESS(status)) {
     396             :     // In CLDR (http://unicode.org/cldr/trac/ticket/9943), Etc/UTC is made
     397             :     // a separate timezone ID from Etc/GMT even though they're still the same
     398             :     // timezone. We have Etc/UTC because 'UTC', 'Etc/Universal',
     399             :     // 'Etc/Zulu' and others are turned to 'Etc/UTC' by ICU. Etc/GMT comes
     400             :     // from Etc/GMT0, Etc/GMT+0, Etc/GMT-0, Etc/Greenwich.
     401             :     // ecma402#sec-canonicalizetimezonename step 3
     402       10026 :     if (canonical_time_zone == UNICODE_STRING_SIMPLE("Etc/UTC") ||
     403        7407 :         canonical_time_zone == UNICODE_STRING_SIMPLE("Etc/GMT")) {
     404         126 :       timezone_value = factory->UTC_string();
     405             :     } else {
     406        4806 :       ASSIGN_RETURN_ON_EXCEPTION(isolate, timezone_value,
     407             :                                  Intl::ToString(isolate, canonical_time_zone),
     408             :                                  JSObject);
     409             :     }
     410             :   } else {
     411             :     // Somehow on Windows we will reach here.
     412           0 :     timezone_value = factory->undefined_value();
     413             :   }
     414             : 
     415             :   // Ugly hack. ICU doesn't expose numbering system in any way, so we have
     416             :   // to assume that for given locale NumberingSystem constructor produces the
     417             :   // same digits as NumberFormat/Calendar would.
     418             :   // Tracked by https://unicode-org.atlassian.net/browse/ICU-13431
     419        2529 :   std::string numbering_system = Intl::GetNumberingSystem(*icu_locale);
     420             : 
     421        2529 :   icu::UnicodeString pattern_unicode;
     422        2529 :   icu_simple_date_format->toPattern(pattern_unicode);
     423             :   std::string pattern;
     424        2529 :   pattern_unicode.toUTF8String(pattern);
     425             : 
     426             :   // 5. For each row of Table 6, except the header row, in table order, do
     427             :   // Table 6: Resolved Options of DateTimeFormat Instances
     428             :   //  Internal Slot          Property
     429             :   //    [[Locale]]           "locale"
     430             :   //    [[Calendar]]         "calendar"
     431             :   //    [[NumberingSystem]]  "numberingSystem"
     432             :   //    [[TimeZone]]         "timeZone"
     433             :   //    [[HourCycle]]        "hourCycle"
     434             :   //                         "hour12"
     435             :   //    [[Weekday]]          "weekday"
     436             :   //    [[Era]]              "era"
     437             :   //    [[Year]]             "year"
     438             :   //    [[Month]]            "month"
     439             :   //    [[Day]]              "day"
     440             :   //    [[Hour]]             "hour"
     441             :   //    [[Minute]]           "minute"
     442             :   //    [[Second]]           "second"
     443             :   //    [[TimeZoneName]]     "timeZoneName"
     444        5058 :   CHECK(JSReceiver::CreateDataProperty(isolate, options,
     445             :                                        factory->locale_string(), locale,
     446             :                                        Just(kDontThrow))
     447             :             .FromJust());
     448        7587 :   CHECK(JSReceiver::CreateDataProperty(
     449             :             isolate, options, factory->calendar_string(),
     450             :             factory->NewStringFromAsciiChecked(calendar_str.c_str()),
     451             :             Just(kDontThrow))
     452             :             .FromJust());
     453        2529 :   if (!numbering_system.empty()) {
     454        7587 :     CHECK(JSReceiver::CreateDataProperty(
     455             :               isolate, options, factory->numberingSystem_string(),
     456             :               factory->NewStringFromAsciiChecked(numbering_system.c_str()),
     457             :               Just(kDontThrow))
     458             :               .FromJust());
     459             :   }
     460        5058 :   CHECK(JSReceiver::CreateDataProperty(isolate, options,
     461             :                                        factory->timeZone_string(),
     462             :                                        timezone_value, Just(kDontThrow))
     463             :             .FromJust());
     464             : 
     465             :   // 5.b.i. Let hc be dtf.[[HourCycle]].
     466             :   Intl::HourCycle hc = date_time_format->hour_cycle();
     467             : 
     468        2529 :   if (hc != Intl::HourCycle::kUndefined) {
     469         675 :     CHECK(JSReceiver::CreateDataProperty(
     470             :               isolate, options, factory->hourCycle_string(),
     471             :               date_time_format->HourCycleAsString(), Just(kDontThrow))
     472             :               .FromJust());
     473         225 :     switch (hc) {
     474             :       //  ii. If hc is "h11" or "h12", let v be true.
     475             :       case Intl::HourCycle::kH11:
     476             :       case Intl::HourCycle::kH12:
     477         324 :         CHECK(JSReceiver::CreateDataProperty(
     478             :                   isolate, options, factory->hour12_string(),
     479             :                   factory->true_value(), Just(kDontThrow))
     480             :                   .FromJust());
     481             :         break;
     482             :       // iii. Else if, hc is "h23" or "h24", let v be false.
     483             :       case Intl::HourCycle::kH23:
     484             :       case Intl::HourCycle::kH24:
     485         126 :         CHECK(JSReceiver::CreateDataProperty(
     486             :                   isolate, options, factory->hour12_string(),
     487             :                   factory->false_value(), Just(kDontThrow))
     488             :                   .FromJust());
     489             :         break;
     490             :       // iv. Else, let v be undefined.
     491             :       case Intl::HourCycle::kUndefined:
     492             :         break;
     493             :     }
     494             :   }
     495             : 
     496       25290 :   for (const auto& item : GetPatternItems()) {
     497       96822 :     for (const auto& pair : item.pairs) {
     498       81126 :       if (pattern.find(pair.pattern) != std::string::npos) {
     499       28260 :         CHECK(JSReceiver::CreateDataProperty(
     500             :                   isolate, options,
     501             :                   factory->NewStringFromAsciiChecked(item.property.c_str()),
     502             :                   factory->NewStringFromAsciiChecked(pair.value.c_str()),
     503             :                   Just(kDontThrow))
     504             :                   .FromJust());
     505             :         break;
     506             :       }
     507             :     }
     508             :   }
     509             : 
     510             :   // dateStyle
     511        2529 :   if (date_time_format->date_style() != DateTimeStyle::kUndefined) {
     512          54 :     CHECK(JSReceiver::CreateDataProperty(
     513             :               isolate, options, factory->dateStyle_string(),
     514             :               DateTimeStyleAsString(isolate, date_time_format->date_style()),
     515             :               Just(kDontThrow))
     516             :               .FromJust());
     517             :   }
     518             : 
     519             :   // timeStyle
     520        2529 :   if (date_time_format->time_style() != DateTimeStyle::kUndefined) {
     521          54 :     CHECK(JSReceiver::CreateDataProperty(
     522             :               isolate, options, factory->timeStyle_string(),
     523             :               DateTimeStyleAsString(isolate, date_time_format->time_style()),
     524             :               Just(kDontThrow))
     525             :               .FromJust());
     526             :   }
     527             : 
     528        2529 :   return options;
     529             : }
     530             : 
     531             : namespace {
     532             : 
     533             : // ecma402/#sec-formatdatetime
     534             : // FormatDateTime( dateTimeFormat, x )
     535         765 : MaybeHandle<String> FormatDateTime(Isolate* isolate,
     536             :                                    const icu::SimpleDateFormat& date_format,
     537             :                                    double x) {
     538         765 :   double date_value = DateCache::TimeClip(x);
     539         765 :   if (std::isnan(date_value)) {
     540         108 :     THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kInvalidTimeValue),
     541             :                     String);
     542             :   }
     543             : 
     544         711 :   icu::UnicodeString result;
     545         711 :   date_format.format(date_value, result);
     546             : 
     547         711 :   return Intl::ToString(isolate, result);
     548             : }
     549             : 
     550             : }  // namespace
     551             : 
     552             : // ecma402/#sec-datetime-format-functions
     553             : // DateTime Format Functions
     554         576 : MaybeHandle<String> JSDateTimeFormat::DateTimeFormat(
     555             :     Isolate* isolate, Handle<JSDateTimeFormat> date_time_format,
     556             :     Handle<Object> date) {
     557             :   // 2. Assert: Type(dtf) is Object and dtf has an [[InitializedDateTimeFormat]]
     558             :   // internal slot.
     559             : 
     560             :   // 3. If date is not provided or is undefined, then
     561             :   double x;
     562         576 :   if (date->IsUndefined()) {
     563             :     // 3.a Let x be Call(%Date_now%, undefined).
     564          27 :     x = JSDate::CurrentTimeValue(isolate);
     565             :   } else {
     566             :     // 4. Else,
     567             :     //    a. Let x be ? ToNumber(date).
     568        1098 :     ASSIGN_RETURN_ON_EXCEPTION(isolate, date, Object::ToNumber(isolate, date),
     569             :                                String);
     570         549 :     CHECK(date->IsNumber());
     571             :     x = date->Number();
     572             :   }
     573             :   // 5. Return FormatDateTime(dtf, x).
     574             :   icu::SimpleDateFormat* format =
     575        1152 :       date_time_format->icu_simple_date_format()->raw();
     576         576 :   return FormatDateTime(isolate, *format, x);
     577             : }
     578             : 
     579             : namespace {
     580             : Isolate::ICUObjectCacheType ConvertToCacheType(
     581             :     JSDateTimeFormat::DefaultsOption type) {
     582         198 :   switch (type) {
     583             :     case JSDateTimeFormat::DefaultsOption::kDate:
     584             :       return Isolate::ICUObjectCacheType::kDefaultSimpleDateFormatForDate;
     585             :     case JSDateTimeFormat::DefaultsOption::kTime:
     586             :       return Isolate::ICUObjectCacheType::kDefaultSimpleDateFormatForTime;
     587             :     case JSDateTimeFormat::DefaultsOption::kAll:
     588             :       return Isolate::ICUObjectCacheType::kDefaultSimpleDateFormat;
     589             :   }
     590             : }
     591             : }  // namespace
     592             : 
     593         198 : MaybeHandle<String> JSDateTimeFormat::ToLocaleDateTime(
     594             :     Isolate* isolate, Handle<Object> date, Handle<Object> locales,
     595             :     Handle<Object> options, RequiredOption required, DefaultsOption defaults) {
     596             :   Isolate::ICUObjectCacheType cache_type = ConvertToCacheType(defaults);
     597             : 
     598             :   Factory* factory = isolate->factory();
     599             :   // 1. Let x be ? thisTimeValue(this value);
     600         198 :   if (!date->IsJSDate()) {
     601           0 :     THROW_NEW_ERROR(isolate,
     602             :                     NewTypeError(MessageTemplate::kMethodInvokedOnWrongType,
     603             :                                  factory->Date_string()),
     604             :                     String);
     605             :   }
     606             : 
     607             :   double const x = Handle<JSDate>::cast(date)->value()->Number();
     608             :   // 2. If x is NaN, return "Invalid Date"
     609         198 :   if (std::isnan(x)) {
     610           0 :     return factory->Invalid_Date_string();
     611             :   }
     612             : 
     613             :   // We only cache the instance when both locales and options are undefined,
     614             :   // as that is the only case when the specified side-effects of examining
     615             :   // those arguments are unobservable.
     616             :   bool can_cache =
     617         279 :       locales->IsUndefined(isolate) && options->IsUndefined(isolate);
     618         198 :   if (can_cache) {
     619             :     // Both locales and options are undefined, check the cache.
     620             :     icu::SimpleDateFormat* cached_icu_simple_date_format =
     621             :         static_cast<icu::SimpleDateFormat*>(
     622          54 :             isolate->get_cached_icu_object(cache_type));
     623          54 :     if (cached_icu_simple_date_format != nullptr) {
     624          24 :       return FormatDateTime(isolate, *cached_icu_simple_date_format, x);
     625             :     }
     626             :   }
     627             :   // 3. Let options be ? ToDateTimeOptions(options, required, defaults).
     628             :   Handle<JSObject> internal_options;
     629         348 :   ASSIGN_RETURN_ON_EXCEPTION(
     630             :       isolate, internal_options,
     631             :       ToDateTimeOptions(isolate, options, required, defaults), String);
     632             : 
     633             :   // 4. Let dateFormat be ? Construct(%DateTimeFormat%, « locales, options »).
     634             :   Handle<JSFunction> constructor = Handle<JSFunction>(
     635         330 :       JSFunction::cast(isolate->context()
     636         330 :                            ->native_context()
     637         330 :                            ->intl_date_time_format_function()),
     638             :       isolate);
     639             :   Handle<JSObject> obj;
     640         330 :   ASSIGN_RETURN_ON_EXCEPTION(
     641             :       isolate, obj,
     642             :       JSObject::New(constructor, constructor, Handle<AllocationSite>::null()),
     643             :       String);
     644             :   Handle<JSDateTimeFormat> date_time_format;
     645         330 :   ASSIGN_RETURN_ON_EXCEPTION(
     646             :       isolate, date_time_format,
     647             :       JSDateTimeFormat::Initialize(isolate, Handle<JSDateTimeFormat>::cast(obj),
     648             :                                    locales, internal_options),
     649             :       String);
     650             : 
     651         165 :   if (can_cache) {
     652             :     isolate->set_icu_object_in_cache(
     653          30 :         cache_type, std::static_pointer_cast<icu::UObject>(
     654          60 :                         date_time_format->icu_simple_date_format()->get()));
     655             :   }
     656             :   // 5. Return FormatDateTime(dateFormat, x).
     657             :   icu::SimpleDateFormat* format =
     658             :       date_time_format->icu_simple_date_format()->raw();
     659         165 :   return FormatDateTime(isolate, *format, x);
     660             : }
     661             : 
     662             : namespace {
     663             : 
     664       27017 : Maybe<bool> IsPropertyUndefined(Isolate* isolate, Handle<JSObject> options,
     665             :                                 const char* property) {
     666             :   Factory* factory = isolate->factory();
     667             :   // i. Let prop be the property name.
     668             :   // ii. Let value be ? Get(options, prop).
     669             :   Handle<Object> value;
     670       81051 :   ASSIGN_RETURN_ON_EXCEPTION_VALUE(
     671             :       isolate, value,
     672             :       Object::GetPropertyOrElement(
     673             :           isolate, options, factory->NewStringFromAsciiChecked(property)),
     674             :       Nothing<bool>());
     675             :   return Just(value->IsUndefined(isolate));
     676             : }
     677             : 
     678        7714 : Maybe<bool> NeedsDefault(Isolate* isolate, Handle<JSObject> options,
     679             :                          const std::vector<std::string>& props) {
     680             :   bool needs_default = true;
     681       34731 :   for (const auto& prop : props) {
     682             :     //  i. Let prop be the property name.
     683             :     // ii. Let value be ? Get(options, prop)
     684             :     Maybe<bool> maybe_undefined =
     685       27017 :         IsPropertyUndefined(isolate, options, prop.c_str());
     686       27017 :     MAYBE_RETURN(maybe_undefined, Nothing<bool>());
     687             :     // iii. If value is not undefined, let needDefaults be false.
     688       27017 :     if (!maybe_undefined.FromJust()) {
     689             :       needs_default = false;
     690             :     }
     691             :   }
     692             :   return Just(needs_default);
     693             : }
     694             : 
     695        3109 : Maybe<bool> CreateDefault(Isolate* isolate, Handle<JSObject> options,
     696             :                           const std::vector<std::string>& props) {
     697             :   Factory* factory = isolate->factory();
     698             :   // i. Perform ? CreateDataPropertyOrThrow(options, prop, "numeric").
     699       12436 :   for (const auto& prop : props) {
     700       27981 :     MAYBE_RETURN(
     701             :         JSReceiver::CreateDataProperty(
     702             :             isolate, options, factory->NewStringFromAsciiChecked(prop.c_str()),
     703             :             factory->numeric_string(), Just(kThrowOnError)),
     704             :         Nothing<bool>());
     705             :   }
     706             :   return Just(true);
     707             : }
     708             : 
     709             : }  // namespace
     710             : 
     711             : // ecma-402/#sec-todatetimeoptions
     712        3921 : MaybeHandle<JSObject> JSDateTimeFormat::ToDateTimeOptions(
     713             :     Isolate* isolate, Handle<Object> input_options, RequiredOption required,
     714             :     DefaultsOption defaults) {
     715             :   Factory* factory = isolate->factory();
     716             :   // 1. If options is undefined, let options be null; otherwise let options be ?
     717             :   //    ToObject(options).
     718             :   Handle<JSObject> options;
     719        3921 :   if (input_options->IsUndefined(isolate)) {
     720        1875 :     options = factory->NewJSObjectWithNullProto();
     721             :   } else {
     722             :     Handle<JSReceiver> options_obj;
     723        4092 :     ASSIGN_RETURN_ON_EXCEPTION(isolate, options_obj,
     724             :                                Object::ToObject(isolate, input_options),
     725             :                                JSObject);
     726             :     // 2. Let options be ObjectCreate(options).
     727        4074 :     ASSIGN_RETURN_ON_EXCEPTION(isolate, options,
     728             :                                JSObject::ObjectCreate(isolate, options_obj),
     729             :                                JSObject);
     730             :   }
     731             : 
     732             :   // 3. Let needDefaults be true.
     733             :   bool needs_default = true;
     734             : 
     735             :   // 4. If required is "date" or "any", then
     736        3912 :   if (required == RequiredOption::kAny || required == RequiredOption::kDate) {
     737             :     // a. For each of the property names "weekday", "year", "month", "day", do
     738       11625 :     const std::vector<std::string> list({"weekday", "year", "month", "day"});
     739        3875 :     Maybe<bool> maybe_needs_default = NeedsDefault(isolate, options, list);
     740        3875 :     MAYBE_RETURN(maybe_needs_default, Handle<JSObject>());
     741             :     needs_default = maybe_needs_default.FromJust();
     742             :   }
     743             : 
     744             :   // 5. If required is "time" or "any", then
     745        3912 :   if (required == RequiredOption::kAny || required == RequiredOption::kTime) {
     746             :     // a. For each of the property names "hour", "minute", "second", do
     747       11517 :     const std::vector<std::string> list({"hour", "minute", "second"});
     748        3839 :     Maybe<bool> maybe_needs_default = NeedsDefault(isolate, options, list);
     749        3839 :     MAYBE_RETURN(maybe_needs_default, Handle<JSObject>());
     750             :     needs_default &= maybe_needs_default.FromJust();
     751             :   }
     752             : 
     753             :   // 6. If needDefaults is true and defaults is either "date" or "all", then
     754        3912 :   if (needs_default) {
     755        3063 :     if (defaults == DefaultsOption::kAll || defaults == DefaultsOption::kDate) {
     756             :       // a. For each of the property names "year", "month", "day", do)
     757        9105 :       const std::vector<std::string> list({"year", "month", "day"});
     758        6070 :       MAYBE_RETURN(CreateDefault(isolate, options, list), Handle<JSObject>());
     759             :     }
     760             :     // 7. If needDefaults is true and defaults is either "time" or "all", then
     761        3063 :     if (defaults == DefaultsOption::kAll || defaults == DefaultsOption::kTime) {
     762             :       // a. For each of the property names "hour", "minute", "second", do
     763         222 :       const std::vector<std::string> list({"hour", "minute", "second"});
     764         148 :       MAYBE_RETURN(CreateDefault(isolate, options, list), Handle<JSObject>());
     765             :     }
     766             :   }
     767             :   // 8. Return options.
     768        3912 :   return options;
     769             : }
     770             : 
     771        3168 : MaybeHandle<JSDateTimeFormat> JSDateTimeFormat::UnwrapDateTimeFormat(
     772             :     Isolate* isolate, Handle<JSReceiver> format_holder) {
     773             :   Handle<Context> native_context =
     774        6336 :       Handle<Context>(isolate->context()->native_context(), isolate);
     775             :   Handle<JSFunction> constructor = Handle<JSFunction>(
     776        6336 :       JSFunction::cast(native_context->intl_date_time_format_function()),
     777             :       isolate);
     778             :   Handle<Object> dtf;
     779        6336 :   ASSIGN_RETURN_ON_EXCEPTION(
     780             :       isolate, dtf,
     781             :       Intl::LegacyUnwrapReceiver(isolate, format_holder, constructor,
     782             :                                  format_holder->IsJSDateTimeFormat()),
     783             :       JSDateTimeFormat);
     784             :   // 2. If Type(dtf) is not Object or dtf does not have an
     785             :   //    [[InitializedDateTimeFormat]] internal slot, then
     786        3168 :   if (!dtf->IsJSDateTimeFormat()) {
     787             :     // a. Throw a TypeError exception.
     788         135 :     THROW_NEW_ERROR(isolate,
     789             :                     NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
     790             :                                  isolate->factory()->NewStringFromAsciiChecked(
     791             :                                      "UnwrapDateTimeFormat"),
     792             :                                  format_holder),
     793             :                     JSDateTimeFormat);
     794             :   }
     795             :   // 3. Return dtf.
     796        3123 :   return Handle<JSDateTimeFormat>::cast(dtf);
     797             : }
     798             : 
     799             : namespace {
     800             : 
     801             : // ecma-402/#sec-isvalidtimezonename
     802         513 : bool IsValidTimeZoneName(const icu::TimeZone& tz) {
     803         513 :   UErrorCode status = U_ZERO_ERROR;
     804         513 :   icu::UnicodeString id;
     805             :   tz.getID(id);
     806         513 :   icu::UnicodeString canonical;
     807         513 :   icu::TimeZone::getCanonicalID(id, canonical, status);
     808        1026 :   return U_SUCCESS(status) &&
     809        1539 :          canonical != icu::UnicodeString("Etc/Unknown", -1, US_INV);
     810             : }
     811             : 
     812        3729 : std::unique_ptr<icu::TimeZone> CreateTimeZone(Isolate* isolate,
     813             :                                               const char* timezone) {
     814             :   // Create time zone as specified by the user. We have to re-create time zone
     815             :   // since calendar takes ownership.
     816        3729 :   if (timezone == nullptr) {
     817             :     // 19.a. Else / Let timeZone be DefaultTimeZone().
     818        3162 :     return std::unique_ptr<icu::TimeZone>(icu::TimeZone::createDefault());
     819             :   }
     820             :   std::string canonicalized =
     821        1134 :       JSDateTimeFormat::CanonicalizeTimeZoneID(isolate, timezone);
     822         567 :   if (canonicalized.empty()) return std::unique_ptr<icu::TimeZone>();
     823             :   std::unique_ptr<icu::TimeZone> tz(
     824         513 :       icu::TimeZone::createTimeZone(canonicalized.c_str()));
     825             :   // 18.b If the result of IsValidTimeZoneName(timeZone) is false, then
     826             :   // i. Throw a RangeError exception.
     827         513 :   if (!IsValidTimeZoneName(*tz)) return std::unique_ptr<icu::TimeZone>();
     828             :   return tz;
     829             : }
     830             : 
     831         290 : class CalendarCache {
     832             :  public:
     833        3585 :   icu::Calendar* CreateCalendar(const icu::Locale& locale, icu::TimeZone* tz) {
     834        3585 :     icu::UnicodeString tz_id;
     835             :     tz->getID(tz_id);
     836             :     std::string key;
     837        3585 :     tz_id.toUTF8String<std::string>(key);
     838             :     key += ":";
     839             :     key += locale.getName();
     840             : 
     841        3585 :     base::MutexGuard guard(&mutex_);
     842             :     auto it = map_.find(key);
     843        3585 :     if (it != map_.end()) {
     844        2060 :       delete tz;
     845        2060 :       return it->second->clone();
     846             :     }
     847             :     // Create a calendar using locale, and apply time zone to it.
     848        1525 :     UErrorCode status = U_ZERO_ERROR;
     849             :     std::unique_ptr<icu::Calendar> calendar(
     850        1525 :         icu::Calendar::createInstance(tz, locale, status));
     851        1525 :     CHECK(U_SUCCESS(status));
     852        1525 :     CHECK_NOT_NULL(calendar.get());
     853             : 
     854        3050 :     if (calendar->getDynamicClassID() ==
     855        1525 :         icu::GregorianCalendar::getStaticClassID()) {
     856             :       icu::GregorianCalendar* gc =
     857             :           static_cast<icu::GregorianCalendar*>(calendar.get());
     858        1070 :       UErrorCode status = U_ZERO_ERROR;
     859             :       // The beginning of ECMAScript time, namely -(2**53)
     860             :       const double start_of_time = -9007199254740992;
     861        1070 :       gc->setGregorianChange(start_of_time, status);
     862             :       DCHECK(U_SUCCESS(status));
     863             :     }
     864             : 
     865        1525 :     if (map_.size() > 8) {  // Cache at most 8 calendars.
     866             :       map_.clear();
     867             :     }
     868        1525 :     map_[key].reset(calendar.release());
     869        3050 :     return map_[key]->clone();
     870             :   }
     871             : 
     872             :  private:
     873             :   std::map<std::string, std::unique_ptr<icu::Calendar>> map_;
     874             :   base::Mutex mutex_;
     875             : };
     876             : 
     877        3585 : icu::Calendar* CreateCalendar(Isolate* isolate, const icu::Locale& icu_locale,
     878             :                               icu::TimeZone* tz) {
     879             :   static base::LazyInstance<CalendarCache>::type calendar_cache =
     880             :       LAZY_INSTANCE_INITIALIZER;
     881        3585 :   return calendar_cache.Pointer()->CreateCalendar(icu_locale, tz);
     882             : }
     883             : 
     884        1633 : std::unique_ptr<icu::SimpleDateFormat> CreateICUDateFormat(
     885             :     const icu::Locale& icu_locale, const icu::UnicodeString& skeleton,
     886             :     icu::DateTimePatternGenerator& generator) {
     887             :   // See https://github.com/tc39/ecma402/issues/225 . The best pattern
     888             :   // generation needs to be done in the base locale according to the
     889             :   // current spec however odd it may be. See also crbug.com/826549 .
     890             :   // This is a temporary work-around to get v8's external behavior to match
     891             :   // the current spec, but does not follow the spec provisions mentioned
     892             :   // in the above Ecma 402 issue.
     893             :   // TODO(jshin): The spec may need to be revised because using the base
     894             :   // locale for the pattern match is not quite right. Moreover, what to
     895             :   // do with 'related year' part when 'chinese/dangi' calendar is specified
     896             :   // has to be discussed. Revisit once the spec is clarified/revised.
     897        1633 :   icu::UnicodeString pattern;
     898        1633 :   UErrorCode status = U_ZERO_ERROR;
     899        3266 :   pattern = generator.getBestPattern(skeleton, UDATPG_MATCH_HOUR_FIELD_LENGTH,
     900             :                                      status);
     901        1633 :   CHECK(U_SUCCESS(status));
     902             : 
     903             :   // Make formatter from skeleton. Calendar and numbering system are added
     904             :   // to the locale as Unicode extension (if they were specified at all).
     905        1633 :   status = U_ZERO_ERROR;
     906             :   std::unique_ptr<icu::SimpleDateFormat> date_format(
     907        1633 :       new icu::SimpleDateFormat(pattern, icu_locale, status));
     908        1633 :   if (U_FAILURE(status)) return std::unique_ptr<icu::SimpleDateFormat>();
     909             : 
     910        1624 :   CHECK_NOT_NULL(date_format.get());
     911             :   return date_format;
     912             : }
     913             : 
     914         275 : class DateFormatCache {
     915             :  public:
     916        3108 :   icu::SimpleDateFormat* Create(const icu::Locale& icu_locale,
     917             :                                 const icu::UnicodeString& skeleton,
     918             :                                 icu::DateTimePatternGenerator& generator) {
     919             :     std::string key;
     920        3108 :     skeleton.toUTF8String<std::string>(key);
     921             :     key += ":";
     922             :     key += icu_locale.getName();
     923             : 
     924        3108 :     base::MutexGuard guard(&mutex_);
     925             :     auto it = map_.find(key);
     926        3108 :     if (it != map_.end()) {
     927        1475 :       return static_cast<icu::SimpleDateFormat*>(it->second->clone());
     928             :     }
     929             : 
     930        1633 :     if (map_.size() > 8) {  // Cache at most 8 DateFormats.
     931             :       map_.clear();
     932             :     }
     933             :     std::unique_ptr<icu::SimpleDateFormat> instance(
     934        1633 :         CreateICUDateFormat(icu_locale, skeleton, generator));
     935        1633 :     if (instance.get() == nullptr) return nullptr;
     936        1624 :     map_[key] = std::move(instance);
     937        3248 :     return static_cast<icu::SimpleDateFormat*>(map_[key]->clone());
     938             :   }
     939             : 
     940             :  private:
     941             :   std::map<std::string, std::unique_ptr<icu::SimpleDateFormat>> map_;
     942             :   base::Mutex mutex_;
     943             : };
     944             : 
     945        3108 : std::unique_ptr<icu::SimpleDateFormat> CreateICUDateFormatFromCache(
     946             :     const icu::Locale& icu_locale, const icu::UnicodeString& skeleton,
     947             :     icu::DateTimePatternGenerator& generator) {
     948             :   static base::LazyInstance<DateFormatCache>::type cache =
     949             :       LAZY_INSTANCE_INITIALIZER;
     950             :   return std::unique_ptr<icu::SimpleDateFormat>(
     951        6216 :       cache.Pointer()->Create(icu_locale, skeleton, generator));
     952             : }
     953             : 
     954        3801 : Intl::HourCycle HourCycleFromPattern(const icu::UnicodeString pattern) {
     955             :   bool in_quote = false;
     956        8679 :   for (int32_t i = 0; i < pattern.length(); i++) {
     957             :     char16_t ch = pattern[i];
     958        6240 :     switch (ch) {
     959             :       case '\'':
     960         180 :         in_quote = !in_quote;
     961         180 :         break;
     962             :       case 'K':
     963           0 :         if (!in_quote) return Intl::HourCycle::kH11;
     964             :         break;
     965             :       case 'h':
     966        3531 :         if (!in_quote) return Intl::HourCycle::kH12;
     967             :         break;
     968             :       case 'H':
     969         270 :         if (!in_quote) return Intl::HourCycle::kH23;
     970             :         break;
     971             :       case 'k':
     972           0 :         if (!in_quote) return Intl::HourCycle::kH24;
     973             :         break;
     974             :     }
     975             :   }
     976             :   return Intl::HourCycle::kUndefined;
     977             : }
     978             : 
     979         432 : icu::DateFormat::EStyle DateTimeStyleToEStyle(
     980             :     JSDateTimeFormat::DateTimeStyle style) {
     981         432 :   switch (style) {
     982             :     case JSDateTimeFormat::DateTimeStyle::kFull:
     983             :       return icu::DateFormat::EStyle::kFull;
     984             :     case JSDateTimeFormat::DateTimeStyle::kLong:
     985          90 :       return icu::DateFormat::EStyle::kLong;
     986             :     case JSDateTimeFormat::DateTimeStyle::kMedium:
     987          90 :       return icu::DateFormat::EStyle::kMedium;
     988             :     case JSDateTimeFormat::DateTimeStyle::kShort:
     989          90 :       return icu::DateFormat::EStyle::kShort;
     990             :     case JSDateTimeFormat::DateTimeStyle::kUndefined:
     991           0 :       UNREACHABLE();
     992             :   }
     993           0 : }
     994             : 
     995           0 : icu::UnicodeString ReplaceSkeleton(const icu::UnicodeString input,
     996             :                                    Intl::HourCycle hc) {
     997             :   icu::UnicodeString result;
     998             :   char16_t to;
     999           0 :   switch (hc) {
    1000             :     case Intl::HourCycle::kH11:
    1001             :       to = 'K';
    1002           0 :       break;
    1003             :     case Intl::HourCycle::kH12:
    1004             :       to = 'h';
    1005           0 :       break;
    1006             :     case Intl::HourCycle::kH23:
    1007             :       to = 'H';
    1008           0 :       break;
    1009             :     case Intl::HourCycle::kH24:
    1010             :       to = 'k';
    1011           0 :       break;
    1012             :     case Intl::HourCycle::kUndefined:
    1013           0 :       UNREACHABLE();
    1014             :   }
    1015           0 :   for (int32_t i = 0; i < input.length(); i++) {
    1016             :     switch (input[i]) {
    1017             :       // We need to skip 'a', 'b', 'B' here due to
    1018             :       // https://unicode-org.atlassian.net/browse/ICU-20437
    1019             :       case 'a':
    1020             :         V8_FALLTHROUGH;
    1021             :       case 'b':
    1022             :         V8_FALLTHROUGH;
    1023             :       case 'B':
    1024             :         // ignore
    1025             :         break;
    1026             :       case 'h':
    1027             :         V8_FALLTHROUGH;
    1028             :       case 'H':
    1029             :         V8_FALLTHROUGH;
    1030             :       case 'K':
    1031             :         V8_FALLTHROUGH;
    1032             :       case 'k':
    1033             :         result += to;
    1034             :         break;
    1035             :       default:
    1036             :         result += input[i];
    1037             :         break;
    1038             :     }
    1039             :   }
    1040           0 :   return result;
    1041             : }
    1042             : 
    1043         270 : std::unique_ptr<icu::SimpleDateFormat> DateTimeStylePattern(
    1044             :     JSDateTimeFormat::DateTimeStyle date_style,
    1045             :     JSDateTimeFormat::DateTimeStyle time_style, const icu::Locale& icu_locale,
    1046             :     Intl::HourCycle hc, icu::DateTimePatternGenerator& generator) {
    1047             :   std::unique_ptr<icu::SimpleDateFormat> result;
    1048         270 :   if (date_style != JSDateTimeFormat::DateTimeStyle::kUndefined) {
    1049         216 :     if (time_style != JSDateTimeFormat::DateTimeStyle::kUndefined) {
    1050             :       result.reset(reinterpret_cast<icu::SimpleDateFormat*>(
    1051         162 :           icu::DateFormat::createDateTimeInstance(
    1052             :               DateTimeStyleToEStyle(date_style),
    1053             :               DateTimeStyleToEStyle(time_style), icu_locale)));
    1054             :     } else {
    1055             :       result.reset(reinterpret_cast<icu::SimpleDateFormat*>(
    1056          54 :           icu::DateFormat::createDateInstance(DateTimeStyleToEStyle(date_style),
    1057             :                                               icu_locale)));
    1058             :       // For instance without time, we do not need to worry about the hour cycle
    1059             :       // impact so we can return directly.
    1060             :       return result;
    1061             :     }
    1062             :   } else {
    1063          54 :     if (time_style != JSDateTimeFormat::DateTimeStyle::kUndefined) {
    1064             :       result.reset(reinterpret_cast<icu::SimpleDateFormat*>(
    1065          54 :           icu::DateFormat::createTimeInstance(DateTimeStyleToEStyle(time_style),
    1066             :                                               icu_locale)));
    1067             :     } else {
    1068           0 :       UNREACHABLE();
    1069             :     }
    1070             :   }
    1071         216 :   icu::UnicodeString pattern;
    1072         216 :   pattern = result->toPattern(pattern);
    1073             : 
    1074         216 :   UErrorCode status = U_ZERO_ERROR;
    1075             :   icu::UnicodeString skeleton =
    1076         432 :       icu::DateTimePatternGenerator::staticGetSkeleton(pattern, status);
    1077         216 :   CHECK(U_SUCCESS(status));
    1078             : 
    1079             :   // If the skeleton match the HourCycle, we just return it.
    1080         216 :   if (hc == HourCycleFromPattern(pattern)) {
    1081             :     return result;
    1082             :   }
    1083             : 
    1084           0 :   return CreateICUDateFormatFromCache(icu_locale, ReplaceSkeleton(skeleton, hc),
    1085           0 :                                       generator);
    1086             : }
    1087             : 
    1088         290 : class DateTimePatternGeneratorCache {
    1089             :  public:
    1090             :   // Return a clone copy that the caller have to free.
    1091        3585 :   icu::DateTimePatternGenerator* CreateGenerator(const icu::Locale& locale) {
    1092        3585 :     std::string key(locale.getBaseName());
    1093        3585 :     base::MutexGuard guard(&mutex_);
    1094             :     auto it = map_.find(key);
    1095        3585 :     if (it != map_.end()) {
    1096        3190 :       return it->second->clone();
    1097             :     }
    1098         395 :     UErrorCode status = U_ZERO_ERROR;
    1099         790 :     map_[key].reset(icu::DateTimePatternGenerator::createInstance(
    1100             :         icu::Locale(key.c_str()), status));
    1101         395 :     CHECK(U_SUCCESS(status));
    1102         790 :     return map_[key]->clone();
    1103             :   }
    1104             : 
    1105             :  private:
    1106             :   std::map<std::string, std::unique_ptr<icu::DateTimePatternGenerator>> map_;
    1107             :   base::Mutex mutex_;
    1108             : };
    1109             : 
    1110             : }  // namespace
    1111             : 
    1112             : enum FormatMatcherOption { kBestFit, kBasic };
    1113             : 
    1114             : // ecma402/#sec-initializedatetimeformat
    1115        3756 : MaybeHandle<JSDateTimeFormat> JSDateTimeFormat::Initialize(
    1116             :     Isolate* isolate, Handle<JSDateTimeFormat> date_time_format,
    1117             :     Handle<Object> locales, Handle<Object> input_options) {
    1118             :   date_time_format->set_flags(0);
    1119             :   // 1. Let requestedLocales be ? CanonicalizeLocaleList(locales).
    1120             :   Maybe<std::vector<std::string>> maybe_requested_locales =
    1121        3756 :       Intl::CanonicalizeLocaleList(isolate, locales);
    1122        3756 :   MAYBE_RETURN(maybe_requested_locales, Handle<JSDateTimeFormat>());
    1123             :   std::vector<std::string> requested_locales =
    1124        3747 :       maybe_requested_locales.FromJust();
    1125             :   // 2. Let options be ? ToDateTimeOptions(options, "any", "date").
    1126             :   Handle<JSObject> options;
    1127        7494 :   ASSIGN_RETURN_ON_EXCEPTION(
    1128             :       isolate, options,
    1129             :       JSDateTimeFormat::ToDateTimeOptions(
    1130             :           isolate, input_options, RequiredOption::kAny, DefaultsOption::kDate),
    1131             :       JSDateTimeFormat);
    1132             : 
    1133             :   // 4. Let matcher be ? GetOption(options, "localeMatcher", "string",
    1134             :   // « "lookup", "best fit" », "best fit").
    1135             :   // 5. Set opt.[[localeMatcher]] to matcher.
    1136             :   Maybe<Intl::MatcherOption> maybe_locale_matcher =
    1137        3747 :       Intl::GetLocaleMatcher(isolate, options, "Intl.DateTimeFormat");
    1138        3747 :   MAYBE_RETURN(maybe_locale_matcher, MaybeHandle<JSDateTimeFormat>());
    1139             :   Intl::MatcherOption locale_matcher = maybe_locale_matcher.FromJust();
    1140             : 
    1141             :   // 6. Let hour12 be ? GetOption(options, "hour12", "boolean", undefined,
    1142             :   // undefined).
    1143             :   bool hour12;
    1144             :   Maybe<bool> maybe_get_hour12 = Intl::GetBoolOption(
    1145        3747 :       isolate, options, "hour12", "Intl.DateTimeFormat", &hour12);
    1146        3747 :   MAYBE_RETURN(maybe_get_hour12, Handle<JSDateTimeFormat>());
    1147             : 
    1148             :   // 7. Let hourCycle be ? GetOption(options, "hourCycle", "string", « "h11",
    1149             :   // "h12", "h23", "h24" », undefined).
    1150             :   Maybe<Intl::HourCycle> maybe_hour_cycle =
    1151        3747 :       Intl::GetHourCycle(isolate, options, "Intl.DateTimeFormat");
    1152        3747 :   MAYBE_RETURN(maybe_hour_cycle, MaybeHandle<JSDateTimeFormat>());
    1153             :   Intl::HourCycle hour_cycle = maybe_hour_cycle.FromJust();
    1154             : 
    1155             :   // 8. If hour12 is not undefined, then
    1156        3747 :   if (maybe_get_hour12.FromJust()) {
    1157             :     // a. Let hourCycle be null.
    1158             :     hour_cycle = Intl::HourCycle::kUndefined;
    1159             :   }
    1160             :   // 9. Set opt.[[hc]] to hourCycle.
    1161             : 
    1162             :   // ecma402/#sec-intl.datetimeformat-internal-slots
    1163             :   // The value of the [[RelevantExtensionKeys]] internal slot is
    1164             :   // « "ca", "nu", "hc" ».
    1165        7494 :   std::set<std::string> relevant_extension_keys = {"nu", "ca", "hc"};
    1166             : 
    1167             :   // 10. Let localeData be %DateTimeFormat%.[[LocaleData]].
    1168             :   // 11. Let r be ResolveLocale( %DateTimeFormat%.[[AvailableLocales]],
    1169             :   //     requestedLocales, opt, %DateTimeFormat%.[[RelevantExtensionKeys]],
    1170             :   //     localeData).
    1171             :   //
    1172             :   Intl::ResolvedLocale r = Intl::ResolveLocale(
    1173             :       isolate, JSDateTimeFormat::GetAvailableLocales(), requested_locales,
    1174        7494 :       locale_matcher, relevant_extension_keys);
    1175             : 
    1176        7494 :   icu::Locale icu_locale = r.icu_locale;
    1177             :   DCHECK(!icu_locale.isBogus());
    1178             : 
    1179             :   // 17. Let timeZone be ? Get(options, "timeZone").
    1180             :   const std::vector<const char*> empty_values;
    1181             :   std::unique_ptr<char[]> timezone = nullptr;
    1182             :   Maybe<bool> maybe_timezone =
    1183             :       Intl::GetStringOption(isolate, options, "timeZone", empty_values,
    1184       11241 :                             "Intl.DateTimeFormat", &timezone);
    1185        3747 :   MAYBE_RETURN(maybe_timezone, Handle<JSDateTimeFormat>());
    1186             : 
    1187        3729 :   std::unique_ptr<icu::TimeZone> tz = CreateTimeZone(isolate, timezone.get());
    1188        3729 :   if (tz.get() == nullptr) {
    1189         432 :     THROW_NEW_ERROR(isolate,
    1190             :                     NewRangeError(MessageTemplate::kInvalidTimeZone,
    1191             :                                   isolate->factory()->NewStringFromAsciiChecked(
    1192             :                                       timezone.get())),
    1193             :                     JSDateTimeFormat);
    1194             :   }
    1195             : 
    1196             :   std::unique_ptr<icu::Calendar> calendar(
    1197        3585 :       CreateCalendar(isolate, icu_locale, tz.release()));
    1198             : 
    1199             :   // 18.b If the result of IsValidTimeZoneName(timeZone) is false, then
    1200             :   // i. Throw a RangeError exception.
    1201        3585 :   if (calendar.get() == nullptr) {
    1202           0 :     THROW_NEW_ERROR(isolate,
    1203             :                     NewRangeError(MessageTemplate::kInvalidTimeZone,
    1204             :                                   isolate->factory()->NewStringFromAsciiChecked(
    1205             :                                       timezone.get())),
    1206             :                     JSDateTimeFormat);
    1207             :   }
    1208             : 
    1209             :   static base::LazyInstance<DateTimePatternGeneratorCache>::type
    1210             :       generator_cache = LAZY_INSTANCE_INITIALIZER;
    1211             : 
    1212             :   std::unique_ptr<icu::DateTimePatternGenerator> generator(
    1213        3585 :       generator_cache.Pointer()->CreateGenerator(icu_locale));
    1214             : 
    1215             :   // 15.Let hcDefault be dataLocaleData.[[hourCycle]].
    1216        3585 :   UErrorCode status = U_ZERO_ERROR;
    1217        7170 :   icu::UnicodeString hour_pattern = generator->getBestPattern("jjmm", status);
    1218        3585 :   CHECK(U_SUCCESS(status));
    1219        3585 :   Intl::HourCycle hc_default = HourCycleFromPattern(hour_pattern);
    1220             : 
    1221             :   // 16.Let hc be r.[[hc]].
    1222             :   Intl::HourCycle hc = Intl::HourCycle::kUndefined;
    1223        3585 :   if (hour_cycle == Intl::HourCycle::kUndefined) {
    1224        7170 :     auto hc_extension_it = r.extensions.find("hc");
    1225        3585 :     if (hc_extension_it != r.extensions.end()) {
    1226         216 :       hc = Intl::ToHourCycle(hc_extension_it->second.c_str());
    1227             :     }
    1228             :   } else {
    1229             :     hc = hour_cycle;
    1230             :   }
    1231             :   // 17. If hc is null, then
    1232        3585 :   if (hc == Intl::HourCycle::kUndefined) {
    1233             :     // a. Set hc to hcDefault.
    1234             :     hc = hc_default;
    1235             :   }
    1236             : 
    1237             :   // 18. If hour12 is not undefined, then
    1238        3585 :   if (maybe_get_hour12.FromJust()) {
    1239             :     // a. If hour12 is true, then
    1240          36 :     if (hour12) {
    1241             :       // i. If hcDefault is "h11" or "h23", then
    1242          36 :       if (hc_default == Intl::HourCycle::kH11 ||
    1243          18 :           hc_default == Intl::HourCycle::kH23) {
    1244             :         // 1. Set hc to "h11".
    1245             :         hc = Intl::HourCycle::kH11;
    1246             :         // ii. Else,
    1247             :       } else {
    1248             :         // 1. Set hc to "h12".
    1249             :         hc = Intl::HourCycle::kH12;
    1250             :       }
    1251             :       // b. Else,
    1252             :     } else {
    1253             :       // ii. If hcDefault is "h11" or "h23", then
    1254          36 :       if (hc_default == Intl::HourCycle::kH11 ||
    1255          18 :           hc_default == Intl::HourCycle::kH23) {
    1256             :         // 1. Set hc to "h23".
    1257             :         hc = Intl::HourCycle::kH23;
    1258             :         // iii. Else,
    1259             :       } else {
    1260             :         // 1. Set hc to "h24".
    1261             :         hc = Intl::HourCycle::kH24;
    1262             :       }
    1263             :     }
    1264             :   }
    1265        3585 :   date_time_format->set_hour_cycle(hc);
    1266             : 
    1267             :   DateTimeStyle date_style = DateTimeStyle::kUndefined;
    1268             :   DateTimeStyle time_style = DateTimeStyle::kUndefined;
    1269             :   std::unique_ptr<icu::SimpleDateFormat> icu_date_format;
    1270             : 
    1271        3585 :   if (FLAG_harmony_intl_datetime_style) {
    1272             :     // 28. Let dateStyle be ? GetOption(options, "dateStyle", "string", «
    1273             :     // "full", "long", "medium", "short" », undefined).
    1274             :     Maybe<DateTimeStyle> maybe_date_style =
    1275             :         Intl::GetStringOption<DateTimeStyle>(
    1276             :             isolate, options, "dateStyle", "Intl.DateTimeFormat",
    1277             :             {"full", "long", "medium", "short"},
    1278             :             {DateTimeStyle::kFull, DateTimeStyle::kLong, DateTimeStyle::kMedium,
    1279             :              DateTimeStyle::kShort},
    1280        1593 :             DateTimeStyle::kUndefined);
    1281         531 :     MAYBE_RETURN(maybe_date_style, MaybeHandle<JSDateTimeFormat>());
    1282             :     // 29. If dateStyle is not undefined, set dateTimeFormat.[[DateStyle]] to
    1283             :     // dateStyle.
    1284             :     date_style = maybe_date_style.FromJust();
    1285         405 :     if (date_style != DateTimeStyle::kUndefined) {
    1286         288 :       date_time_format->set_date_style(date_style);
    1287             :     }
    1288             : 
    1289             :     // 30. Let timeStyle be ? GetOption(options, "timeStyle", "string", «
    1290             :     // "full", "long", "medium", "short" »).
    1291             :     Maybe<DateTimeStyle> maybe_time_style =
    1292             :         Intl::GetStringOption<DateTimeStyle>(
    1293             :             isolate, options, "timeStyle", "Intl.DateTimeFormat",
    1294             :             {"full", "long", "medium", "short"},
    1295             :             {DateTimeStyle::kFull, DateTimeStyle::kLong, DateTimeStyle::kMedium,
    1296             :              DateTimeStyle::kShort},
    1297        1215 :             DateTimeStyle::kUndefined);
    1298         405 :     MAYBE_RETURN(maybe_time_style, MaybeHandle<JSDateTimeFormat>());
    1299             : 
    1300             :     // 31. If timeStyle is not undefined, set dateTimeFormat.[[TimeStyle]] to
    1301             :     // timeStyle.
    1302             :     time_style = maybe_time_style.FromJust();
    1303         315 :     if (time_style != DateTimeStyle::kUndefined) {
    1304         216 :       date_time_format->set_time_style(time_style);
    1305             :     }
    1306             : 
    1307             :     // 32. If dateStyle or timeStyle are not undefined, then
    1308         315 :     if (date_style != DateTimeStyle::kUndefined ||
    1309             :         time_style != DateTimeStyle::kUndefined) {
    1310         540 :       icu_date_format = DateTimeStylePattern(date_style, time_style, icu_locale,
    1311             :                                              hc, *generator);
    1312             :     }
    1313             :   }
    1314             :   // 33. Else,
    1315        3369 :   if (icu_date_format.get() == nullptr) {
    1316             :     bool has_hour_option = false;
    1317             :     // b. For each row of Table 5, except the header row, do
    1318             :     std::string skeleton;
    1319        3099 :     for (const PatternData& item : GetPatternData(hc)) {
    1320       27891 :       std::unique_ptr<char[]> input;
    1321             :       // i. Let prop be the name given in the Property column of the row.
    1322             :       // ii. Let value be ? GetOption(options, prop, "string", « the strings
    1323             :       // given in the Values column of the row », undefined).
    1324             :       Maybe<bool> maybe_get_option = Intl::GetStringOption(
    1325             :           isolate, options, item.property.c_str(), item.allowed_values,
    1326       83673 :           "Intl.DateTimeFormat", &input);
    1327       27891 :       MAYBE_RETURN(maybe_get_option, Handle<JSDateTimeFormat>());
    1328       27891 :       if (maybe_get_option.FromJust()) {
    1329       17610 :         if (item.property == "hour") {
    1330             :           has_hour_option = true;
    1331             :         }
    1332             :         DCHECK_NOT_NULL(input.get());
    1333             :         // iii. Set opt.[[<prop>]] to value.
    1334       17610 :         skeleton += item.map.find(input.get())->second;
    1335             :       }
    1336             :     }
    1337             : 
    1338             :     enum FormatMatcherOption { kBestFit, kBasic };
    1339             :     // We implement only best fit algorithm, but still need to check
    1340             :     // if the formatMatcher values are in range.
    1341             :     // c. Let matcher be ? GetOption(options, "formatMatcher", "string",
    1342             :     //     «  "basic", "best fit" », "best fit").
    1343             :     Maybe<FormatMatcherOption> maybe_format_matcher =
    1344             :         Intl::GetStringOption<FormatMatcherOption>(
    1345             :             isolate, options, "formatMatcher", "Intl.DateTimeFormat",
    1346             :             {"best fit", "basic"},
    1347             :             {FormatMatcherOption::kBestFit, FormatMatcherOption::kBasic},
    1348        9297 :             FormatMatcherOption::kBestFit);
    1349        3099 :     MAYBE_RETURN(maybe_format_matcher, MaybeHandle<JSDateTimeFormat>());
    1350             :     // TODO(ftang): uncomment the following line and handle format_matcher.
    1351             :     // FormatMatcherOption format_matcher = maybe_format_matcher.FromJust();
    1352             : 
    1353        6198 :     icu::UnicodeString skeleton_ustr(skeleton.c_str());
    1354             :     icu_date_format =
    1355        6198 :         CreateICUDateFormatFromCache(icu_locale, skeleton_ustr, *generator);
    1356        3099 :     if (icu_date_format.get() == nullptr) {
    1357             :       // Remove extensions and try again.
    1358           9 :       icu_locale = icu::Locale(icu_locale.getBaseName());
    1359             :       icu_date_format =
    1360          18 :           CreateICUDateFormatFromCache(icu_locale, skeleton_ustr, *generator);
    1361           9 :       if (icu_date_format.get() == nullptr) {
    1362           0 :         FATAL("Failed to create ICU date format, are ICU data files missing?");
    1363             :       }
    1364             :     }
    1365             : 
    1366             :     // g. If dateTimeFormat.[[Hour]] is not undefined, then
    1367        3099 :     if (!has_hour_option) {
    1368             :       // h. Else, i. Set dateTimeFormat.[[HourCycle]] to undefined.
    1369        2683 :       date_time_format->set_hour_cycle(Intl::HourCycle::kUndefined);
    1370             :     }
    1371             :   }
    1372             : 
    1373             :   // The creation of Calendar depends on timeZone so we have to put 13 after 17.
    1374             :   // Also icu_date_format is not created until here.
    1375             :   // 13. Set dateTimeFormat.[[Calendar]] to r.[[ca]].
    1376        3369 :   icu_date_format->adoptCalendar(calendar.release());
    1377             : 
    1378             :   // 12.1.1 InitializeDateTimeFormat ( dateTimeFormat, locales, options )
    1379             :   //
    1380             :   // Steps 8-9 set opt.[[hc]] to value *other than undefined*
    1381             :   // if "hour12" is set or "hourCycle" is set in the option.
    1382             :   //
    1383             :   // 9.2.6 ResolveLocale (... )
    1384             :   // Step 8.h / 8.i and 8.k
    1385             :   //
    1386             :   // An hour12 option always overrides an hourCycle option.
    1387             :   // Additionally hour12 and hourCycle both clear out any existing Unicode
    1388             :   // extension key in the input locale.
    1389             :   //
    1390             :   // See details in https://github.com/tc39/test262/pull/2035
    1391        6702 :   if (maybe_get_hour12.FromJust() ||
    1392             :       maybe_hour_cycle.FromJust() != Intl::HourCycle::kUndefined) {
    1393          72 :     auto hc_extension_it = r.extensions.find("hc");
    1394          36 :     if (hc_extension_it != r.extensions.end()) {
    1395           0 :       if (date_time_format->hour_cycle() !=
    1396           0 :           Intl::ToHourCycle(hc_extension_it->second.c_str())) {
    1397             :         // Remove -hc- if it does not agree with what we used.
    1398           0 :         UErrorCode status = U_ZERO_ERROR;
    1399           0 :         icu_locale.setKeywordValue(uloc_toLegacyKey("hc"), nullptr, status);
    1400           0 :         CHECK(U_SUCCESS(status));
    1401             :       }
    1402             :     }
    1403             :   }
    1404             : 
    1405             :   Handle<Managed<icu::Locale>> managed_locale =
    1406        3369 :       Managed<icu::Locale>::FromRawPtr(isolate, 0, icu_locale.clone());
    1407             : 
    1408        3369 :   date_time_format->set_icu_locale(*managed_locale);
    1409             :   Handle<Managed<icu::SimpleDateFormat>> managed_format =
    1410             :       Managed<icu::SimpleDateFormat>::FromUniquePtr(isolate, 0,
    1411        6738 :                                                     std::move(icu_date_format));
    1412        3369 :   date_time_format->set_icu_simple_date_format(*managed_format);
    1413             : 
    1414        3369 :   return date_time_format;
    1415             : }
    1416             : 
    1417             : namespace {
    1418             : 
    1419             : // The list comes from third_party/icu/source/i18n/unicode/udat.h.
    1420             : // They're mapped to DateTimeFormat components listed at
    1421             : // https://tc39.github.io/ecma402/#sec-datetimeformat-abstracts .
    1422         387 : Handle<String> IcuDateFieldIdToDateType(int32_t field_id, Isolate* isolate) {
    1423         387 :   switch (field_id) {
    1424             :     case -1:
    1425             :       return isolate->factory()->literal_string();
    1426             :     case UDAT_YEAR_FIELD:
    1427             :     case UDAT_EXTENDED_YEAR_FIELD:
    1428             :     case UDAT_YEAR_NAME_FIELD:
    1429             :       return isolate->factory()->year_string();
    1430             :     case UDAT_MONTH_FIELD:
    1431             :     case UDAT_STANDALONE_MONTH_FIELD:
    1432             :       return isolate->factory()->month_string();
    1433             :     case UDAT_DATE_FIELD:
    1434             :       return isolate->factory()->day_string();
    1435             :     case UDAT_HOUR_OF_DAY1_FIELD:
    1436             :     case UDAT_HOUR_OF_DAY0_FIELD:
    1437             :     case UDAT_HOUR1_FIELD:
    1438             :     case UDAT_HOUR0_FIELD:
    1439             :       return isolate->factory()->hour_string();
    1440             :     case UDAT_MINUTE_FIELD:
    1441             :       return isolate->factory()->minute_string();
    1442             :     case UDAT_SECOND_FIELD:
    1443             :       return isolate->factory()->second_string();
    1444             :     case UDAT_DAY_OF_WEEK_FIELD:
    1445             :     case UDAT_DOW_LOCAL_FIELD:
    1446             :     case UDAT_STANDALONE_DAY_FIELD:
    1447             :       return isolate->factory()->weekday_string();
    1448             :     case UDAT_AM_PM_FIELD:
    1449             :       return isolate->factory()->dayPeriod_string();
    1450             :     case UDAT_TIMEZONE_FIELD:
    1451             :     case UDAT_TIMEZONE_RFC_FIELD:
    1452             :     case UDAT_TIMEZONE_GENERIC_FIELD:
    1453             :     case UDAT_TIMEZONE_SPECIAL_FIELD:
    1454             :     case UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD:
    1455             :     case UDAT_TIMEZONE_ISO_FIELD:
    1456             :     case UDAT_TIMEZONE_ISO_LOCAL_FIELD:
    1457             :       return isolate->factory()->timeZoneName_string();
    1458             :     case UDAT_ERA_FIELD:
    1459             :       return isolate->factory()->era_string();
    1460             :     default:
    1461             :       // Other UDAT_*_FIELD's cannot show up because there is no way to specify
    1462             :       // them via options of Intl.DateTimeFormat.
    1463           0 :       UNREACHABLE();
    1464             :       // To prevent MSVC from issuing C4715 warning.
    1465             :       return Handle<String>();
    1466             :   }
    1467             : }
    1468             : 
    1469             : }  // namespace
    1470             : 
    1471         153 : MaybeHandle<Object> JSDateTimeFormat::FormatToParts(
    1472             :     Isolate* isolate, Handle<JSDateTimeFormat> date_time_format,
    1473             :     double date_value) {
    1474             :   Factory* factory = isolate->factory();
    1475             :   icu::SimpleDateFormat* format =
    1476             :       date_time_format->icu_simple_date_format()->raw();
    1477         153 :   CHECK_NOT_NULL(format);
    1478             : 
    1479         153 :   icu::UnicodeString formatted;
    1480         306 :   icu::FieldPositionIterator fp_iter;
    1481         153 :   icu::FieldPosition fp;
    1482         153 :   UErrorCode status = U_ZERO_ERROR;
    1483         153 :   format->format(date_value, formatted, &fp_iter, status);
    1484         153 :   if (U_FAILURE(status)) {
    1485           0 :     THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError), Object);
    1486             :   }
    1487             : 
    1488             :   Handle<JSArray> result = factory->NewJSArray(0);
    1489             :   int32_t length = formatted.length();
    1490         153 :   if (length == 0) return result;
    1491             : 
    1492             :   int index = 0;
    1493             :   int32_t previous_end_pos = 0;
    1494             :   Handle<String> substring;
    1495         927 :   while (fp_iter.next(fp)) {
    1496             :     int32_t begin_pos = fp.getBeginIndex();
    1497             :     int32_t end_pos = fp.getEndIndex();
    1498             : 
    1499         387 :     if (previous_end_pos < begin_pos) {
    1500         432 :       ASSIGN_RETURN_ON_EXCEPTION(
    1501             :           isolate, substring,
    1502             :           Intl::ToString(isolate, formatted, previous_end_pos, begin_pos),
    1503             :           Object);
    1504             :       Intl::AddElement(isolate, result, index,
    1505         216 :                        IcuDateFieldIdToDateType(-1, isolate), substring);
    1506         216 :       ++index;
    1507             :     }
    1508         774 :     ASSIGN_RETURN_ON_EXCEPTION(
    1509             :         isolate, substring,
    1510             :         Intl::ToString(isolate, formatted, begin_pos, end_pos), Object);
    1511         387 :     Intl::AddElement(isolate, result, index,
    1512             :                      IcuDateFieldIdToDateType(fp.getField(), isolate),
    1513         387 :                      substring);
    1514             :     previous_end_pos = end_pos;
    1515         387 :     ++index;
    1516             :   }
    1517         153 :   if (previous_end_pos < length) {
    1518           0 :     ASSIGN_RETURN_ON_EXCEPTION(
    1519             :         isolate, substring,
    1520             :         Intl::ToString(isolate, formatted, previous_end_pos, length), Object);
    1521             :     Intl::AddElement(isolate, result, index,
    1522           0 :                      IcuDateFieldIdToDateType(-1, isolate), substring);
    1523             :   }
    1524         153 :   JSObject::ValidateElements(*result);
    1525         153 :   return result;
    1526             : }
    1527             : 
    1528         131 : const std::set<std::string>& JSDateTimeFormat::GetAvailableLocales() {
    1529        3878 :   return Intl::GetAvailableLocalesForDateFormat();
    1530             : }
    1531             : 
    1532         225 : Handle<String> JSDateTimeFormat::HourCycleAsString() const {
    1533         225 :   switch (hour_cycle()) {
    1534             :     case Intl::HourCycle::kUndefined:
    1535             :       return GetReadOnlyRoots().undefined_string_handle();
    1536             :     case Intl::HourCycle::kH11:
    1537             :       return GetReadOnlyRoots().h11_string_handle();
    1538             :     case Intl::HourCycle::kH12:
    1539             :       return GetReadOnlyRoots().h12_string_handle();
    1540             :     case Intl::HourCycle::kH23:
    1541             :       return GetReadOnlyRoots().h23_string_handle();
    1542             :     case Intl::HourCycle::kH24:
    1543             :       return GetReadOnlyRoots().h24_string_handle();
    1544             :     default:
    1545           0 :       UNREACHABLE();
    1546             :   }
    1547             : }
    1548             : 
    1549             : }  // namespace internal
    1550      120216 : }  // namespace v8

Generated by: LCOV version 1.10