5 #ifndef V8_INTL_SUPPORT 6 #error Internationalization is expected to be enabled. 7 #endif // V8_INTL_SUPPORT 9 #include "src/objects/js-date-time-format.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" 21 #include "unicode/calendar.h" 22 #include "unicode/dtptngen.h" 23 #include "unicode/gregocal.h" 24 #include "unicode/smpdtfmt.h" 25 #include "unicode/unistr.h" 34 PatternMap(std::string pattern, std::string value)
35 : pattern(
std::move(pattern)), value(
std::move(value)) {}
36 virtual ~PatternMap() =
default;
43 PatternItem(
const std::string property, std::vector<PatternMap> pairs,
44 std::vector<const char*> allowed_values)
45 : property(
std::move(property)),
46 pairs(
std::move(pairs)),
47 allowed_values(allowed_values) {}
48 virtual ~PatternItem() =
default;
50 const std::string property;
53 std::vector<PatternMap> pairs;
54 std::vector<const char*> allowed_values;
57 const std::vector<PatternItem> GetPatternItems() {
58 const std::vector<const char*> kLongShort = {
"long",
"short"};
59 const std::vector<const char*> kNarrowLongShort = {
"narrow",
"long",
"short"};
60 const std::vector<const char*> k2DigitNumeric = {
"2-digit",
"numeric"};
61 const std::vector<const char*> kNarrowLongShort2DigitNumeric = {
62 "narrow",
"long",
"short",
"2-digit",
"numeric"};
63 const std::vector<PatternItem> kPatternItems = {
64 PatternItem(
"weekday",
73 {{
"GGGGG",
"narrow"}, {
"GGGG",
"long"}, {
"GGG",
"short"}},
75 PatternItem(
"year", {{
"yy",
"2-digit"}, {
"y",
"numeric"}},
89 kNarrowLongShort2DigitNumeric),
90 PatternItem(
"day", {{
"dd",
"2-digit"}, {
"d",
"numeric"}}, k2DigitNumeric),
97 PatternItem(
"minute", {{
"mm",
"2-digit"}, {
"m",
"numeric"}},
99 PatternItem(
"second", {{
"ss",
"2-digit"}, {
"s",
"numeric"}},
101 PatternItem(
"timeZoneName", {{
"zzzz",
"long"}, {
"z",
"short"}},
103 return kPatternItems;
108 PatternData(
const std::string property, std::vector<PatternMap> pairs,
109 std::vector<const char*> allowed_values)
110 : property(
std::move(property)), allowed_values(allowed_values) {
111 for (
const auto& pair : pairs) {
112 map.insert(std::make_pair(pair.value, pair.pattern));
115 virtual ~PatternData() =
default;
117 const std::string property;
118 std::map<const std::string, const std::string> map;
119 std::vector<const char*> allowed_values;
128 const std::vector<PatternData> CreateCommonData(
const PatternData& hour_data) {
129 std::vector<PatternData> build;
130 for (
const PatternItem& item : GetPatternItems()) {
131 if (item.property ==
"hour") {
132 build.push_back(hour_data);
135 PatternData(item.property, item.pairs, item.allowed_values));
141 const std::vector<PatternData> CreateData(
const char* digit2,
142 const char* numeric) {
143 return CreateCommonData(
144 PatternData(
"hour", {{digit2,
"2-digit"}, {numeric,
"numeric"}},
145 {
"2-digit",
"numeric"}));
148 const std::vector<PatternData> GetPatternData(HourOption option) {
149 const std::vector<PatternData> data = CreateData(
"jj",
"j");
150 const std::vector<PatternData> data_h12 = CreateData(
"hh",
"h");
151 const std::vector<PatternData> data_h24 = CreateData(
"HH",
"H");
153 case HourOption::H_12:
155 case HourOption::H_24:
157 case HourOption::H_UNKNOWN:
162 std::string GetGMTTzID(Isolate* isolate,
const std::string& input) {
163 std::string ret =
"Etc/GMT";
164 switch (input.length()) {
166 if (input[7] ==
'0')
return ret +
'0';
169 if ((input[7] ==
'+' || input[7] ==
'-') &&
170 IsInRange(input[8],
'0',
'9')) {
171 return ret + input[7] + input[8];
175 if ((input[7] ==
'+' || input[7] ==
'-') && (input[8] ==
'1') &&
176 IsInRange(input[9],
'0',
'4')) {
177 return ret + input[7] + input[8] + input[9];
186 bool IsAsciiAlpha(
char ch) {
187 return IsInRange(ch,
'A',
'Z') || IsInRange(ch,
'a',
'z');
192 char LocaleIndependentAsciiToUpper(
char ch) {
193 return (IsInRange(ch,
'a',
'z')) ? (ch -
'a' +
'A') : ch;
197 char LocaleIndependentAsciiToLower(
char ch) {
198 return (IsInRange(ch,
'A',
'Z')) ? (ch -
'A' +
'a') : ch;
206 std::string ToTitleCaseTimezoneLocation(Isolate* isolate,
207 const std::string& input) {
208 std::string title_cased;
210 for (
char ch : input) {
212 if (IsAsciiAlpha(ch)) {
213 title_cased += word_length == 0 ? LocaleIndependentAsciiToUpper(ch)
214 : LocaleIndependentAsciiToLower(ch);
216 }
else if (ch ==
'_' || ch ==
'-' || ch ==
'/') {
218 if (word_length == 2) {
219 size_t pos = title_cased.length() - 2;
220 std::string substr = title_cased.substr(pos, 2);
221 if (substr ==
"Of" || substr ==
"Es" || substr ==
"Au") {
222 title_cased[pos] = LocaleIndependentAsciiToLower(title_cased[pos]);
229 return std::string();
237 std::string JSDateTimeFormat::CanonicalizeTimeZoneID(Isolate* isolate,
238 const std::string& input) {
239 std::string upper = input;
240 transform(upper.begin(), upper.end(), upper.begin(),
241 LocaleIndependentAsciiToUpper);
242 if (upper ==
"UTC" || upper ==
"GMT" || upper ==
"ETC/UTC" ||
243 upper ==
"ETC/GMT") {
253 if (strncmp(upper.c_str(),
"ETC/GMT", 7) == 0) {
254 return GetGMTTzID(isolate, input);
256 return ToTitleCaseTimezoneLocation(isolate, input);
260 MaybeHandle<JSObject> JSDateTimeFormat::ResolvedOptions(
261 Isolate* isolate, Handle<JSDateTimeFormat> date_time_format) {
262 Factory* factory = isolate->factory();
264 Handle<JSObject> options = factory->NewJSObject(isolate->object_function());
266 Handle<Object> resolved_obj;
268 UErrorCode status = U_ZERO_ERROR;
269 char language[ULOC_FULLNAME_CAPACITY];
270 uloc_toLanguageTag(date_time_format->icu_locale()->raw()->getName(), language,
271 ULOC_FULLNAME_CAPACITY, FALSE, &status);
272 CHECK(U_SUCCESS(status));
273 Handle<String> locale = factory->NewStringFromAsciiChecked(language);
275 icu::SimpleDateFormat* icu_simple_date_format =
276 date_time_format->icu_simple_date_format()->raw();
278 const icu::Calendar* calendar = icu_simple_date_format->getCalendar();
282 std::string calendar_str = calendar->getType();
288 if (calendar_str ==
"gregorian") {
289 calendar_str =
"gregory";
290 }
else if (calendar_str ==
"ethiopic-amete-alem") {
291 calendar_str =
"ethioaa";
294 const icu::TimeZone& tz = calendar->getTimeZone();
295 icu::UnicodeString time_zone;
297 status = U_ZERO_ERROR;
298 icu::UnicodeString canonical_time_zone;
299 icu::TimeZone::getCanonicalID(time_zone, canonical_time_zone, status);
300 Handle<Object> timezone_value;
301 if (U_SUCCESS(status)) {
308 if (canonical_time_zone == UNICODE_STRING_SIMPLE(
"Etc/UTC") ||
309 canonical_time_zone == UNICODE_STRING_SIMPLE(
"Etc/GMT")) {
310 timezone_value = factory->UTC_string();
312 ASSIGN_RETURN_ON_EXCEPTION(isolate, timezone_value,
313 Intl::ToString(isolate, canonical_time_zone),
318 timezone_value = factory->undefined_value();
325 std::string numbering_system =
326 Intl::GetNumberingSystem(*(date_time_format->icu_locale()->raw()));
328 icu::UnicodeString pattern_unicode;
329 icu_simple_date_format->toPattern(pattern_unicode);
331 pattern_unicode.toUTF8String(pattern);
351 CHECK(JSReceiver::CreateDataProperty(
352 isolate, options, factory->locale_string(), locale, kDontThrow)
354 CHECK(JSReceiver::CreateDataProperty(
355 isolate, options, factory->calendar_string(),
356 factory->NewStringFromAsciiChecked(calendar_str.c_str()),
359 if (!numbering_system.empty()) {
360 CHECK(JSReceiver::CreateDataProperty(
361 isolate, options, factory->numberingSystem_string(),
362 factory->NewStringFromAsciiChecked(numbering_system.c_str()),
366 CHECK(JSReceiver::CreateDataProperty(isolate, options,
367 factory->timeZone_string(),
368 timezone_value, kDontThrow)
370 if (pattern.find(
'h') != std::string::npos) {
371 CHECK(JSReceiver::CreateDataProperty(isolate, options,
372 factory->hourCycle_string(),
373 factory->h11_string(), kDontThrow)
375 }
else if (pattern.find(
'H') != std::string::npos) {
376 CHECK(JSReceiver::CreateDataProperty(isolate, options,
377 factory->hourCycle_string(),
378 factory->h12_string(), kDontThrow)
380 }
else if (pattern.find(
'k') != std::string::npos) {
381 CHECK(JSReceiver::CreateDataProperty(isolate, options,
382 factory->hourCycle_string(),
383 factory->h23_string(), kDontThrow)
385 }
else if (pattern.find(
'K') != std::string::npos) {
386 CHECK(JSReceiver::CreateDataProperty(isolate, options,
387 factory->hourCycle_string(),
388 factory->h24_string(), kDontThrow)
396 if (pattern.find(
'h') != std::string::npos) {
397 CHECK(JSReceiver::CreateDataProperty(isolate, options,
398 factory->hour12_string(),
399 factory->true_value(), kDontThrow)
401 }
else if (pattern.find(
'H') != std::string::npos) {
402 CHECK(JSReceiver::CreateDataProperty(isolate, options,
403 factory->hour12_string(),
404 factory->false_value(), kDontThrow)
408 const std::vector<PatternItem> items = GetPatternItems();
409 for (
const auto& item : items) {
410 for (
const auto& pair : item.pairs) {
411 if (pattern.find(pair.pattern) != std::string::npos) {
414 CHECK(JSReceiver::CreateDataProperty(
416 factory->NewStringFromAsciiChecked(item.property.c_str()),
417 factory->NewStringFromAsciiChecked(pair.value.c_str()),
432 MaybeHandle<String> FormatDateTime(Isolate* isolate,
433 const icu::SimpleDateFormat& date_format,
435 double date_value = DateCache::TimeClip(x);
436 if (std::isnan(date_value)) {
437 THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kInvalidTimeValue),
441 icu::UnicodeString result;
442 date_format.format(date_value, result);
444 return Intl::ToString(isolate, result);
451 MaybeHandle<String> JSDateTimeFormat::DateTimeFormat(
452 Isolate* isolate, Handle<JSDateTimeFormat> date_time_format,
453 Handle<Object> date) {
459 if (date->IsUndefined()) {
461 x = JSDate::CurrentTimeValue(isolate);
465 ASSIGN_RETURN_ON_EXCEPTION(isolate, date, Object::ToNumber(isolate, date),
467 CHECK(date->IsNumber());
471 return FormatDateTime(
472 isolate, *(date_time_format->icu_simple_date_format()->raw()), x);
476 Isolate::ICUObjectCacheType ConvertToCacheType(
477 JSDateTimeFormat::DefaultsOption type) {
479 case JSDateTimeFormat::DefaultsOption::kDate:
480 return Isolate::ICUObjectCacheType::kDefaultSimpleDateFormatForDate;
481 case JSDateTimeFormat::DefaultsOption::kTime:
482 return Isolate::ICUObjectCacheType::kDefaultSimpleDateFormatForTime;
483 case JSDateTimeFormat::DefaultsOption::kAll:
484 return Isolate::ICUObjectCacheType::kDefaultSimpleDateFormat;
489 MaybeHandle<String> JSDateTimeFormat::ToLocaleDateTime(
490 Isolate* isolate, Handle<Object> date, Handle<Object> locales,
491 Handle<Object> options, RequiredOption required, DefaultsOption defaults) {
492 Isolate::ICUObjectCacheType cache_type = ConvertToCacheType(defaults);
494 Factory* factory = isolate->factory();
496 if (!date->IsJSDate()) {
497 THROW_NEW_ERROR(isolate,
498 NewTypeError(MessageTemplate::kMethodInvokedOnWrongType,
499 factory->Date_string()),
503 double const x = Handle<JSDate>::cast(date)->value()->Number();
506 return factory->Invalid_Date_string();
513 locales->IsUndefined(isolate) && options->IsUndefined(isolate);
516 icu::SimpleDateFormat* cached_icu_simple_date_format =
517 static_cast<icu::SimpleDateFormat*
>(
518 isolate->get_cached_icu_object(cache_type));
519 if (cached_icu_simple_date_format !=
nullptr) {
520 return FormatDateTime(isolate, *cached_icu_simple_date_format, x);
524 Handle<JSObject> internal_options;
525 ASSIGN_RETURN_ON_EXCEPTION(
526 isolate, internal_options,
527 ToDateTimeOptions(isolate, options, required, defaults), String);
530 Handle<JSFunction> constructor = Handle<JSFunction>(
531 JSFunction::cast(isolate->context()
533 ->intl_date_time_format_function()),
535 Handle<JSObject> obj;
536 ASSIGN_RETURN_ON_EXCEPTION(
538 JSObject::New(constructor, constructor, Handle<AllocationSite>::null()),
540 Handle<JSDateTimeFormat> date_time_format;
541 ASSIGN_RETURN_ON_EXCEPTION(
542 isolate, date_time_format,
543 JSDateTimeFormat::Initialize(isolate, Handle<JSDateTimeFormat>::cast(obj),
544 locales, internal_options),
548 isolate->set_icu_object_in_cache(
549 cache_type, std::static_pointer_cast<icu::UObject>(
550 date_time_format->icu_simple_date_format()->get()));
553 return FormatDateTime(
554 isolate, *(date_time_format->icu_simple_date_format()->raw()), x);
559 Maybe<bool> IsPropertyUndefined(Isolate* isolate, Handle<JSObject> options,
560 const char* property) {
561 Factory* factory = isolate->factory();
564 Handle<Object> value;
565 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
567 Object::GetPropertyOrElement(
568 isolate, options, factory->NewStringFromAsciiChecked(property)),
570 return Just(value->IsUndefined(isolate));
573 Maybe<bool> NeedsDefault(Isolate* isolate, Handle<JSObject> options,
574 const std::vector<std::string>& props) {
575 bool needs_default =
true;
576 for (
const auto& prop : props) {
579 Maybe<bool> maybe_undefined =
580 IsPropertyUndefined(isolate, options, prop.c_str());
581 MAYBE_RETURN(maybe_undefined, Nothing<bool>());
583 if (!maybe_undefined.FromJust()) {
584 needs_default =
false;
587 return Just(needs_default);
590 Maybe<bool> CreateDefault(Isolate* isolate, Handle<JSObject> options,
591 const std::vector<std::string>& props) {
592 Factory* factory = isolate->factory();
594 for (
const auto& prop : props) {
596 JSReceiver::CreateDataProperty(
597 isolate, options, factory->NewStringFromAsciiChecked(prop.c_str()),
598 factory->numeric_string(), kThrowOnError),
607 MaybeHandle<JSObject> JSDateTimeFormat::ToDateTimeOptions(
608 Isolate* isolate, Handle<Object> input_options, RequiredOption required,
609 DefaultsOption defaults) {
610 Factory* factory = isolate->factory();
613 Handle<JSObject> options;
614 if (input_options->IsUndefined(isolate)) {
615 options = factory->NewJSObjectWithNullProto();
617 Handle<JSReceiver> options_obj;
618 ASSIGN_RETURN_ON_EXCEPTION(isolate, options_obj,
619 Object::ToObject(isolate, input_options),
622 ASSIGN_RETURN_ON_EXCEPTION(isolate, options,
623 JSObject::ObjectCreate(isolate, options_obj),
628 bool needs_default =
true;
631 if (required == RequiredOption::kAny || required == RequiredOption::kDate) {
633 const std::vector<std::string> list({
"weekday",
"year",
"month",
"day"});
634 Maybe<bool> maybe_needs_default = NeedsDefault(isolate, options, list);
635 MAYBE_RETURN(maybe_needs_default, Handle<JSObject>());
636 needs_default = maybe_needs_default.FromJust();
640 if (required == RequiredOption::kAny || required == RequiredOption::kTime) {
642 const std::vector<std::string> list({
"hour",
"minute",
"second"});
643 Maybe<bool> maybe_needs_default = NeedsDefault(isolate, options, list);
644 MAYBE_RETURN(maybe_needs_default, Handle<JSObject>());
645 needs_default &= maybe_needs_default.FromJust();
650 if (defaults == DefaultsOption::kAll || defaults == DefaultsOption::kDate) {
652 const std::vector<std::string> list({
"year",
"month",
"day"});
653 MAYBE_RETURN(CreateDefault(isolate, options, list), Handle<JSObject>());
656 if (defaults == DefaultsOption::kAll || defaults == DefaultsOption::kTime) {
658 const std::vector<std::string> list({
"hour",
"minute",
"second"});
659 MAYBE_RETURN(CreateDefault(isolate, options, list), Handle<JSObject>());
666 MaybeHandle<JSDateTimeFormat> JSDateTimeFormat::UnwrapDateTimeFormat(
667 Isolate* isolate, Handle<JSReceiver> format_holder) {
668 Handle<Context> native_context =
669 Handle<Context>(isolate->context()->native_context(), isolate);
670 Handle<JSFunction> constructor = Handle<JSFunction>(
671 JSFunction::cast(native_context->intl_date_time_format_function()),
674 ASSIGN_RETURN_ON_EXCEPTION(
676 Intl::LegacyUnwrapReceiver(isolate, format_holder, constructor,
677 format_holder->IsJSDateTimeFormat()),
681 if (!dtf->IsJSDateTimeFormat()) {
683 THROW_NEW_ERROR(isolate,
684 NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
685 isolate->factory()->NewStringFromAsciiChecked(
686 "UnwrapDateTimeFormat"),
691 return Handle<JSDateTimeFormat>::cast(dtf);
697 bool IsValidTimeZoneName(
const icu::TimeZone& tz) {
698 UErrorCode status = U_ZERO_ERROR;
699 icu::UnicodeString id;
701 icu::UnicodeString canonical;
702 icu::TimeZone::getCanonicalID(
id, canonical, status);
703 return U_SUCCESS(status) &&
704 canonical != icu::UnicodeString(
"Etc/Unknown", -1, US_INV);
707 std::unique_ptr<icu::TimeZone> CreateTimeZone(Isolate* isolate,
708 const char* timezone) {
711 if (timezone ==
nullptr) {
713 return std::unique_ptr<icu::TimeZone>(icu::TimeZone::createDefault());
715 std::string canonicalized =
716 JSDateTimeFormat::CanonicalizeTimeZoneID(isolate, timezone);
717 if (canonicalized.empty())
return std::unique_ptr<icu::TimeZone>();
718 std::unique_ptr<icu::TimeZone> tz(
719 icu::TimeZone::createTimeZone(canonicalized.c_str()));
722 if (!IsValidTimeZoneName(*tz))
return std::unique_ptr<icu::TimeZone>();
726 std::unique_ptr<icu::Calendar> CreateCalendar(Isolate* isolate,
727 const icu::Locale& icu_locale,
728 const char* timezone) {
729 std::unique_ptr<icu::TimeZone> tz = CreateTimeZone(isolate, timezone);
730 if (tz.get() ==
nullptr)
return std::unique_ptr<icu::Calendar>();
733 UErrorCode status = U_ZERO_ERROR;
734 std::unique_ptr<icu::Calendar> calendar(
735 icu::Calendar::createInstance(tz.release(), icu_locale, status));
736 CHECK(U_SUCCESS(status));
737 CHECK_NOT_NULL(calendar.get());
739 if (calendar->getDynamicClassID() ==
740 icu::GregorianCalendar::getStaticClassID()) {
741 icu::GregorianCalendar* gc =
742 static_cast<icu::GregorianCalendar*
>(calendar.get());
743 UErrorCode status = U_ZERO_ERROR;
745 const double start_of_time = -9007199254740992;
746 gc->setGregorianChange(start_of_time, status);
747 DCHECK(U_SUCCESS(status));
752 std::unique_ptr<icu::SimpleDateFormat> CreateICUDateFormat(
753 Isolate* isolate,
const icu::Locale& icu_locale,
754 const std::string& skeleton) {
765 icu::Locale no_extension_locale(icu_locale.getBaseName());
766 UErrorCode status = U_ZERO_ERROR;
767 std::unique_ptr<icu::DateTimePatternGenerator> generator(
768 icu::DateTimePatternGenerator::createInstance(no_extension_locale,
770 icu::UnicodeString pattern;
771 if (U_SUCCESS(status)) {
773 generator->getBestPattern(icu::UnicodeString(skeleton.c_str()), status);
778 status = U_ZERO_ERROR;
779 std::unique_ptr<icu::SimpleDateFormat> date_format(
780 new icu::SimpleDateFormat(pattern, icu_locale, status));
781 if (U_FAILURE(status))
return std::unique_ptr<icu::SimpleDateFormat>();
783 CHECK_NOT_NULL(date_format.get());
789 enum FormatMatcherOption { kBestFit, kBasic };
792 MaybeHandle<JSDateTimeFormat> JSDateTimeFormat::Initialize(
793 Isolate* isolate, Handle<JSDateTimeFormat> date_time_format,
794 Handle<Object> locales, Handle<Object> input_options) {
796 Maybe<std::vector<std::string>> maybe_requested_locales =
797 Intl::CanonicalizeLocaleList(isolate, locales);
798 MAYBE_RETURN(maybe_requested_locales, Handle<JSDateTimeFormat>());
799 std::vector<std::string> requested_locales =
800 maybe_requested_locales.FromJust();
803 Handle<JSObject> options;
804 ASSIGN_RETURN_ON_EXCEPTION(
806 JSDateTimeFormat::ToDateTimeOptions(
807 isolate, input_options, RequiredOption::kAny, DefaultsOption::kDate),
813 Maybe<Intl::MatcherOption> maybe_locale_matcher =
814 Intl::GetLocaleMatcher(isolate, options,
"Intl.DateTimeFormat");
815 MAYBE_RETURN(maybe_locale_matcher, MaybeHandle<JSDateTimeFormat>());
816 Intl::MatcherOption locale_matcher = maybe_locale_matcher.FromJust();
821 Maybe<bool> maybe_get_hour12 = Intl::GetBoolOption(
822 isolate, options,
"hour12",
"Intl.DateTimeFormat", &hour12);
823 MAYBE_RETURN(maybe_get_hour12, Handle<JSDateTimeFormat>());
824 HourOption hour_option = HourOption::H_UNKNOWN;
825 if (maybe_get_hour12.FromJust()) {
826 hour_option = hour12 ? HourOption::H_12 : HourOption::H_24;
831 Maybe<Intl::HourCycle> maybe_hour_cycle =
832 Intl::GetHourCycle(isolate, options,
"Intl.DateTimeFormat");
833 MAYBE_RETURN(maybe_hour_cycle, MaybeHandle<JSDateTimeFormat>());
838 if (maybe_get_hour12.FromJust()) {
852 std::set<std::string> relevant_extension_keys = {
"nu",
"ca"};
859 Intl::ResolvedLocale r = Intl::ResolveLocale(
860 isolate, JSDateTimeFormat::GetAvailableLocales(), requested_locales,
861 locale_matcher, relevant_extension_keys);
865 icu::Locale icu_locale = r.icu_locale;
866 DCHECK(!icu_locale.isBogus());
869 const std::vector<const char*> empty_values;
870 std::unique_ptr<char[]> timezone =
nullptr;
871 Maybe<bool> maybe_timezone =
872 Intl::GetStringOption(isolate, options,
"timeZone", empty_values,
873 "Intl.DateTimeFormat", &timezone);
874 MAYBE_RETURN(maybe_timezone, Handle<JSDateTimeFormat>());
877 std::string skeleton;
878 for (
const auto& item : GetPatternData(hour_option)) {
879 std::unique_ptr<char[]> input;
883 Maybe<bool> maybe_get_option = Intl::GetStringOption(
884 isolate, options, item.property.c_str(), item.allowed_values,
885 "Intl.DateTimeFormat", &input);
886 MAYBE_RETURN(maybe_get_option, Handle<JSDateTimeFormat>());
887 if (maybe_get_option.FromJust()) {
888 DCHECK_NOT_NULL(input.get());
890 skeleton += item.map.find(input.get())->second;
894 enum FormatMatcherOption { kBestFit, kBasic };
899 Maybe<FormatMatcherOption> maybe_format_matcher =
900 Intl::GetStringOption<FormatMatcherOption>(
901 isolate, options,
"formatMatcher",
"Intl.DateTimeFormat",
902 {
"best fit",
"basic"},
903 {FormatMatcherOption::kBestFit, FormatMatcherOption::kBasic},
904 FormatMatcherOption::kBestFit);
905 MAYBE_RETURN(maybe_format_matcher, MaybeHandle<JSDateTimeFormat>());
909 std::unique_ptr<icu::SimpleDateFormat> date_format(
910 CreateICUDateFormat(isolate, icu_locale, skeleton));
911 if (date_format.get() ==
nullptr) {
913 icu_locale = icu::Locale(icu_locale.getBaseName());
914 date_format = CreateICUDateFormat(isolate, icu_locale, skeleton);
915 if (date_format.get() ==
nullptr) {
916 FATAL(
"Failed to create ICU date format, are ICU data files missing?");
922 icu::Locale* cloned_locale = icu_locale.clone();
923 CHECK_NOT_NULL(cloned_locale);
924 Handle<Managed<icu::Locale>> managed_locale =
925 Managed<icu::Locale>::FromRawPtr(isolate, 0, cloned_locale);
926 date_time_format->set_icu_locale(*managed_locale);
929 std::unique_ptr<icu::Calendar> calendar(
930 CreateCalendar(isolate, icu_locale, timezone.get()));
934 if (calendar.get() ==
nullptr) {
935 THROW_NEW_ERROR(isolate,
936 NewRangeError(MessageTemplate::kInvalidTimeZone,
937 isolate->factory()->NewStringFromAsciiChecked(
941 date_format->adoptCalendar(calendar.release());
943 Handle<Managed<icu::SimpleDateFormat>> managed_format =
944 Managed<icu::SimpleDateFormat>::FromUniquePtr(isolate, 0,
945 std::move(date_format));
946 date_time_format->set_icu_simple_date_format(*managed_format);
948 return date_time_format;
956 Handle<String> IcuDateFieldIdToDateType(int32_t field_id, Isolate* isolate) {
959 return isolate->factory()->literal_string();
960 case UDAT_YEAR_FIELD:
961 case UDAT_EXTENDED_YEAR_FIELD:
962 case UDAT_YEAR_NAME_FIELD:
963 return isolate->factory()->year_string();
964 case UDAT_MONTH_FIELD:
965 case UDAT_STANDALONE_MONTH_FIELD:
966 return isolate->factory()->month_string();
967 case UDAT_DATE_FIELD:
968 return isolate->factory()->day_string();
969 case UDAT_HOUR_OF_DAY1_FIELD:
970 case UDAT_HOUR_OF_DAY0_FIELD:
971 case UDAT_HOUR1_FIELD:
972 case UDAT_HOUR0_FIELD:
973 return isolate->factory()->hour_string();
974 case UDAT_MINUTE_FIELD:
975 return isolate->factory()->minute_string();
976 case UDAT_SECOND_FIELD:
977 return isolate->factory()->second_string();
978 case UDAT_DAY_OF_WEEK_FIELD:
979 case UDAT_DOW_LOCAL_FIELD:
980 case UDAT_STANDALONE_DAY_FIELD:
981 return isolate->factory()->weekday_string();
982 case UDAT_AM_PM_FIELD:
983 return isolate->factory()->dayPeriod_string();
984 case UDAT_TIMEZONE_FIELD:
985 case UDAT_TIMEZONE_RFC_FIELD:
986 case UDAT_TIMEZONE_GENERIC_FIELD:
987 case UDAT_TIMEZONE_SPECIAL_FIELD:
988 case UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD:
989 case UDAT_TIMEZONE_ISO_FIELD:
990 case UDAT_TIMEZONE_ISO_LOCAL_FIELD:
991 return isolate->factory()->timeZoneName_string();
993 return isolate->factory()->era_string();
999 return Handle<String>();
1005 MaybeHandle<Object> JSDateTimeFormat::FormatToParts(
1006 Isolate* isolate, Handle<JSDateTimeFormat> date_time_format,
1007 double date_value) {
1008 Factory* factory = isolate->factory();
1009 icu::SimpleDateFormat* format =
1010 date_time_format->icu_simple_date_format()->raw();
1011 CHECK_NOT_NULL(format);
1013 icu::UnicodeString formatted;
1014 icu::FieldPositionIterator fp_iter;
1015 icu::FieldPosition fp;
1016 UErrorCode status = U_ZERO_ERROR;
1017 format->format(date_value, formatted, &fp_iter, status);
1018 if (U_FAILURE(status)) {
1019 THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError), Object);
1022 Handle<JSArray> result = factory->NewJSArray(0);
1023 int32_t length = formatted.length();
1024 if (length == 0)
return result;
1027 int32_t previous_end_pos = 0;
1028 Handle<String> substring;
1029 while (fp_iter.next(fp)) {
1030 int32_t begin_pos = fp.getBeginIndex();
1031 int32_t end_pos = fp.getEndIndex();
1033 if (previous_end_pos < begin_pos) {
1034 ASSIGN_RETURN_ON_EXCEPTION(
1036 Intl::ToString(isolate, formatted, previous_end_pos, begin_pos),
1038 Intl::AddElement(isolate, result, index,
1039 IcuDateFieldIdToDateType(-1, isolate), substring);
1042 ASSIGN_RETURN_ON_EXCEPTION(
1044 Intl::ToString(isolate, formatted, begin_pos, end_pos), Object);
1045 Intl::AddElement(isolate, result, index,
1046 IcuDateFieldIdToDateType(fp.getField(), isolate),
1048 previous_end_pos = end_pos;
1051 if (previous_end_pos < length) {
1052 ASSIGN_RETURN_ON_EXCEPTION(
1054 Intl::ToString(isolate, formatted, previous_end_pos, length), Object);
1055 Intl::AddElement(isolate, result, index,
1056 IcuDateFieldIdToDateType(-1, isolate), substring);
1058 JSObject::ValidateElements(*result);
1062 std::set<std::string> JSDateTimeFormat::GetAvailableLocales() {
1063 int32_t num_locales = 0;
1064 const icu::Locale* icu_available_locales =
1065 icu::DateFormat::getAvailableLocales(num_locales);
1066 return Intl::BuildLocaleSet(icu_available_locales, num_locales);