diff options
Diffstat (limited to 'deps/v8/src/objects/js-date-time-format.cc')
-rw-r--r-- | deps/v8/src/objects/js-date-time-format.cc | 689 |
1 files changed, 539 insertions, 150 deletions
diff --git a/deps/v8/src/objects/js-date-time-format.cc b/deps/v8/src/objects/js-date-time-format.cc index b3b1d11253..3c1405f563 100644 --- a/deps/v8/src/objects/js-date-time-format.cc +++ b/deps/v8/src/objects/js-date-time-format.cc @@ -54,7 +54,7 @@ class PatternItem { std::vector<const char*> allowed_values; }; -const std::vector<PatternItem> GetPatternItems() { +static const std::vector<PatternItem> BuildPatternItems() { const std::vector<const char*> kLongShort = {"long", "short"}; const std::vector<const char*> kNarrowLongShort = {"narrow", "long", "short"}; const std::vector<const char*> k2DigitNumeric = {"2-digit", "numeric"}; @@ -107,6 +107,22 @@ const std::vector<PatternItem> GetPatternItems() { return kPatternItems; } +class PatternItems { + public: + PatternItems() : data(BuildPatternItems()) {} + virtual ~PatternItems() {} + const std::vector<PatternItem>& Get() const { return data; } + + private: + const std::vector<PatternItem> data; +}; + +static const std::vector<PatternItem>& GetPatternItems() { + static base::LazyInstance<PatternItems>::type items = + LAZY_INSTANCE_INITIALIZER; + return items.Pointer()->Get(); +} + class PatternData { public: PatternData(const std::string property, std::vector<PatternMap> pairs, @@ -154,23 +170,57 @@ const std::vector<PatternData> CreateData(const char* digit2, // kk 24 // K hour in am/pm (0~11) K 0 // KK 00 -const std::vector<PatternData> GetPatternData(Intl::HourCycle hour_cycle) { - const std::vector<PatternData> data = CreateData("jj", "j"); - const std::vector<PatternData> data_h11 = CreateData("KK", "K"); - const std::vector<PatternData> data_h12 = CreateData("hh", "h"); - const std::vector<PatternData> data_h23 = CreateData("HH", "H"); - const std::vector<PatternData> data_h24 = CreateData("kk", "k"); + +class Pattern { + public: + Pattern(const char* d1, const char* d2) : data(CreateData(d1, d2)) {} + virtual ~Pattern() {} + virtual const std::vector<PatternData>& Get() const { return data; } + + private: + std::vector<PatternData> data; +}; + +#define DEFFINE_TRAIT(name, d1, d2) \ + struct name { \ + static void Construct(void* allocated_ptr) { \ + new (allocated_ptr) Pattern(d1, d2); \ + } \ + }; +DEFFINE_TRAIT(H11Trait, "KK", "K") +DEFFINE_TRAIT(H12Trait, "hh", "h") +DEFFINE_TRAIT(H23Trait, "HH", "H") +DEFFINE_TRAIT(H24Trait, "kk", "k") +DEFFINE_TRAIT(HDefaultTrait, "jj", "j") +#undef DEFFINE_TRAIT + +const std::vector<PatternData>& GetPatternData(Intl::HourCycle hour_cycle) { switch (hour_cycle) { - case Intl::HourCycle::kH11: - return data_h11; - case Intl::HourCycle::kH12: - return data_h12; - case Intl::HourCycle::kH23: - return data_h23; - case Intl::HourCycle::kH24: - return data_h24; - case Intl::HourCycle::kUndefined: - return data; + case Intl::HourCycle::kH11: { + static base::LazyInstance<Pattern, H11Trait>::type h11 = + LAZY_INSTANCE_INITIALIZER; + return h11.Pointer()->Get(); + } + case Intl::HourCycle::kH12: { + static base::LazyInstance<Pattern, H12Trait>::type h12 = + LAZY_INSTANCE_INITIALIZER; + return h12.Pointer()->Get(); + } + case Intl::HourCycle::kH23: { + static base::LazyInstance<Pattern, H23Trait>::type h23 = + LAZY_INSTANCE_INITIALIZER; + return h23.Pointer()->Get(); + } + case Intl::HourCycle::kH24: { + static base::LazyInstance<Pattern, H24Trait>::type h24 = + LAZY_INSTANCE_INITIALIZER; + return h24.Pointer()->Get(); + } + case Intl::HourCycle::kUndefined: { + static base::LazyInstance<Pattern, HDefaultTrait>::type hDefault = + LAZY_INSTANCE_INITIALIZER; + return hDefault.Pointer()->Get(); + } default: UNREACHABLE(); } @@ -273,6 +323,26 @@ std::string JSDateTimeFormat::CanonicalizeTimeZoneID(Isolate* isolate, return ToTitleCaseTimezoneLocation(isolate, input); } +namespace { + +Handle<String> DateTimeStyleAsString(Isolate* isolate, + JSDateTimeFormat::DateTimeStyle style) { + switch (style) { + case JSDateTimeFormat::DateTimeStyle::kFull: + return ReadOnlyRoots(isolate).full_string_handle(); + case JSDateTimeFormat::DateTimeStyle::kLong: + return ReadOnlyRoots(isolate).long_string_handle(); + case JSDateTimeFormat::DateTimeStyle::kMedium: + return ReadOnlyRoots(isolate).medium_string_handle(); + case JSDateTimeFormat::DateTimeStyle::kShort: + return ReadOnlyRoots(isolate).short_string_handle(); + case JSDateTimeFormat::DateTimeStyle::kUndefined: + UNREACHABLE(); + } +} + +} // namespace + // ecma402 #sec-intl.datetimeformat.prototype.resolvedoptions MaybeHandle<JSObject> JSDateTimeFormat::ResolvedOptions( Isolate* isolate, Handle<JSDateTimeFormat> date_time_format) { @@ -366,24 +436,25 @@ MaybeHandle<JSObject> JSDateTimeFormat::ResolvedOptions( // [[Minute]] "minute" // [[Second]] "second" // [[TimeZoneName]] "timeZoneName" - CHECK(JSReceiver::CreateDataProperty( - isolate, options, factory->locale_string(), locale, kDontThrow) + CHECK(JSReceiver::CreateDataProperty(isolate, options, + factory->locale_string(), locale, + Just(kDontThrow)) .FromJust()); CHECK(JSReceiver::CreateDataProperty( isolate, options, factory->calendar_string(), factory->NewStringFromAsciiChecked(calendar_str.c_str()), - kDontThrow) + Just(kDontThrow)) .FromJust()); if (!numbering_system.empty()) { CHECK(JSReceiver::CreateDataProperty( isolate, options, factory->numberingSystem_string(), factory->NewStringFromAsciiChecked(numbering_system.c_str()), - kDontThrow) + Just(kDontThrow)) .FromJust()); } CHECK(JSReceiver::CreateDataProperty(isolate, options, factory->timeZone_string(), - timezone_value, kDontThrow) + timezone_value, Just(kDontThrow)) .FromJust()); // 5.b.i. Let hc be dtf.[[HourCycle]]. @@ -392,23 +463,23 @@ MaybeHandle<JSObject> JSDateTimeFormat::ResolvedOptions( if (hc != Intl::HourCycle::kUndefined) { CHECK(JSReceiver::CreateDataProperty( isolate, options, factory->hourCycle_string(), - date_time_format->HourCycleAsString(), kDontThrow) + date_time_format->HourCycleAsString(), Just(kDontThrow)) .FromJust()); switch (hc) { // ii. If hc is "h11" or "h12", let v be true. case Intl::HourCycle::kH11: case Intl::HourCycle::kH12: - CHECK(JSReceiver::CreateDataProperty(isolate, options, - factory->hour12_string(), - factory->true_value(), kDontThrow) + CHECK(JSReceiver::CreateDataProperty( + isolate, options, factory->hour12_string(), + factory->true_value(), Just(kDontThrow)) .FromJust()); break; // iii. Else if, hc is "h23" or "h24", let v be false. case Intl::HourCycle::kH23: case Intl::HourCycle::kH24: - CHECK(JSReceiver::CreateDataProperty(isolate, options, - factory->hour12_string(), - factory->false_value(), kDontThrow) + CHECK(JSReceiver::CreateDataProperty( + isolate, options, factory->hour12_string(), + factory->false_value(), Just(kDontThrow)) .FromJust()); break; // iv. Else, let v be undefined. @@ -424,13 +495,31 @@ MaybeHandle<JSObject> JSDateTimeFormat::ResolvedOptions( isolate, options, factory->NewStringFromAsciiChecked(item.property.c_str()), factory->NewStringFromAsciiChecked(pair.value.c_str()), - kDontThrow) + Just(kDontThrow)) .FromJust()); break; } } } + // dateStyle + if (date_time_format->date_style() != DateTimeStyle::kUndefined) { + CHECK(JSReceiver::CreateDataProperty( + isolate, options, factory->dateStyle_string(), + DateTimeStyleAsString(isolate, date_time_format->date_style()), + Just(kDontThrow)) + .FromJust()); + } + + // timeStyle + if (date_time_format->time_style() != DateTimeStyle::kUndefined) { + CHECK(JSReceiver::CreateDataProperty( + isolate, options, factory->timeStyle_string(), + DateTimeStyleAsString(isolate, date_time_format->time_style()), + Just(kDontThrow)) + .FromJust()); + } + return options; } @@ -606,7 +695,7 @@ Maybe<bool> CreateDefault(Isolate* isolate, Handle<JSObject> options, MAYBE_RETURN( JSReceiver::CreateDataProperty( isolate, options, factory->NewStringFromAsciiChecked(prop.c_str()), - factory->numeric_string(), kThrowOnError), + factory->numeric_string(), Just(kThrowOnError)), Nothing<bool>()); } return Just(true); @@ -734,35 +823,62 @@ std::unique_ptr<icu::TimeZone> CreateTimeZone(Isolate* isolate, return tz; } -std::unique_ptr<icu::Calendar> CreateCalendar(Isolate* isolate, - const icu::Locale& icu_locale, - const char* timezone) { - std::unique_ptr<icu::TimeZone> tz = CreateTimeZone(isolate, timezone); - if (tz.get() == nullptr) return std::unique_ptr<icu::Calendar>(); - - // Create a calendar using locale, and apply time zone to it. - UErrorCode status = U_ZERO_ERROR; - std::unique_ptr<icu::Calendar> calendar( - icu::Calendar::createInstance(tz.release(), icu_locale, status)); - CHECK(U_SUCCESS(status)); - CHECK_NOT_NULL(calendar.get()); - - if (calendar->getDynamicClassID() == - icu::GregorianCalendar::getStaticClassID()) { - icu::GregorianCalendar* gc = - static_cast<icu::GregorianCalendar*>(calendar.get()); +class CalendarCache { + public: + icu::Calendar* CreateCalendar(const icu::Locale& locale, icu::TimeZone* tz) { + icu::UnicodeString tz_id; + tz->getID(tz_id); + std::string key; + tz_id.toUTF8String<std::string>(key); + key += ":"; + key += locale.getName(); + + base::MutexGuard guard(&mutex_); + auto it = map_.find(key); + if (it != map_.end()) { + delete tz; + return it->second->clone(); + } + // Create a calendar using locale, and apply time zone to it. UErrorCode status = U_ZERO_ERROR; - // The beginning of ECMAScript time, namely -(2**53) - const double start_of_time = -9007199254740992; - gc->setGregorianChange(start_of_time, status); - DCHECK(U_SUCCESS(status)); + std::unique_ptr<icu::Calendar> calendar( + icu::Calendar::createInstance(tz, locale, status)); + CHECK(U_SUCCESS(status)); + CHECK_NOT_NULL(calendar.get()); + + if (calendar->getDynamicClassID() == + icu::GregorianCalendar::getStaticClassID()) { + icu::GregorianCalendar* gc = + static_cast<icu::GregorianCalendar*>(calendar.get()); + UErrorCode status = U_ZERO_ERROR; + // The beginning of ECMAScript time, namely -(2**53) + const double start_of_time = -9007199254740992; + gc->setGregorianChange(start_of_time, status); + DCHECK(U_SUCCESS(status)); + } + + if (map_.size() > 8) { // Cache at most 8 calendars. + map_.clear(); + } + map_[key].reset(calendar.release()); + return map_[key]->clone(); } - return calendar; + + private: + std::map<std::string, std::unique_ptr<icu::Calendar>> map_; + base::Mutex mutex_; +}; + +icu::Calendar* CreateCalendar(Isolate* isolate, const icu::Locale& icu_locale, + icu::TimeZone* tz) { + static base::LazyInstance<CalendarCache>::type calendar_cache = + LAZY_INSTANCE_INITIALIZER; + return calendar_cache.Pointer()->CreateCalendar(icu_locale, tz); } std::unique_ptr<icu::SimpleDateFormat> CreateICUDateFormat( - Isolate* isolate, const icu::Locale& icu_locale, - const std::string& skeleton) { + const icu::Locale& icu_locale, const icu::UnicodeString& skeleton, + icu::DateTimePatternGenerator& generator) { // See https://github.com/tc39/ecma402/issues/225 . The best pattern // generation needs to be done in the base locale according to the // current spec however odd it may be. See also crbug.com/826549 . @@ -773,16 +889,10 @@ std::unique_ptr<icu::SimpleDateFormat> CreateICUDateFormat( // locale for the pattern match is not quite right. Moreover, what to // do with 'related year' part when 'chinese/dangi' calendar is specified // has to be discussed. Revisit once the spec is clarified/revised. - icu::Locale no_extension_locale(icu_locale.getBaseName()); - UErrorCode status = U_ZERO_ERROR; - std::unique_ptr<icu::DateTimePatternGenerator> generator( - icu::DateTimePatternGenerator::createInstance(no_extension_locale, - status)); icu::UnicodeString pattern; - if (U_SUCCESS(status)) { - pattern = - generator->getBestPattern(icu::UnicodeString(skeleton.c_str()), status); - } + UErrorCode status = U_ZERO_ERROR; + pattern = generator.getBestPattern(skeleton, status); + CHECK(U_SUCCESS(status)); // Make formatter from skeleton. Calendar and numbering system are added // to the locale as Unicode extension (if they were specified at all). @@ -795,9 +905,47 @@ std::unique_ptr<icu::SimpleDateFormat> CreateICUDateFormat( return date_format; } -Intl::HourCycle HourCycleDefault(icu::SimpleDateFormat* date_format) { - icu::UnicodeString pattern; - date_format->toPattern(pattern); +class DateFormatCache { + public: + icu::SimpleDateFormat* Create(const icu::Locale& icu_locale, + const icu::UnicodeString& skeleton, + icu::DateTimePatternGenerator& generator) { + std::string key; + skeleton.toUTF8String<std::string>(key); + key += ":"; + key += icu_locale.getName(); + + base::MutexGuard guard(&mutex_); + auto it = map_.find(key); + if (it != map_.end()) { + return static_cast<icu::SimpleDateFormat*>(it->second->clone()); + } + + if (map_.size() > 8) { // Cache at most 8 DateFormats. + map_.clear(); + } + std::unique_ptr<icu::SimpleDateFormat> instance( + CreateICUDateFormat(icu_locale, skeleton, generator)); + if (instance.get() == nullptr) return nullptr; + map_[key] = std::move(instance); + return static_cast<icu::SimpleDateFormat*>(map_[key]->clone()); + } + + private: + std::map<std::string, std::unique_ptr<icu::SimpleDateFormat>> map_; + base::Mutex mutex_; +}; + +std::unique_ptr<icu::SimpleDateFormat> CreateICUDateFormatFromCache( + const icu::Locale& icu_locale, const icu::UnicodeString& skeleton, + icu::DateTimePatternGenerator& generator) { + static base::LazyInstance<DateFormatCache>::type cache = + LAZY_INSTANCE_INITIALIZER; + return std::unique_ptr<icu::SimpleDateFormat>( + cache.Pointer()->Create(icu_locale, skeleton, generator)); +} + +Intl::HourCycle HourCycleFromPattern(const icu::UnicodeString pattern) { bool in_quote = false; for (int32_t i = 0; i < pattern.length(); i++) { char16_t ch = pattern[i]; @@ -822,6 +970,137 @@ Intl::HourCycle HourCycleDefault(icu::SimpleDateFormat* date_format) { return Intl::HourCycle::kUndefined; } +icu::DateFormat::EStyle DateTimeStyleToEStyle( + JSDateTimeFormat::DateTimeStyle style) { + switch (style) { + case JSDateTimeFormat::DateTimeStyle::kFull: + return icu::DateFormat::EStyle::kFull; + case JSDateTimeFormat::DateTimeStyle::kLong: + return icu::DateFormat::EStyle::kLong; + case JSDateTimeFormat::DateTimeStyle::kMedium: + return icu::DateFormat::EStyle::kMedium; + case JSDateTimeFormat::DateTimeStyle::kShort: + return icu::DateFormat::EStyle::kShort; + case JSDateTimeFormat::DateTimeStyle::kUndefined: + UNREACHABLE(); + } +} + +icu::UnicodeString ReplaceSkeleton(const icu::UnicodeString input, + Intl::HourCycle hc) { + icu::UnicodeString result; + char16_t to; + switch (hc) { + case Intl::HourCycle::kH11: + to = 'K'; + break; + case Intl::HourCycle::kH12: + to = 'h'; + break; + case Intl::HourCycle::kH23: + to = 'H'; + break; + case Intl::HourCycle::kH24: + to = 'k'; + break; + case Intl::HourCycle::kUndefined: + UNREACHABLE(); + } + for (int32_t i = 0; i < input.length(); i++) { + switch (input[i]) { + // We need to skip 'a', 'b', 'B' here due to + // https://unicode-org.atlassian.net/browse/ICU-20437 + case 'a': + V8_FALLTHROUGH; + case 'b': + V8_FALLTHROUGH; + case 'B': + // ignore + break; + case 'h': + V8_FALLTHROUGH; + case 'H': + V8_FALLTHROUGH; + case 'K': + V8_FALLTHROUGH; + case 'k': + result += to; + break; + default: + result += input[i]; + break; + } + } + return result; +} + +std::unique_ptr<icu::SimpleDateFormat> DateTimeStylePattern( + JSDateTimeFormat::DateTimeStyle date_style, + JSDateTimeFormat::DateTimeStyle time_style, const icu::Locale& icu_locale, + Intl::HourCycle hc, icu::DateTimePatternGenerator& generator) { + std::unique_ptr<icu::SimpleDateFormat> result; + if (date_style != JSDateTimeFormat::DateTimeStyle::kUndefined) { + if (time_style != JSDateTimeFormat::DateTimeStyle::kUndefined) { + result.reset(reinterpret_cast<icu::SimpleDateFormat*>( + icu::DateFormat::createDateTimeInstance( + DateTimeStyleToEStyle(date_style), + DateTimeStyleToEStyle(time_style), icu_locale))); + } else { + result.reset(reinterpret_cast<icu::SimpleDateFormat*>( + icu::DateFormat::createDateInstance(DateTimeStyleToEStyle(date_style), + icu_locale))); + // For instance without time, we do not need to worry about the hour cycle + // impact so we can return directly. + return result; + } + } else { + if (time_style != JSDateTimeFormat::DateTimeStyle::kUndefined) { + result.reset(reinterpret_cast<icu::SimpleDateFormat*>( + icu::DateFormat::createTimeInstance(DateTimeStyleToEStyle(time_style), + icu_locale))); + } else { + UNREACHABLE(); + } + } + icu::UnicodeString pattern; + pattern = result->toPattern(pattern); + + UErrorCode status = U_ZERO_ERROR; + icu::UnicodeString skeleton = + icu::DateTimePatternGenerator::staticGetSkeleton(pattern, status); + CHECK(U_SUCCESS(status)); + + // If the skeleton match the HourCycle, we just return it. + if (hc == HourCycleFromPattern(pattern)) { + return result; + } + + return CreateICUDateFormatFromCache(icu_locale, ReplaceSkeleton(skeleton, hc), + generator); +} + +class DateTimePatternGeneratorCache { + public: + // Return a clone copy that the caller have to free. + icu::DateTimePatternGenerator* CreateGenerator(const icu::Locale& locale) { + std::string key(locale.getBaseName()); + base::MutexGuard guard(&mutex_); + auto it = map_.find(key); + if (it != map_.end()) { + return it->second->clone(); + } + UErrorCode status = U_ZERO_ERROR; + map_[key].reset(icu::DateTimePatternGenerator::createInstance( + icu::Locale(key.c_str()), status)); + CHECK(U_SUCCESS(status)); + return map_[key]->clone(); + } + + private: + std::map<std::string, std::unique_ptr<icu::DateTimePatternGenerator>> map_; + base::Mutex mutex_; +}; + } // namespace enum FormatMatcherOption { kBestFit, kBasic }; @@ -891,14 +1170,6 @@ MaybeHandle<JSDateTimeFormat> JSDateTimeFormat::Initialize( icu::Locale icu_locale = r.icu_locale; DCHECK(!icu_locale.isBogus()); - if (!maybe_get_hour12.FromJust() && - hour_cycle == Intl::HourCycle::kUndefined) { - auto hc_extension_it = r.extensions.find("hc"); - if (hc_extension_it != r.extensions.end()) { - hour_cycle = Intl::ToHourCycle(hc_extension_it->second.c_str()); - } - } - // 17. Let timeZone be ? Get(options, "timeZone"). const std::vector<const char*> empty_values; std::unique_ptr<char[]> timezone = nullptr; @@ -907,8 +1178,17 @@ MaybeHandle<JSDateTimeFormat> JSDateTimeFormat::Initialize( "Intl.DateTimeFormat", &timezone); MAYBE_RETURN(maybe_timezone, Handle<JSDateTimeFormat>()); + std::unique_ptr<icu::TimeZone> tz = CreateTimeZone(isolate, timezone.get()); + if (tz.get() == nullptr) { + THROW_NEW_ERROR(isolate, + NewRangeError(MessageTemplate::kInvalidTimeZone, + isolate->factory()->NewStringFromAsciiChecked( + timezone.get())), + JSDateTimeFormat); + } + std::unique_ptr<icu::Calendar> calendar( - CreateCalendar(isolate, icu_locale, timezone.get())); + CreateCalendar(isolate, icu_locale, tz.release())); // 18.b If the result of IsValidTimeZoneName(timeZone) is false, then // i. Throw a RangeError exception. @@ -920,97 +1200,209 @@ MaybeHandle<JSDateTimeFormat> JSDateTimeFormat::Initialize( JSDateTimeFormat); } - // 29. If dateTimeFormat.[[Hour]] is not undefined, then + static base::LazyInstance<DateTimePatternGeneratorCache>::type + generator_cache = LAZY_INSTANCE_INITIALIZER; + + std::unique_ptr<icu::DateTimePatternGenerator> generator( + generator_cache.Pointer()->CreateGenerator(icu_locale)); + + // 15.Let hcDefault be dataLocaleData.[[hourCycle]]. + UErrorCode status = U_ZERO_ERROR; + icu::UnicodeString hour_pattern = generator->getBestPattern("jjmm", status); + CHECK(U_SUCCESS(status)); + Intl::HourCycle hc_default = HourCycleFromPattern(hour_pattern); + + // 16.Let hc be r.[[hc]]. + Intl::HourCycle hc = Intl::HourCycle::kUndefined; if (hour_cycle == Intl::HourCycle::kUndefined) { - // d. If hour12 is not undefined, then - if (maybe_get_hour12.FromJust()) { - // i. If hour12 is true, then - if (hour12) { - hour_cycle = Intl::HourCycle::kH12; - } else { // ii. Else, - hour_cycle = Intl::HourCycle::kH23; - } + auto hc_extension_it = r.extensions.find("hc"); + if (hc_extension_it != r.extensions.end()) { + hc = Intl::ToHourCycle(hc_extension_it->second.c_str()); } + } else { + hc = hour_cycle; + } + // 17. If hc is null, then + if (hc == Intl::HourCycle::kUndefined) { + // a. Set hc to hcDefault. + hc = hc_default; } - bool has_hour_option = false; - // 22. For each row of Table 5, except the header row, do - std::string skeleton; - for (const PatternData& item : GetPatternData(hour_cycle)) { - std::unique_ptr<char[]> input; - // a. Let prop be the name given in the Property column of the row. - // b. Let value be ? GetOption(options, prop, "string", « the strings given - // in the Values column of the row », undefined). - Maybe<bool> maybe_get_option = Intl::GetStringOption( - isolate, options, item.property.c_str(), item.allowed_values, - "Intl.DateTimeFormat", &input); - MAYBE_RETURN(maybe_get_option, Handle<JSDateTimeFormat>()); - if (maybe_get_option.FromJust()) { - if (item.property == "hour") { - has_hour_option = true; + // 18. If hour12 is not undefined, then + if (maybe_get_hour12.FromJust()) { + // a. If hour12 is true, then + if (hour12) { + // i. If hcDefault is "h11" or "h23", then + if (hc_default == Intl::HourCycle::kH11 || + hc_default == Intl::HourCycle::kH23) { + // 1. Set hc to "h11". + hc = Intl::HourCycle::kH11; + // ii. Else, + } else { + // 1. Set hc to "h12". + hc = Intl::HourCycle::kH12; } - DCHECK_NOT_NULL(input.get()); - // c. Set opt.[[<prop>]] to value. - skeleton += item.map.find(input.get())->second; + // b. Else, + } else { + // ii. If hcDefault is "h11" or "h23", then + if (hc_default == Intl::HourCycle::kH11 || + hc_default == Intl::HourCycle::kH23) { + // 1. Set hc to "h23". + hc = Intl::HourCycle::kH23; + // iii. Else, + } else { + // 1. Set hc to "h24". + hc = Intl::HourCycle::kH24; + } + } + } + date_time_format->set_hour_cycle(hc); + + DateTimeStyle date_style = DateTimeStyle::kUndefined; + DateTimeStyle time_style = DateTimeStyle::kUndefined; + std::unique_ptr<icu::SimpleDateFormat> icu_date_format; + + if (FLAG_harmony_intl_datetime_style) { + // 28. Let dateStyle be ? GetOption(options, "dateStyle", "string", « + // "full", "long", "medium", "short" », undefined). + Maybe<DateTimeStyle> maybe_date_style = + Intl::GetStringOption<DateTimeStyle>( + isolate, options, "dateStyle", "Intl.DateTimeFormat", + {"full", "long", "medium", "short"}, + {DateTimeStyle::kFull, DateTimeStyle::kLong, DateTimeStyle::kMedium, + DateTimeStyle::kShort}, + DateTimeStyle::kUndefined); + MAYBE_RETURN(maybe_date_style, MaybeHandle<JSDateTimeFormat>()); + // 29. If dateStyle is not undefined, set dateTimeFormat.[[DateStyle]] to + // dateStyle. + date_style = maybe_date_style.FromJust(); + if (date_style != DateTimeStyle::kUndefined) { + date_time_format->set_date_style(date_style); + } + + // 30. Let timeStyle be ? GetOption(options, "timeStyle", "string", « + // "full", "long", "medium", "short" »). + Maybe<DateTimeStyle> maybe_time_style = + Intl::GetStringOption<DateTimeStyle>( + isolate, options, "timeStyle", "Intl.DateTimeFormat", + {"full", "long", "medium", "short"}, + {DateTimeStyle::kFull, DateTimeStyle::kLong, DateTimeStyle::kMedium, + DateTimeStyle::kShort}, + DateTimeStyle::kUndefined); + MAYBE_RETURN(maybe_time_style, MaybeHandle<JSDateTimeFormat>()); + + // 31. If timeStyle is not undefined, set dateTimeFormat.[[TimeStyle]] to + // timeStyle. + time_style = maybe_time_style.FromJust(); + if (time_style != DateTimeStyle::kUndefined) { + date_time_format->set_time_style(time_style); + } + + // 32. If dateStyle or timeStyle are not undefined, then + if (date_style != DateTimeStyle::kUndefined || + time_style != DateTimeStyle::kUndefined) { + icu_date_format = DateTimeStylePattern(date_style, time_style, icu_locale, + hc, *generator); } } + // 33. Else, + if (icu_date_format.get() == nullptr) { + bool has_hour_option = false; + // b. For each row of Table 5, except the header row, do + std::string skeleton; + for (const PatternData& item : GetPatternData(hc)) { + std::unique_ptr<char[]> input; + // i. Let prop be the name given in the Property column of the row. + // ii. Let value be ? GetOption(options, prop, "string", « the strings + // given in the Values column of the row », undefined). + Maybe<bool> maybe_get_option = Intl::GetStringOption( + isolate, options, item.property.c_str(), item.allowed_values, + "Intl.DateTimeFormat", &input); + MAYBE_RETURN(maybe_get_option, Handle<JSDateTimeFormat>()); + if (maybe_get_option.FromJust()) { + if (item.property == "hour") { + has_hour_option = true; + } + DCHECK_NOT_NULL(input.get()); + // iii. Set opt.[[<prop>]] to value. + skeleton += item.map.find(input.get())->second; + } + } - enum FormatMatcherOption { kBestFit, kBasic }; - // We implement only best fit algorithm, but still need to check - // if the formatMatcher values are in range. - // 25. Let matcher be ? GetOption(options, "formatMatcher", "string", - // « "basic", "best fit" », "best fit"). - Maybe<FormatMatcherOption> maybe_format_matcher = - Intl::GetStringOption<FormatMatcherOption>( - isolate, options, "formatMatcher", "Intl.DateTimeFormat", - {"best fit", "basic"}, - {FormatMatcherOption::kBestFit, FormatMatcherOption::kBasic}, - FormatMatcherOption::kBestFit); - MAYBE_RETURN(maybe_format_matcher, MaybeHandle<JSDateTimeFormat>()); - // TODO(ftang): uncomment the following line and handle format_matcher. - // FormatMatcherOption format_matcher = maybe_format_matcher.FromJust(); + enum FormatMatcherOption { kBestFit, kBasic }; + // We implement only best fit algorithm, but still need to check + // if the formatMatcher values are in range. + // c. Let matcher be ? GetOption(options, "formatMatcher", "string", + // « "basic", "best fit" », "best fit"). + Maybe<FormatMatcherOption> maybe_format_matcher = + Intl::GetStringOption<FormatMatcherOption>( + isolate, options, "formatMatcher", "Intl.DateTimeFormat", + {"best fit", "basic"}, + {FormatMatcherOption::kBestFit, FormatMatcherOption::kBasic}, + FormatMatcherOption::kBestFit); + MAYBE_RETURN(maybe_format_matcher, MaybeHandle<JSDateTimeFormat>()); + // TODO(ftang): uncomment the following line and handle format_matcher. + // FormatMatcherOption format_matcher = maybe_format_matcher.FromJust(); + + icu::UnicodeString skeleton_ustr(skeleton.c_str()); + icu_date_format = + CreateICUDateFormatFromCache(icu_locale, skeleton_ustr, *generator); + if (icu_date_format.get() == nullptr) { + // Remove extensions and try again. + icu_locale = icu::Locale(icu_locale.getBaseName()); + icu_date_format = + CreateICUDateFormatFromCache(icu_locale, skeleton_ustr, *generator); + if (icu_date_format.get() == nullptr) { + FATAL("Failed to create ICU date format, are ICU data files missing?"); + } + } - std::unique_ptr<icu::SimpleDateFormat> date_format( - CreateICUDateFormat(isolate, icu_locale, skeleton)); - if (date_format.get() == nullptr) { - // Remove extensions and try again. - icu_locale = icu::Locale(icu_locale.getBaseName()); - date_format = CreateICUDateFormat(isolate, icu_locale, skeleton); - if (date_format.get() == nullptr) { - FATAL("Failed to create ICU date format, are ICU data files missing?"); + // g. If dateTimeFormat.[[Hour]] is not undefined, then + if (!has_hour_option) { + // h. Else, i. Set dateTimeFormat.[[HourCycle]] to undefined. + date_time_format->set_hour_cycle(Intl::HourCycle::kUndefined); } } // The creation of Calendar depends on timeZone so we have to put 13 after 17. - // Also date_format is not created until here. + // Also icu_date_format is not created until here. // 13. Set dateTimeFormat.[[Calendar]] to r.[[ca]]. - date_format->adoptCalendar(calendar.release()); - - // 29. If dateTimeFormat.[[Hour]] is not undefined, then - if (has_hour_option) { - // a. Let hcDefault be dataLocaleData.[[hourCycle]]. - Intl::HourCycle hc_default = HourCycleDefault(date_format.get()); - // b. Let hc be dateTimeFormat.[[HourCycle]]. - Intl::HourCycle hc = hour_cycle; - // c. If hc is null, then - if (hc == Intl::HourCycle::kUndefined) { - // i. Set hc to hcDefault. - hc = hc_default; + icu_date_format->adoptCalendar(calendar.release()); + + // 12.1.1 InitializeDateTimeFormat ( dateTimeFormat, locales, options ) + // + // Steps 8-9 set opt.[[hc]] to value *other than undefined* + // if "hour12" is set or "hourCycle" is set in the option. + // + // 9.2.6 ResolveLocale (... ) + // Step 8.h / 8.i and 8.k + // + // An hour12 option always overrides an hourCycle option. + // Additionally hour12 and hourCycle both clear out any existing Unicode + // extension key in the input locale. + // + // See details in https://github.com/tc39/test262/pull/2035 + if (maybe_get_hour12.FromJust() || + maybe_hour_cycle.FromJust() != Intl::HourCycle::kUndefined) { + auto hc_extension_it = r.extensions.find("hc"); + if (hc_extension_it != r.extensions.end()) { + if (date_time_format->hour_cycle() != + Intl::ToHourCycle(hc_extension_it->second.c_str())) { + // Remove -hc- if it does not agree with what we used. + UErrorCode status = U_ZERO_ERROR; + icu_locale.setKeywordValue(uloc_toLegacyKey("hc"), nullptr, status); + CHECK(U_SUCCESS(status)); + } } - // e. Set dateTimeFormat.[[HourCycle]] to hc. - date_time_format->set_hour_cycle(hc); - // 30. Else - } else { - // a. Set dateTimeFormat.[[HourCycle]] to undefined. - date_time_format->set_hour_cycle(Intl::HourCycle::kUndefined); } + Handle<Managed<icu::Locale>> managed_locale = Managed<icu::Locale>::FromRawPtr(isolate, 0, icu_locale.clone()); date_time_format->set_icu_locale(*managed_locale); Handle<Managed<icu::SimpleDateFormat>> managed_format = Managed<icu::SimpleDateFormat>::FromUniquePtr(isolate, 0, - std::move(date_format)); + std::move(icu_date_format)); date_time_format->set_icu_simple_date_format(*managed_format); return date_time_format; @@ -1127,11 +1519,8 @@ MaybeHandle<Object> JSDateTimeFormat::FormatToParts( return result; } -std::set<std::string> JSDateTimeFormat::GetAvailableLocales() { - int32_t num_locales = 0; - const icu::Locale* icu_available_locales = - icu::DateFormat::getAvailableLocales(num_locales); - return Intl::BuildLocaleSet(icu_available_locales, num_locales); +const std::set<std::string>& JSDateTimeFormat::GetAvailableLocales() { + return Intl::GetAvailableLocalesForDateFormat(); } Handle<String> JSDateTimeFormat::HourCycleAsString() const { |