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

Generated by: LCOV version 1.10