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