summaryrefslogtreecommitdiff
path: root/deps/v8/src/objects/js-date-time-format.cc
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/objects/js-date-time-format.cc')
-rw-r--r--deps/v8/src/objects/js-date-time-format.cc689
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 {