11cb0ef41Sopenharmony_ci// Copyright 2018 the V8 project authors. All rights reserved. 21cb0ef41Sopenharmony_ci// Use of this source code is governed by a BSD-style license that can be 31cb0ef41Sopenharmony_ci// found in the LICENSE file. 41cb0ef41Sopenharmony_ci 51cb0ef41Sopenharmony_ci#ifndef V8_INTL_SUPPORT 61cb0ef41Sopenharmony_ci#error Internationalization is expected to be enabled. 71cb0ef41Sopenharmony_ci#endif // V8_INTL_SUPPORT 81cb0ef41Sopenharmony_ci 91cb0ef41Sopenharmony_ci#include "src/objects/js-relative-time-format.h" 101cb0ef41Sopenharmony_ci 111cb0ef41Sopenharmony_ci#include <map> 121cb0ef41Sopenharmony_ci#include <memory> 131cb0ef41Sopenharmony_ci#include <string> 141cb0ef41Sopenharmony_ci 151cb0ef41Sopenharmony_ci#include "src/execution/isolate.h" 161cb0ef41Sopenharmony_ci#include "src/heap/factory.h" 171cb0ef41Sopenharmony_ci#include "src/objects/intl-objects.h" 181cb0ef41Sopenharmony_ci#include "src/objects/js-number-format.h" 191cb0ef41Sopenharmony_ci#include "src/objects/js-relative-time-format-inl.h" 201cb0ef41Sopenharmony_ci#include "src/objects/managed-inl.h" 211cb0ef41Sopenharmony_ci#include "src/objects/objects-inl.h" 221cb0ef41Sopenharmony_ci#include "src/objects/option-utils.h" 231cb0ef41Sopenharmony_ci#include "unicode/decimfmt.h" 241cb0ef41Sopenharmony_ci#include "unicode/numfmt.h" 251cb0ef41Sopenharmony_ci#include "unicode/reldatefmt.h" 261cb0ef41Sopenharmony_ci#include "unicode/unum.h" 271cb0ef41Sopenharmony_ci 281cb0ef41Sopenharmony_cinamespace v8 { 291cb0ef41Sopenharmony_cinamespace internal { 301cb0ef41Sopenharmony_ci 311cb0ef41Sopenharmony_cinamespace { 321cb0ef41Sopenharmony_ci// Style: identifying the relative time format style used. 331cb0ef41Sopenharmony_ci// 341cb0ef41Sopenharmony_ci// ecma402/#sec-properties-of-intl-relativetimeformat-instances 351cb0ef41Sopenharmony_ci 361cb0ef41Sopenharmony_cienum class Style { 371cb0ef41Sopenharmony_ci LONG, // Everything spelled out. 381cb0ef41Sopenharmony_ci SHORT, // Abbreviations used when possible. 391cb0ef41Sopenharmony_ci NARROW // Use the shortest possible form. 401cb0ef41Sopenharmony_ci}; 411cb0ef41Sopenharmony_ci 421cb0ef41Sopenharmony_ciUDateRelativeDateTimeFormatterStyle toIcuStyle(Style style) { 431cb0ef41Sopenharmony_ci switch (style) { 441cb0ef41Sopenharmony_ci case Style::LONG: 451cb0ef41Sopenharmony_ci return UDAT_STYLE_LONG; 461cb0ef41Sopenharmony_ci case Style::SHORT: 471cb0ef41Sopenharmony_ci return UDAT_STYLE_SHORT; 481cb0ef41Sopenharmony_ci case Style::NARROW: 491cb0ef41Sopenharmony_ci return UDAT_STYLE_NARROW; 501cb0ef41Sopenharmony_ci } 511cb0ef41Sopenharmony_ci UNREACHABLE(); 521cb0ef41Sopenharmony_ci} 531cb0ef41Sopenharmony_ci 541cb0ef41Sopenharmony_ciStyle fromIcuStyle(UDateRelativeDateTimeFormatterStyle icu_style) { 551cb0ef41Sopenharmony_ci switch (icu_style) { 561cb0ef41Sopenharmony_ci case UDAT_STYLE_LONG: 571cb0ef41Sopenharmony_ci return Style::LONG; 581cb0ef41Sopenharmony_ci case UDAT_STYLE_SHORT: 591cb0ef41Sopenharmony_ci return Style::SHORT; 601cb0ef41Sopenharmony_ci case UDAT_STYLE_NARROW: 611cb0ef41Sopenharmony_ci return Style::NARROW; 621cb0ef41Sopenharmony_ci case UDAT_STYLE_COUNT: 631cb0ef41Sopenharmony_ci UNREACHABLE(); 641cb0ef41Sopenharmony_ci } 651cb0ef41Sopenharmony_ci UNREACHABLE(); 661cb0ef41Sopenharmony_ci} 671cb0ef41Sopenharmony_ci} // namespace 681cb0ef41Sopenharmony_ci 691cb0ef41Sopenharmony_ciMaybeHandle<JSRelativeTimeFormat> JSRelativeTimeFormat::New( 701cb0ef41Sopenharmony_ci Isolate* isolate, Handle<Map> map, Handle<Object> locales, 711cb0ef41Sopenharmony_ci Handle<Object> input_options) { 721cb0ef41Sopenharmony_ci // 1. Let requestedLocales be ? CanonicalizeLocaleList(locales). 731cb0ef41Sopenharmony_ci Maybe<std::vector<std::string>> maybe_requested_locales = 741cb0ef41Sopenharmony_ci Intl::CanonicalizeLocaleList(isolate, locales); 751cb0ef41Sopenharmony_ci MAYBE_RETURN(maybe_requested_locales, Handle<JSRelativeTimeFormat>()); 761cb0ef41Sopenharmony_ci std::vector<std::string> requested_locales = 771cb0ef41Sopenharmony_ci maybe_requested_locales.FromJust(); 781cb0ef41Sopenharmony_ci 791cb0ef41Sopenharmony_ci // 2. Set options to ? CoerceOptionsToObject(options). 801cb0ef41Sopenharmony_ci Handle<JSReceiver> options; 811cb0ef41Sopenharmony_ci const char* service = "Intl.RelativeTimeFormat"; 821cb0ef41Sopenharmony_ci ASSIGN_RETURN_ON_EXCEPTION( 831cb0ef41Sopenharmony_ci isolate, options, CoerceOptionsToObject(isolate, input_options, service), 841cb0ef41Sopenharmony_ci JSRelativeTimeFormat); 851cb0ef41Sopenharmony_ci 861cb0ef41Sopenharmony_ci // 4. Let opt be a new Record. 871cb0ef41Sopenharmony_ci // 5. Let matcher be ? GetOption(options, "localeMatcher", "string", « 881cb0ef41Sopenharmony_ci // "lookup", "best fit" », "best fit"). 891cb0ef41Sopenharmony_ci // 6. Set opt.[[localeMatcher]] to matcher. 901cb0ef41Sopenharmony_ci Maybe<Intl::MatcherOption> maybe_locale_matcher = 911cb0ef41Sopenharmony_ci Intl::GetLocaleMatcher(isolate, options, service); 921cb0ef41Sopenharmony_ci MAYBE_RETURN(maybe_locale_matcher, MaybeHandle<JSRelativeTimeFormat>()); 931cb0ef41Sopenharmony_ci Intl::MatcherOption matcher = maybe_locale_matcher.FromJust(); 941cb0ef41Sopenharmony_ci 951cb0ef41Sopenharmony_ci // 7. Let _numberingSystem_ be ? GetOption(_options_, `"numberingSystem"`, 961cb0ef41Sopenharmony_ci // `"string"`, *undefined*, *undefined*). 971cb0ef41Sopenharmony_ci std::unique_ptr<char[]> numbering_system_str = nullptr; 981cb0ef41Sopenharmony_ci Maybe<bool> maybe_numberingSystem = Intl::GetNumberingSystem( 991cb0ef41Sopenharmony_ci isolate, options, service, &numbering_system_str); 1001cb0ef41Sopenharmony_ci // 8. If _numberingSystem_ is not *undefined*, then 1011cb0ef41Sopenharmony_ci // a. If _numberingSystem_ does not match the 1021cb0ef41Sopenharmony_ci // `(3*8alphanum) *("-" (3*8alphanum))` sequence, throw a *RangeError* 1031cb0ef41Sopenharmony_ci // exception. 1041cb0ef41Sopenharmony_ci MAYBE_RETURN(maybe_numberingSystem, MaybeHandle<JSRelativeTimeFormat>()); 1051cb0ef41Sopenharmony_ci 1061cb0ef41Sopenharmony_ci // 9. Set _opt_.[[nu]] to _numberingSystem_. 1071cb0ef41Sopenharmony_ci 1081cb0ef41Sopenharmony_ci // 10. Let localeData be %RelativeTimeFormat%.[[LocaleData]]. 1091cb0ef41Sopenharmony_ci // 11. Let r be 1101cb0ef41Sopenharmony_ci // ResolveLocale(%RelativeTimeFormat%.[[AvailableLocales]], 1111cb0ef41Sopenharmony_ci // requestedLocales, opt, 1121cb0ef41Sopenharmony_ci // %RelativeTimeFormat%.[[RelevantExtensionKeys]], localeData). 1131cb0ef41Sopenharmony_ci Maybe<Intl::ResolvedLocale> maybe_resolve_locale = 1141cb0ef41Sopenharmony_ci Intl::ResolveLocale(isolate, JSRelativeTimeFormat::GetAvailableLocales(), 1151cb0ef41Sopenharmony_ci requested_locales, matcher, {"nu"}); 1161cb0ef41Sopenharmony_ci if (maybe_resolve_locale.IsNothing()) { 1171cb0ef41Sopenharmony_ci THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kIcuError), 1181cb0ef41Sopenharmony_ci JSRelativeTimeFormat); 1191cb0ef41Sopenharmony_ci } 1201cb0ef41Sopenharmony_ci Intl::ResolvedLocale r = maybe_resolve_locale.FromJust(); 1211cb0ef41Sopenharmony_ci 1221cb0ef41Sopenharmony_ci UErrorCode status = U_ZERO_ERROR; 1231cb0ef41Sopenharmony_ci 1241cb0ef41Sopenharmony_ci icu::Locale icu_locale = r.icu_locale; 1251cb0ef41Sopenharmony_ci if (numbering_system_str != nullptr) { 1261cb0ef41Sopenharmony_ci auto nu_extension_it = r.extensions.find("nu"); 1271cb0ef41Sopenharmony_ci if (nu_extension_it != r.extensions.end() && 1281cb0ef41Sopenharmony_ci nu_extension_it->second != numbering_system_str.get()) { 1291cb0ef41Sopenharmony_ci icu_locale.setUnicodeKeywordValue("nu", nullptr, status); 1301cb0ef41Sopenharmony_ci DCHECK(U_SUCCESS(status)); 1311cb0ef41Sopenharmony_ci } 1321cb0ef41Sopenharmony_ci } 1331cb0ef41Sopenharmony_ci // 12. Let locale be r.[[Locale]]. 1341cb0ef41Sopenharmony_ci Maybe<std::string> maybe_locale_str = Intl::ToLanguageTag(icu_locale); 1351cb0ef41Sopenharmony_ci MAYBE_RETURN(maybe_locale_str, MaybeHandle<JSRelativeTimeFormat>()); 1361cb0ef41Sopenharmony_ci 1371cb0ef41Sopenharmony_ci // 13. Set relativeTimeFormat.[[Locale]] to locale. 1381cb0ef41Sopenharmony_ci Handle<String> locale_str = isolate->factory()->NewStringFromAsciiChecked( 1391cb0ef41Sopenharmony_ci maybe_locale_str.FromJust().c_str()); 1401cb0ef41Sopenharmony_ci 1411cb0ef41Sopenharmony_ci // 14. Set relativeTimeFormat.[[NumberingSystem]] to r.[[nu]]. 1421cb0ef41Sopenharmony_ci if (numbering_system_str != nullptr && 1431cb0ef41Sopenharmony_ci Intl::IsValidNumberingSystem(numbering_system_str.get())) { 1441cb0ef41Sopenharmony_ci icu_locale.setUnicodeKeywordValue("nu", numbering_system_str.get(), status); 1451cb0ef41Sopenharmony_ci DCHECK(U_SUCCESS(status)); 1461cb0ef41Sopenharmony_ci } 1471cb0ef41Sopenharmony_ci // 15. Let dataLocale be r.[[DataLocale]]. 1481cb0ef41Sopenharmony_ci 1491cb0ef41Sopenharmony_ci // 16. Let s be ? GetOption(options, "style", "string", 1501cb0ef41Sopenharmony_ci // «"long", "short", "narrow"», "long"). 1511cb0ef41Sopenharmony_ci Maybe<Style> maybe_style = GetStringOption<Style>( 1521cb0ef41Sopenharmony_ci isolate, options, "style", service, {"long", "short", "narrow"}, 1531cb0ef41Sopenharmony_ci {Style::LONG, Style::SHORT, Style::NARROW}, Style::LONG); 1541cb0ef41Sopenharmony_ci MAYBE_RETURN(maybe_style, MaybeHandle<JSRelativeTimeFormat>()); 1551cb0ef41Sopenharmony_ci Style style_enum = maybe_style.FromJust(); 1561cb0ef41Sopenharmony_ci 1571cb0ef41Sopenharmony_ci // 17. Set relativeTimeFormat.[[Style]] to s. 1581cb0ef41Sopenharmony_ci 1591cb0ef41Sopenharmony_ci // 18. Let numeric be ? GetOption(options, "numeric", "string", 1601cb0ef41Sopenharmony_ci // «"always", "auto"», "always"). 1611cb0ef41Sopenharmony_ci Maybe<Numeric> maybe_numeric = GetStringOption<Numeric>( 1621cb0ef41Sopenharmony_ci isolate, options, "numeric", service, {"always", "auto"}, 1631cb0ef41Sopenharmony_ci {Numeric::ALWAYS, Numeric::AUTO}, Numeric::ALWAYS); 1641cb0ef41Sopenharmony_ci MAYBE_RETURN(maybe_numeric, MaybeHandle<JSRelativeTimeFormat>()); 1651cb0ef41Sopenharmony_ci Numeric numeric_enum = maybe_numeric.FromJust(); 1661cb0ef41Sopenharmony_ci 1671cb0ef41Sopenharmony_ci // 19. Set relativeTimeFormat.[[Numeric]] to numeric. 1681cb0ef41Sopenharmony_ci 1691cb0ef41Sopenharmony_ci // 23. Let relativeTimeFormat.[[NumberFormat]] be 1701cb0ef41Sopenharmony_ci // ? Construct(%NumberFormat%, « nfLocale, nfOptions »). 1711cb0ef41Sopenharmony_ci icu::NumberFormat* number_format = 1721cb0ef41Sopenharmony_ci icu::NumberFormat::createInstance(icu_locale, UNUM_DECIMAL, status); 1731cb0ef41Sopenharmony_ci if (U_FAILURE(status)) { 1741cb0ef41Sopenharmony_ci // Data build filter files excluded data in "rbnf_tree" since ECMA402 does 1751cb0ef41Sopenharmony_ci // not support "algorithmic" numbering systems. Therefore we may get the 1761cb0ef41Sopenharmony_ci // U_MISSING_RESOURCE_ERROR here. Fallback to locale without the numbering 1771cb0ef41Sopenharmony_ci // system and create the object again. 1781cb0ef41Sopenharmony_ci if (status == U_MISSING_RESOURCE_ERROR) { 1791cb0ef41Sopenharmony_ci delete number_format; 1801cb0ef41Sopenharmony_ci status = U_ZERO_ERROR; 1811cb0ef41Sopenharmony_ci icu_locale.setUnicodeKeywordValue("nu", nullptr, status); 1821cb0ef41Sopenharmony_ci DCHECK(U_SUCCESS(status)); 1831cb0ef41Sopenharmony_ci number_format = 1841cb0ef41Sopenharmony_ci icu::NumberFormat::createInstance(icu_locale, UNUM_DECIMAL, status); 1851cb0ef41Sopenharmony_ci } 1861cb0ef41Sopenharmony_ci if (U_FAILURE(status) || number_format == nullptr) { 1871cb0ef41Sopenharmony_ci delete number_format; 1881cb0ef41Sopenharmony_ci THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kIcuError), 1891cb0ef41Sopenharmony_ci JSRelativeTimeFormat); 1901cb0ef41Sopenharmony_ci } 1911cb0ef41Sopenharmony_ci } 1921cb0ef41Sopenharmony_ci 1931cb0ef41Sopenharmony_ci if (number_format->getDynamicClassID() == 1941cb0ef41Sopenharmony_ci icu::DecimalFormat::getStaticClassID()) { 1951cb0ef41Sopenharmony_ci icu::DecimalFormat* decimal_format = 1961cb0ef41Sopenharmony_ci static_cast<icu::DecimalFormat*>(number_format); 1971cb0ef41Sopenharmony_ci decimal_format->setMinimumGroupingDigits(-2); 1981cb0ef41Sopenharmony_ci } 1991cb0ef41Sopenharmony_ci 2001cb0ef41Sopenharmony_ci // Change UDISPCTX_CAPITALIZATION_NONE to other values if 2011cb0ef41Sopenharmony_ci // ECMA402 later include option to change capitalization. 2021cb0ef41Sopenharmony_ci // Ref: https://github.com/tc39/proposal-intl-relative-time/issues/11 2031cb0ef41Sopenharmony_ci icu::RelativeDateTimeFormatter* icu_formatter = 2041cb0ef41Sopenharmony_ci new icu::RelativeDateTimeFormatter(icu_locale, number_format, 2051cb0ef41Sopenharmony_ci toIcuStyle(style_enum), 2061cb0ef41Sopenharmony_ci UDISPCTX_CAPITALIZATION_NONE, status); 2071cb0ef41Sopenharmony_ci if (U_FAILURE(status) || icu_formatter == nullptr) { 2081cb0ef41Sopenharmony_ci delete icu_formatter; 2091cb0ef41Sopenharmony_ci THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kIcuError), 2101cb0ef41Sopenharmony_ci JSRelativeTimeFormat); 2111cb0ef41Sopenharmony_ci } 2121cb0ef41Sopenharmony_ci 2131cb0ef41Sopenharmony_ci Handle<String> numbering_system_string = 2141cb0ef41Sopenharmony_ci isolate->factory()->NewStringFromAsciiChecked( 2151cb0ef41Sopenharmony_ci Intl::GetNumberingSystem(icu_locale).c_str()); 2161cb0ef41Sopenharmony_ci 2171cb0ef41Sopenharmony_ci Handle<Managed<icu::RelativeDateTimeFormatter>> managed_formatter = 2181cb0ef41Sopenharmony_ci Managed<icu::RelativeDateTimeFormatter>::FromRawPtr(isolate, 0, 2191cb0ef41Sopenharmony_ci icu_formatter); 2201cb0ef41Sopenharmony_ci 2211cb0ef41Sopenharmony_ci Handle<JSRelativeTimeFormat> relative_time_format_holder = 2221cb0ef41Sopenharmony_ci Handle<JSRelativeTimeFormat>::cast( 2231cb0ef41Sopenharmony_ci isolate->factory()->NewFastOrSlowJSObjectFromMap(map)); 2241cb0ef41Sopenharmony_ci 2251cb0ef41Sopenharmony_ci DisallowGarbageCollection no_gc; 2261cb0ef41Sopenharmony_ci relative_time_format_holder->set_flags(0); 2271cb0ef41Sopenharmony_ci relative_time_format_holder->set_locale(*locale_str); 2281cb0ef41Sopenharmony_ci relative_time_format_holder->set_numberingSystem(*numbering_system_string); 2291cb0ef41Sopenharmony_ci relative_time_format_holder->set_numeric(numeric_enum); 2301cb0ef41Sopenharmony_ci relative_time_format_holder->set_icu_formatter(*managed_formatter); 2311cb0ef41Sopenharmony_ci 2321cb0ef41Sopenharmony_ci // 25. Return relativeTimeFormat. 2331cb0ef41Sopenharmony_ci return relative_time_format_holder; 2341cb0ef41Sopenharmony_ci} 2351cb0ef41Sopenharmony_ci 2361cb0ef41Sopenharmony_cinamespace { 2371cb0ef41Sopenharmony_ci 2381cb0ef41Sopenharmony_ciHandle<String> StyleAsString(Isolate* isolate, Style style) { 2391cb0ef41Sopenharmony_ci switch (style) { 2401cb0ef41Sopenharmony_ci case Style::LONG: 2411cb0ef41Sopenharmony_ci return ReadOnlyRoots(isolate).long_string_handle(); 2421cb0ef41Sopenharmony_ci case Style::SHORT: 2431cb0ef41Sopenharmony_ci return ReadOnlyRoots(isolate).short_string_handle(); 2441cb0ef41Sopenharmony_ci case Style::NARROW: 2451cb0ef41Sopenharmony_ci return ReadOnlyRoots(isolate).narrow_string_handle(); 2461cb0ef41Sopenharmony_ci } 2471cb0ef41Sopenharmony_ci UNREACHABLE(); 2481cb0ef41Sopenharmony_ci} 2491cb0ef41Sopenharmony_ci 2501cb0ef41Sopenharmony_ci} // namespace 2511cb0ef41Sopenharmony_ci 2521cb0ef41Sopenharmony_ciHandle<JSObject> JSRelativeTimeFormat::ResolvedOptions( 2531cb0ef41Sopenharmony_ci Isolate* isolate, Handle<JSRelativeTimeFormat> format_holder) { 2541cb0ef41Sopenharmony_ci Factory* factory = isolate->factory(); 2551cb0ef41Sopenharmony_ci icu::RelativeDateTimeFormatter* formatter = 2561cb0ef41Sopenharmony_ci format_holder->icu_formatter().raw(); 2571cb0ef41Sopenharmony_ci DCHECK_NOT_NULL(formatter); 2581cb0ef41Sopenharmony_ci Handle<JSObject> result = factory->NewJSObject(isolate->object_function()); 2591cb0ef41Sopenharmony_ci Handle<String> locale(format_holder->locale(), isolate); 2601cb0ef41Sopenharmony_ci Handle<String> numberingSystem(format_holder->numberingSystem(), isolate); 2611cb0ef41Sopenharmony_ci JSObject::AddProperty(isolate, result, factory->locale_string(), locale, 2621cb0ef41Sopenharmony_ci NONE); 2631cb0ef41Sopenharmony_ci JSObject::AddProperty( 2641cb0ef41Sopenharmony_ci isolate, result, factory->style_string(), 2651cb0ef41Sopenharmony_ci StyleAsString(isolate, fromIcuStyle(formatter->getFormatStyle())), NONE); 2661cb0ef41Sopenharmony_ci JSObject::AddProperty(isolate, result, factory->numeric_string(), 2671cb0ef41Sopenharmony_ci format_holder->NumericAsString(), NONE); 2681cb0ef41Sopenharmony_ci JSObject::AddProperty(isolate, result, factory->numberingSystem_string(), 2691cb0ef41Sopenharmony_ci numberingSystem, NONE); 2701cb0ef41Sopenharmony_ci return result; 2711cb0ef41Sopenharmony_ci} 2721cb0ef41Sopenharmony_ci 2731cb0ef41Sopenharmony_ciHandle<String> JSRelativeTimeFormat::NumericAsString() const { 2741cb0ef41Sopenharmony_ci switch (numeric()) { 2751cb0ef41Sopenharmony_ci case Numeric::ALWAYS: 2761cb0ef41Sopenharmony_ci return GetReadOnlyRoots().always_string_handle(); 2771cb0ef41Sopenharmony_ci case Numeric::AUTO: 2781cb0ef41Sopenharmony_ci return GetReadOnlyRoots().auto_string_handle(); 2791cb0ef41Sopenharmony_ci } 2801cb0ef41Sopenharmony_ci UNREACHABLE(); 2811cb0ef41Sopenharmony_ci} 2821cb0ef41Sopenharmony_ci 2831cb0ef41Sopenharmony_cinamespace { 2841cb0ef41Sopenharmony_ci 2851cb0ef41Sopenharmony_ciHandle<String> UnitAsString(Isolate* isolate, URelativeDateTimeUnit unit_enum) { 2861cb0ef41Sopenharmony_ci Factory* factory = isolate->factory(); 2871cb0ef41Sopenharmony_ci switch (unit_enum) { 2881cb0ef41Sopenharmony_ci case UDAT_REL_UNIT_SECOND: 2891cb0ef41Sopenharmony_ci return factory->second_string(); 2901cb0ef41Sopenharmony_ci case UDAT_REL_UNIT_MINUTE: 2911cb0ef41Sopenharmony_ci return factory->minute_string(); 2921cb0ef41Sopenharmony_ci case UDAT_REL_UNIT_HOUR: 2931cb0ef41Sopenharmony_ci return factory->hour_string(); 2941cb0ef41Sopenharmony_ci case UDAT_REL_UNIT_DAY: 2951cb0ef41Sopenharmony_ci return factory->day_string(); 2961cb0ef41Sopenharmony_ci case UDAT_REL_UNIT_WEEK: 2971cb0ef41Sopenharmony_ci return factory->week_string(); 2981cb0ef41Sopenharmony_ci case UDAT_REL_UNIT_MONTH: 2991cb0ef41Sopenharmony_ci return factory->month_string(); 3001cb0ef41Sopenharmony_ci case UDAT_REL_UNIT_QUARTER: 3011cb0ef41Sopenharmony_ci return factory->quarter_string(); 3021cb0ef41Sopenharmony_ci case UDAT_REL_UNIT_YEAR: 3031cb0ef41Sopenharmony_ci return factory->year_string(); 3041cb0ef41Sopenharmony_ci default: 3051cb0ef41Sopenharmony_ci UNREACHABLE(); 3061cb0ef41Sopenharmony_ci } 3071cb0ef41Sopenharmony_ci} 3081cb0ef41Sopenharmony_ci 3091cb0ef41Sopenharmony_cibool GetURelativeDateTimeUnit(Handle<String> unit, 3101cb0ef41Sopenharmony_ci URelativeDateTimeUnit* unit_enum) { 3111cb0ef41Sopenharmony_ci std::unique_ptr<char[]> unit_str = unit->ToCString(); 3121cb0ef41Sopenharmony_ci if ((strcmp("second", unit_str.get()) == 0) || 3131cb0ef41Sopenharmony_ci (strcmp("seconds", unit_str.get()) == 0)) { 3141cb0ef41Sopenharmony_ci *unit_enum = UDAT_REL_UNIT_SECOND; 3151cb0ef41Sopenharmony_ci } else if ((strcmp("minute", unit_str.get()) == 0) || 3161cb0ef41Sopenharmony_ci (strcmp("minutes", unit_str.get()) == 0)) { 3171cb0ef41Sopenharmony_ci *unit_enum = UDAT_REL_UNIT_MINUTE; 3181cb0ef41Sopenharmony_ci } else if ((strcmp("hour", unit_str.get()) == 0) || 3191cb0ef41Sopenharmony_ci (strcmp("hours", unit_str.get()) == 0)) { 3201cb0ef41Sopenharmony_ci *unit_enum = UDAT_REL_UNIT_HOUR; 3211cb0ef41Sopenharmony_ci } else if ((strcmp("day", unit_str.get()) == 0) || 3221cb0ef41Sopenharmony_ci (strcmp("days", unit_str.get()) == 0)) { 3231cb0ef41Sopenharmony_ci *unit_enum = UDAT_REL_UNIT_DAY; 3241cb0ef41Sopenharmony_ci } else if ((strcmp("week", unit_str.get()) == 0) || 3251cb0ef41Sopenharmony_ci (strcmp("weeks", unit_str.get()) == 0)) { 3261cb0ef41Sopenharmony_ci *unit_enum = UDAT_REL_UNIT_WEEK; 3271cb0ef41Sopenharmony_ci } else if ((strcmp("month", unit_str.get()) == 0) || 3281cb0ef41Sopenharmony_ci (strcmp("months", unit_str.get()) == 0)) { 3291cb0ef41Sopenharmony_ci *unit_enum = UDAT_REL_UNIT_MONTH; 3301cb0ef41Sopenharmony_ci } else if ((strcmp("quarter", unit_str.get()) == 0) || 3311cb0ef41Sopenharmony_ci (strcmp("quarters", unit_str.get()) == 0)) { 3321cb0ef41Sopenharmony_ci *unit_enum = UDAT_REL_UNIT_QUARTER; 3331cb0ef41Sopenharmony_ci } else if ((strcmp("year", unit_str.get()) == 0) || 3341cb0ef41Sopenharmony_ci (strcmp("years", unit_str.get()) == 0)) { 3351cb0ef41Sopenharmony_ci *unit_enum = UDAT_REL_UNIT_YEAR; 3361cb0ef41Sopenharmony_ci } else { 3371cb0ef41Sopenharmony_ci return false; 3381cb0ef41Sopenharmony_ci } 3391cb0ef41Sopenharmony_ci return true; 3401cb0ef41Sopenharmony_ci} 3411cb0ef41Sopenharmony_ci 3421cb0ef41Sopenharmony_citemplate <typename T> 3431cb0ef41Sopenharmony_ciMaybeHandle<T> FormatCommon( 3441cb0ef41Sopenharmony_ci Isolate* isolate, Handle<JSRelativeTimeFormat> format, 3451cb0ef41Sopenharmony_ci Handle<Object> value_obj, Handle<Object> unit_obj, const char* func_name, 3461cb0ef41Sopenharmony_ci MaybeHandle<T> (*formatToResult)(Isolate*, 3471cb0ef41Sopenharmony_ci const icu::FormattedRelativeDateTime&, 3481cb0ef41Sopenharmony_ci Handle<String>, bool)) { 3491cb0ef41Sopenharmony_ci // 3. Let value be ? ToNumber(value). 3501cb0ef41Sopenharmony_ci Handle<Object> value; 3511cb0ef41Sopenharmony_ci ASSIGN_RETURN_ON_EXCEPTION(isolate, value, 3521cb0ef41Sopenharmony_ci Object::ToNumber(isolate, value_obj), T); 3531cb0ef41Sopenharmony_ci double number = value->Number(); 3541cb0ef41Sopenharmony_ci // 4. Let unit be ? ToString(unit). 3551cb0ef41Sopenharmony_ci Handle<String> unit; 3561cb0ef41Sopenharmony_ci ASSIGN_RETURN_ON_EXCEPTION(isolate, unit, Object::ToString(isolate, unit_obj), 3571cb0ef41Sopenharmony_ci T); 3581cb0ef41Sopenharmony_ci // 4. If isFinite(value) is false, then throw a RangeError exception. 3591cb0ef41Sopenharmony_ci if (!std::isfinite(number)) { 3601cb0ef41Sopenharmony_ci THROW_NEW_ERROR( 3611cb0ef41Sopenharmony_ci isolate, 3621cb0ef41Sopenharmony_ci NewRangeError(MessageTemplate::kNotFiniteNumber, 3631cb0ef41Sopenharmony_ci isolate->factory()->NewStringFromAsciiChecked(func_name)), 3641cb0ef41Sopenharmony_ci T); 3651cb0ef41Sopenharmony_ci } 3661cb0ef41Sopenharmony_ci icu::RelativeDateTimeFormatter* formatter = format->icu_formatter().raw(); 3671cb0ef41Sopenharmony_ci DCHECK_NOT_NULL(formatter); 3681cb0ef41Sopenharmony_ci URelativeDateTimeUnit unit_enum; 3691cb0ef41Sopenharmony_ci if (!GetURelativeDateTimeUnit(unit, &unit_enum)) { 3701cb0ef41Sopenharmony_ci THROW_NEW_ERROR( 3711cb0ef41Sopenharmony_ci isolate, 3721cb0ef41Sopenharmony_ci NewRangeError(MessageTemplate::kInvalidUnit, 3731cb0ef41Sopenharmony_ci isolate->factory()->NewStringFromAsciiChecked(func_name), 3741cb0ef41Sopenharmony_ci unit), 3751cb0ef41Sopenharmony_ci T); 3761cb0ef41Sopenharmony_ci } 3771cb0ef41Sopenharmony_ci UErrorCode status = U_ZERO_ERROR; 3781cb0ef41Sopenharmony_ci icu::FormattedRelativeDateTime formatted = 3791cb0ef41Sopenharmony_ci (format->numeric() == JSRelativeTimeFormat::Numeric::ALWAYS) 3801cb0ef41Sopenharmony_ci ? formatter->formatNumericToValue(number, unit_enum, status) 3811cb0ef41Sopenharmony_ci : formatter->formatToValue(number, unit_enum, status); 3821cb0ef41Sopenharmony_ci if (U_FAILURE(status)) { 3831cb0ef41Sopenharmony_ci THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError), T); 3841cb0ef41Sopenharmony_ci } 3851cb0ef41Sopenharmony_ci return formatToResult(isolate, formatted, UnitAsString(isolate, unit_enum), 3861cb0ef41Sopenharmony_ci value->IsNaN()); 3871cb0ef41Sopenharmony_ci} 3881cb0ef41Sopenharmony_ci 3891cb0ef41Sopenharmony_ciMaybeHandle<String> FormatToString( 3901cb0ef41Sopenharmony_ci Isolate* isolate, const icu::FormattedRelativeDateTime& formatted, 3911cb0ef41Sopenharmony_ci Handle<String> unit, bool is_nan) { 3921cb0ef41Sopenharmony_ci UErrorCode status = U_ZERO_ERROR; 3931cb0ef41Sopenharmony_ci icu::UnicodeString result = formatted.toString(status); 3941cb0ef41Sopenharmony_ci if (U_FAILURE(status)) { 3951cb0ef41Sopenharmony_ci THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError), String); 3961cb0ef41Sopenharmony_ci } 3971cb0ef41Sopenharmony_ci return Intl::ToString(isolate, result); 3981cb0ef41Sopenharmony_ci} 3991cb0ef41Sopenharmony_ci 4001cb0ef41Sopenharmony_ciMaybe<bool> AddLiteral(Isolate* isolate, Handle<JSArray> array, 4011cb0ef41Sopenharmony_ci const icu::UnicodeString& string, int32_t index, 4021cb0ef41Sopenharmony_ci int32_t start, int32_t limit) { 4031cb0ef41Sopenharmony_ci Handle<String> substring; 4041cb0ef41Sopenharmony_ci ASSIGN_RETURN_ON_EXCEPTION_VALUE( 4051cb0ef41Sopenharmony_ci isolate, substring, Intl::ToString(isolate, string, start, limit), 4061cb0ef41Sopenharmony_ci Nothing<bool>()); 4071cb0ef41Sopenharmony_ci Intl::AddElement(isolate, array, index, isolate->factory()->literal_string(), 4081cb0ef41Sopenharmony_ci substring); 4091cb0ef41Sopenharmony_ci return Just(true); 4101cb0ef41Sopenharmony_ci} 4111cb0ef41Sopenharmony_ci 4121cb0ef41Sopenharmony_ciMaybe<bool> AddUnit(Isolate* isolate, Handle<JSArray> array, 4131cb0ef41Sopenharmony_ci const icu::UnicodeString& string, int32_t index, 4141cb0ef41Sopenharmony_ci const NumberFormatSpan& part, Handle<String> unit, 4151cb0ef41Sopenharmony_ci bool is_nan) { 4161cb0ef41Sopenharmony_ci Handle<String> substring; 4171cb0ef41Sopenharmony_ci ASSIGN_RETURN_ON_EXCEPTION_VALUE( 4181cb0ef41Sopenharmony_ci isolate, substring, 4191cb0ef41Sopenharmony_ci Intl::ToString(isolate, string, part.begin_pos, part.end_pos), 4201cb0ef41Sopenharmony_ci Nothing<bool>()); 4211cb0ef41Sopenharmony_ci Intl::AddElement(isolate, array, index, 4221cb0ef41Sopenharmony_ci Intl::NumberFieldToType(isolate, part, string, is_nan), 4231cb0ef41Sopenharmony_ci substring, isolate->factory()->unit_string(), unit); 4241cb0ef41Sopenharmony_ci return Just(true); 4251cb0ef41Sopenharmony_ci} 4261cb0ef41Sopenharmony_ci 4271cb0ef41Sopenharmony_ciMaybeHandle<JSArray> FormatToJSArray( 4281cb0ef41Sopenharmony_ci Isolate* isolate, const icu::FormattedRelativeDateTime& formatted, 4291cb0ef41Sopenharmony_ci Handle<String> unit, bool is_nan) { 4301cb0ef41Sopenharmony_ci UErrorCode status = U_ZERO_ERROR; 4311cb0ef41Sopenharmony_ci icu::UnicodeString string = formatted.toString(status); 4321cb0ef41Sopenharmony_ci 4331cb0ef41Sopenharmony_ci Factory* factory = isolate->factory(); 4341cb0ef41Sopenharmony_ci Handle<JSArray> array = factory->NewJSArray(0); 4351cb0ef41Sopenharmony_ci icu::ConstrainedFieldPosition cfpos; 4361cb0ef41Sopenharmony_ci cfpos.constrainCategory(UFIELD_CATEGORY_NUMBER); 4371cb0ef41Sopenharmony_ci int32_t index = 0; 4381cb0ef41Sopenharmony_ci 4391cb0ef41Sopenharmony_ci int32_t previous_end = 0; 4401cb0ef41Sopenharmony_ci Handle<String> substring; 4411cb0ef41Sopenharmony_ci std::vector<std::pair<int32_t, int32_t>> groups; 4421cb0ef41Sopenharmony_ci while (formatted.nextPosition(cfpos, status) && U_SUCCESS(status)) { 4431cb0ef41Sopenharmony_ci int32_t category = cfpos.getCategory(); 4441cb0ef41Sopenharmony_ci int32_t field = cfpos.getField(); 4451cb0ef41Sopenharmony_ci int32_t start = cfpos.getStart(); 4461cb0ef41Sopenharmony_ci int32_t limit = cfpos.getLimit(); 4471cb0ef41Sopenharmony_ci if (category == UFIELD_CATEGORY_NUMBER) { 4481cb0ef41Sopenharmony_ci if (field == UNUM_GROUPING_SEPARATOR_FIELD) { 4491cb0ef41Sopenharmony_ci groups.push_back(std::pair<int32_t, int32_t>(start, limit)); 4501cb0ef41Sopenharmony_ci continue; 4511cb0ef41Sopenharmony_ci } 4521cb0ef41Sopenharmony_ci if (start > previous_end) { 4531cb0ef41Sopenharmony_ci Maybe<bool> maybe_added = 4541cb0ef41Sopenharmony_ci AddLiteral(isolate, array, string, index++, previous_end, start); 4551cb0ef41Sopenharmony_ci MAYBE_RETURN(maybe_added, Handle<JSArray>()); 4561cb0ef41Sopenharmony_ci } 4571cb0ef41Sopenharmony_ci if (field == UNUM_INTEGER_FIELD) { 4581cb0ef41Sopenharmony_ci for (auto start_limit : groups) { 4591cb0ef41Sopenharmony_ci if (start_limit.first > start) { 4601cb0ef41Sopenharmony_ci Maybe<bool> maybe_added = 4611cb0ef41Sopenharmony_ci AddUnit(isolate, array, string, index++, 4621cb0ef41Sopenharmony_ci NumberFormatSpan(field, start, start_limit.first), unit, 4631cb0ef41Sopenharmony_ci is_nan); 4641cb0ef41Sopenharmony_ci MAYBE_RETURN(maybe_added, Handle<JSArray>()); 4651cb0ef41Sopenharmony_ci maybe_added = 4661cb0ef41Sopenharmony_ci AddUnit(isolate, array, string, index++, 4671cb0ef41Sopenharmony_ci NumberFormatSpan(UNUM_GROUPING_SEPARATOR_FIELD, 4681cb0ef41Sopenharmony_ci start_limit.first, start_limit.second), 4691cb0ef41Sopenharmony_ci unit, is_nan); 4701cb0ef41Sopenharmony_ci MAYBE_RETURN(maybe_added, Handle<JSArray>()); 4711cb0ef41Sopenharmony_ci start = start_limit.second; 4721cb0ef41Sopenharmony_ci } 4731cb0ef41Sopenharmony_ci } 4741cb0ef41Sopenharmony_ci } 4751cb0ef41Sopenharmony_ci Maybe<bool> maybe_added = 4761cb0ef41Sopenharmony_ci AddUnit(isolate, array, string, index++, 4771cb0ef41Sopenharmony_ci NumberFormatSpan(field, start, limit), unit, is_nan); 4781cb0ef41Sopenharmony_ci MAYBE_RETURN(maybe_added, Handle<JSArray>()); 4791cb0ef41Sopenharmony_ci previous_end = limit; 4801cb0ef41Sopenharmony_ci } 4811cb0ef41Sopenharmony_ci } 4821cb0ef41Sopenharmony_ci if (U_FAILURE(status)) { 4831cb0ef41Sopenharmony_ci THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError), JSArray); 4841cb0ef41Sopenharmony_ci } 4851cb0ef41Sopenharmony_ci if (string.length() > previous_end) { 4861cb0ef41Sopenharmony_ci Maybe<bool> maybe_added = AddLiteral(isolate, array, string, index, 4871cb0ef41Sopenharmony_ci previous_end, string.length()); 4881cb0ef41Sopenharmony_ci MAYBE_RETURN(maybe_added, Handle<JSArray>()); 4891cb0ef41Sopenharmony_ci } 4901cb0ef41Sopenharmony_ci 4911cb0ef41Sopenharmony_ci JSObject::ValidateElements(*array); 4921cb0ef41Sopenharmony_ci return array; 4931cb0ef41Sopenharmony_ci} 4941cb0ef41Sopenharmony_ci 4951cb0ef41Sopenharmony_ci} // namespace 4961cb0ef41Sopenharmony_ci 4971cb0ef41Sopenharmony_ciMaybeHandle<String> JSRelativeTimeFormat::Format( 4981cb0ef41Sopenharmony_ci Isolate* isolate, Handle<Object> value_obj, Handle<Object> unit_obj, 4991cb0ef41Sopenharmony_ci Handle<JSRelativeTimeFormat> format) { 5001cb0ef41Sopenharmony_ci return FormatCommon<String>(isolate, format, value_obj, unit_obj, 5011cb0ef41Sopenharmony_ci "Intl.RelativeTimeFormat.prototype.format", 5021cb0ef41Sopenharmony_ci FormatToString); 5031cb0ef41Sopenharmony_ci} 5041cb0ef41Sopenharmony_ci 5051cb0ef41Sopenharmony_ciMaybeHandle<JSArray> JSRelativeTimeFormat::FormatToParts( 5061cb0ef41Sopenharmony_ci Isolate* isolate, Handle<Object> value_obj, Handle<Object> unit_obj, 5071cb0ef41Sopenharmony_ci Handle<JSRelativeTimeFormat> format) { 5081cb0ef41Sopenharmony_ci return FormatCommon<JSArray>( 5091cb0ef41Sopenharmony_ci isolate, format, value_obj, unit_obj, 5101cb0ef41Sopenharmony_ci "Intl.RelativeTimeFormat.prototype.formatToParts", FormatToJSArray); 5111cb0ef41Sopenharmony_ci} 5121cb0ef41Sopenharmony_ci 5131cb0ef41Sopenharmony_ciconst std::set<std::string>& JSRelativeTimeFormat::GetAvailableLocales() { 5141cb0ef41Sopenharmony_ci // Since RelativeTimeFormatter does not have a method to list all 5151cb0ef41Sopenharmony_ci // available locales, work around by calling the DateFormat. 5161cb0ef41Sopenharmony_ci return Intl::GetAvailableLocalesForDateFormat(); 5171cb0ef41Sopenharmony_ci} 5181cb0ef41Sopenharmony_ci 5191cb0ef41Sopenharmony_ci} // namespace internal 5201cb0ef41Sopenharmony_ci} // namespace v8 521