LCOV - code coverage report
Current view: top level - src/objects - js-date-time-format.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 403 444 90.8 %
Date: 2019-02-19 Functions: 49 58 84.5 %

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

Generated by: LCOV version 1.10