• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef V8_INTL_SUPPORT
6 #error Internationalization is expected to be enabled.
7 #endif  // V8_INTL_SUPPORT
8 
9 #include "src/objects/js-relative-time-format.h"
10 
11 #include <map>
12 #include <memory>
13 #include <string>
14 
15 #include "src/execution/isolate.h"
16 #include "src/heap/factory.h"
17 #include "src/objects/intl-objects.h"
18 #include "src/objects/js-number-format.h"
19 #include "src/objects/js-relative-time-format-inl.h"
20 #include "src/objects/objects-inl.h"
21 #include "unicode/decimfmt.h"
22 #include "unicode/numfmt.h"
23 #include "unicode/reldatefmt.h"
24 #include "unicode/unum.h"
25 
26 namespace v8 {
27 namespace internal {
28 
29 namespace {
30 // Style: identifying the relative time format style used.
31 //
32 // ecma402/#sec-properties-of-intl-relativetimeformat-instances
33 
34 enum class Style {
35   LONG,   // Everything spelled out.
36   SHORT,  // Abbreviations used when possible.
37   NARROW  // Use the shortest possible form.
38 };
39 
toIcuStyle(Style style)40 UDateRelativeDateTimeFormatterStyle toIcuStyle(Style style) {
41   switch (style) {
42     case Style::LONG:
43       return UDAT_STYLE_LONG;
44     case Style::SHORT:
45       return UDAT_STYLE_SHORT;
46     case Style::NARROW:
47       return UDAT_STYLE_NARROW;
48   }
49   UNREACHABLE();
50 }
51 
fromIcuStyle(UDateRelativeDateTimeFormatterStyle icu_style)52 Style fromIcuStyle(UDateRelativeDateTimeFormatterStyle icu_style) {
53   switch (icu_style) {
54     case UDAT_STYLE_LONG:
55       return Style::LONG;
56     case UDAT_STYLE_SHORT:
57       return Style::SHORT;
58     case UDAT_STYLE_NARROW:
59       return Style::NARROW;
60     case UDAT_STYLE_COUNT:
61       UNREACHABLE();
62   }
63   UNREACHABLE();
64 }
65 }  // namespace
66 
New(Isolate * isolate,Handle<Map> map,Handle<Object> locales,Handle<Object> input_options)67 MaybeHandle<JSRelativeTimeFormat> JSRelativeTimeFormat::New(
68     Isolate* isolate, Handle<Map> map, Handle<Object> locales,
69     Handle<Object> input_options) {
70   // 1. Let requestedLocales be ? CanonicalizeLocaleList(locales).
71   Maybe<std::vector<std::string>> maybe_requested_locales =
72       Intl::CanonicalizeLocaleList(isolate, locales);
73   MAYBE_RETURN(maybe_requested_locales, Handle<JSRelativeTimeFormat>());
74   std::vector<std::string> requested_locales =
75       maybe_requested_locales.FromJust();
76 
77   // 2. If options is undefined, then
78   Handle<JSReceiver> options;
79   if (input_options->IsUndefined(isolate)) {
80     // 2. a. Let options be ObjectCreate(null).
81     options = isolate->factory()->NewJSObjectWithNullProto();
82     // 3. Else
83   } else {
84     // 3. a. Let options be ? ToObject(options).
85     ASSIGN_RETURN_ON_EXCEPTION(isolate, options,
86                                Object::ToObject(isolate, input_options),
87                                JSRelativeTimeFormat);
88   }
89 
90   // 4. Let opt be a new Record.
91   // 5. Let matcher be ? GetOption(options, "localeMatcher", "string", «
92   // "lookup", "best fit" », "best fit").
93   // 6. Set opt.[[localeMatcher]] to matcher.
94   Maybe<Intl::MatcherOption> maybe_locale_matcher =
95       Intl::GetLocaleMatcher(isolate, options, "Intl.RelativeTimeFormat");
96   MAYBE_RETURN(maybe_locale_matcher, MaybeHandle<JSRelativeTimeFormat>());
97   Intl::MatcherOption matcher = maybe_locale_matcher.FromJust();
98 
99   // 7. Let _numberingSystem_ be ? GetOption(_options_, `"numberingSystem"`,
100   //    `"string"`, *undefined*, *undefined*).
101   std::unique_ptr<char[]> numbering_system_str = nullptr;
102   Maybe<bool> maybe_numberingSystem = Intl::GetNumberingSystem(
103       isolate, options, "Intl.RelativeTimeFormat", &numbering_system_str);
104   // 8. If _numberingSystem_ is not *undefined*, then
105   // a. If _numberingSystem_ does not match the
106   //    `(3*8alphanum) *("-" (3*8alphanum))` sequence, throw a *RangeError*
107   //     exception.
108   MAYBE_RETURN(maybe_numberingSystem, MaybeHandle<JSRelativeTimeFormat>());
109 
110   // 9. Set _opt_.[[nu]] to _numberingSystem_.
111 
112   // 10. Let localeData be %RelativeTimeFormat%.[[LocaleData]].
113   // 11. Let r be
114   // ResolveLocale(%RelativeTimeFormat%.[[AvailableLocales]],
115   //               requestedLocales, opt,
116   //               %RelativeTimeFormat%.[[RelevantExtensionKeys]], localeData).
117   Maybe<Intl::ResolvedLocale> maybe_resolve_locale =
118       Intl::ResolveLocale(isolate, JSRelativeTimeFormat::GetAvailableLocales(),
119                           requested_locales, matcher, {"nu"});
120   if (maybe_resolve_locale.IsNothing()) {
121     THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kIcuError),
122                     JSRelativeTimeFormat);
123   }
124   Intl::ResolvedLocale r = maybe_resolve_locale.FromJust();
125 
126   UErrorCode status = U_ZERO_ERROR;
127 
128   icu::Locale icu_locale = r.icu_locale;
129   if (numbering_system_str != nullptr) {
130     auto nu_extension_it = r.extensions.find("nu");
131     if (nu_extension_it != r.extensions.end() &&
132         nu_extension_it->second != numbering_system_str.get()) {
133       icu_locale.setUnicodeKeywordValue("nu", nullptr, status);
134       DCHECK(U_SUCCESS(status));
135     }
136   }
137   // 12. Let locale be r.[[Locale]].
138   Maybe<std::string> maybe_locale_str = Intl::ToLanguageTag(icu_locale);
139   MAYBE_RETURN(maybe_locale_str, MaybeHandle<JSRelativeTimeFormat>());
140 
141   // 13. Set relativeTimeFormat.[[Locale]] to locale.
142   Handle<String> locale_str = isolate->factory()->NewStringFromAsciiChecked(
143       maybe_locale_str.FromJust().c_str());
144 
145   // 14. Set relativeTimeFormat.[[NumberingSystem]] to r.[[nu]].
146   if (numbering_system_str != nullptr &&
147       Intl::IsValidNumberingSystem(numbering_system_str.get())) {
148     icu_locale.setUnicodeKeywordValue("nu", numbering_system_str.get(), status);
149     DCHECK(U_SUCCESS(status));
150   }
151   // 15. Let dataLocale be r.[[DataLocale]].
152 
153   // 16. Let s be ? GetOption(options, "style", "string",
154   //                          «"long", "short", "narrow"», "long").
155   Maybe<Style> maybe_style = Intl::GetStringOption<Style>(
156       isolate, options, "style", "Intl.RelativeTimeFormat",
157       {"long", "short", "narrow"}, {Style::LONG, Style::SHORT, Style::NARROW},
158       Style::LONG);
159   MAYBE_RETURN(maybe_style, MaybeHandle<JSRelativeTimeFormat>());
160   Style style_enum = maybe_style.FromJust();
161 
162   // 17. Set relativeTimeFormat.[[Style]] to s.
163 
164   // 18. Let numeric be ? GetOption(options, "numeric", "string",
165   //                                «"always", "auto"», "always").
166   Maybe<Numeric> maybe_numeric = Intl::GetStringOption<Numeric>(
167       isolate, options, "numeric", "Intl.RelativeTimeFormat",
168       {"always", "auto"}, {Numeric::ALWAYS, Numeric::AUTO}, Numeric::ALWAYS);
169   MAYBE_RETURN(maybe_numeric, MaybeHandle<JSRelativeTimeFormat>());
170   Numeric numeric_enum = maybe_numeric.FromJust();
171 
172   // 19. Set relativeTimeFormat.[[Numeric]] to numeric.
173 
174   // 23. Let relativeTimeFormat.[[NumberFormat]] be
175   //     ? Construct(%NumberFormat%, « nfLocale, nfOptions »).
176   icu::NumberFormat* number_format =
177       icu::NumberFormat::createInstance(icu_locale, UNUM_DECIMAL, status);
178   if (U_FAILURE(status)) {
179     // Data build filter files excluded data in "rbnf_tree" since ECMA402 does
180     // not support "algorithmic" numbering systems. Therefore we may get the
181     // U_MISSING_RESOURCE_ERROR here. Fallback to locale without the numbering
182     // system and create the object again.
183     if (status == U_MISSING_RESOURCE_ERROR) {
184       delete number_format;
185       status = U_ZERO_ERROR;
186       icu_locale.setUnicodeKeywordValue("nu", nullptr, status);
187       DCHECK(U_SUCCESS(status));
188       number_format =
189           icu::NumberFormat::createInstance(icu_locale, UNUM_DECIMAL, status);
190     }
191     if (U_FAILURE(status) || number_format == nullptr) {
192       delete number_format;
193       THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kIcuError),
194                       JSRelativeTimeFormat);
195     }
196   }
197 
198   if (number_format->getDynamicClassID() ==
199       icu::DecimalFormat::getStaticClassID()) {
200     icu::DecimalFormat* decimal_format =
201         static_cast<icu::DecimalFormat*>(number_format);
202     decimal_format->setMinimumGroupingDigits(-2);
203   }
204 
205   // Change UDISPCTX_CAPITALIZATION_NONE to other values if
206   // ECMA402 later include option to change capitalization.
207   // Ref: https://github.com/tc39/proposal-intl-relative-time/issues/11
208   icu::RelativeDateTimeFormatter* icu_formatter =
209       new icu::RelativeDateTimeFormatter(icu_locale, number_format,
210                                          toIcuStyle(style_enum),
211                                          UDISPCTX_CAPITALIZATION_NONE, status);
212   if (U_FAILURE(status) || icu_formatter == nullptr) {
213     delete icu_formatter;
214     THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kIcuError),
215                     JSRelativeTimeFormat);
216   }
217 
218   Handle<String> numbering_system_string =
219       isolate->factory()->NewStringFromAsciiChecked(
220           Intl::GetNumberingSystem(icu_locale).c_str());
221 
222   Handle<Managed<icu::RelativeDateTimeFormatter>> managed_formatter =
223       Managed<icu::RelativeDateTimeFormatter>::FromRawPtr(isolate, 0,
224                                                           icu_formatter);
225 
226   Handle<JSRelativeTimeFormat> relative_time_format_holder =
227       Handle<JSRelativeTimeFormat>::cast(
228           isolate->factory()->NewFastOrSlowJSObjectFromMap(map));
229 
230   DisallowHeapAllocation no_gc;
231   relative_time_format_holder->set_flags(0);
232   relative_time_format_holder->set_locale(*locale_str);
233   relative_time_format_holder->set_numberingSystem(*numbering_system_string);
234   relative_time_format_holder->set_numeric(numeric_enum);
235   relative_time_format_holder->set_icu_formatter(*managed_formatter);
236 
237   // 25. Return relativeTimeFormat.
238   return relative_time_format_holder;
239 }
240 
241 namespace {
242 
StyleAsString(Isolate * isolate,Style style)243 Handle<String> StyleAsString(Isolate* isolate, Style style) {
244   switch (style) {
245     case Style::LONG:
246       return ReadOnlyRoots(isolate).long_string_handle();
247     case Style::SHORT:
248       return ReadOnlyRoots(isolate).short_string_handle();
249     case Style::NARROW:
250       return ReadOnlyRoots(isolate).narrow_string_handle();
251   }
252   UNREACHABLE();
253 }
254 
255 }  // namespace
256 
ResolvedOptions(Isolate * isolate,Handle<JSRelativeTimeFormat> format_holder)257 Handle<JSObject> JSRelativeTimeFormat::ResolvedOptions(
258     Isolate* isolate, Handle<JSRelativeTimeFormat> format_holder) {
259   Factory* factory = isolate->factory();
260   icu::RelativeDateTimeFormatter* formatter =
261       format_holder->icu_formatter().raw();
262   DCHECK_NOT_NULL(formatter);
263   Handle<JSObject> result = factory->NewJSObject(isolate->object_function());
264   Handle<String> locale(format_holder->locale(), isolate);
265   Handle<String> numberingSystem(format_holder->numberingSystem(), isolate);
266   JSObject::AddProperty(isolate, result, factory->locale_string(), locale,
267                         NONE);
268   JSObject::AddProperty(
269       isolate, result, factory->style_string(),
270       StyleAsString(isolate, fromIcuStyle(formatter->getFormatStyle())), NONE);
271   JSObject::AddProperty(isolate, result, factory->numeric_string(),
272                         format_holder->NumericAsString(), NONE);
273   JSObject::AddProperty(isolate, result, factory->numberingSystem_string(),
274                         numberingSystem, NONE);
275   return result;
276 }
277 
NumericAsString() const278 Handle<String> JSRelativeTimeFormat::NumericAsString() const {
279   switch (numeric()) {
280     case Numeric::ALWAYS:
281       return GetReadOnlyRoots().always_string_handle();
282     case Numeric::AUTO:
283       return GetReadOnlyRoots().auto_string_handle();
284   }
285   UNREACHABLE();
286 }
287 
288 namespace {
289 
UnitAsString(Isolate * isolate,URelativeDateTimeUnit unit_enum)290 Handle<String> UnitAsString(Isolate* isolate, URelativeDateTimeUnit unit_enum) {
291   Factory* factory = isolate->factory();
292   switch (unit_enum) {
293     case UDAT_REL_UNIT_SECOND:
294       return factory->second_string();
295     case UDAT_REL_UNIT_MINUTE:
296       return factory->minute_string();
297     case UDAT_REL_UNIT_HOUR:
298       return factory->hour_string();
299     case UDAT_REL_UNIT_DAY:
300       return factory->day_string();
301     case UDAT_REL_UNIT_WEEK:
302       return factory->week_string();
303     case UDAT_REL_UNIT_MONTH:
304       return factory->month_string();
305     case UDAT_REL_UNIT_QUARTER:
306       return factory->quarter_string();
307     case UDAT_REL_UNIT_YEAR:
308       return factory->year_string();
309     default:
310       UNREACHABLE();
311   }
312 }
313 
GetURelativeDateTimeUnit(Handle<String> unit,URelativeDateTimeUnit * unit_enum)314 bool GetURelativeDateTimeUnit(Handle<String> unit,
315                               URelativeDateTimeUnit* unit_enum) {
316   std::unique_ptr<char[]> unit_str = unit->ToCString();
317   if ((strcmp("second", unit_str.get()) == 0) ||
318       (strcmp("seconds", unit_str.get()) == 0)) {
319     *unit_enum = UDAT_REL_UNIT_SECOND;
320   } else if ((strcmp("minute", unit_str.get()) == 0) ||
321              (strcmp("minutes", unit_str.get()) == 0)) {
322     *unit_enum = UDAT_REL_UNIT_MINUTE;
323   } else if ((strcmp("hour", unit_str.get()) == 0) ||
324              (strcmp("hours", unit_str.get()) == 0)) {
325     *unit_enum = UDAT_REL_UNIT_HOUR;
326   } else if ((strcmp("day", unit_str.get()) == 0) ||
327              (strcmp("days", unit_str.get()) == 0)) {
328     *unit_enum = UDAT_REL_UNIT_DAY;
329   } else if ((strcmp("week", unit_str.get()) == 0) ||
330              (strcmp("weeks", unit_str.get()) == 0)) {
331     *unit_enum = UDAT_REL_UNIT_WEEK;
332   } else if ((strcmp("month", unit_str.get()) == 0) ||
333              (strcmp("months", unit_str.get()) == 0)) {
334     *unit_enum = UDAT_REL_UNIT_MONTH;
335   } else if ((strcmp("quarter", unit_str.get()) == 0) ||
336              (strcmp("quarters", unit_str.get()) == 0)) {
337     *unit_enum = UDAT_REL_UNIT_QUARTER;
338   } else if ((strcmp("year", unit_str.get()) == 0) ||
339              (strcmp("years", unit_str.get()) == 0)) {
340     *unit_enum = UDAT_REL_UNIT_YEAR;
341   } else {
342     return false;
343   }
344   return true;
345 }
346 
347 template <typename T>
FormatCommon(Isolate * isolate,Handle<JSRelativeTimeFormat> format,Handle<Object> value_obj,Handle<Object> unit_obj,const char * func_name,MaybeHandle<T> (* formatToResult)(Isolate *,const icu::FormattedRelativeDateTime &,Handle<Object>,Handle<String>))348 MaybeHandle<T> FormatCommon(
349     Isolate* isolate, Handle<JSRelativeTimeFormat> format,
350     Handle<Object> value_obj, Handle<Object> unit_obj, const char* func_name,
351     MaybeHandle<T> (*formatToResult)(Isolate*,
352                                      const icu::FormattedRelativeDateTime&,
353                                      Handle<Object>, Handle<String>)) {
354   // 3. Let value be ? ToNumber(value).
355   Handle<Object> value;
356   ASSIGN_RETURN_ON_EXCEPTION(isolate, value,
357                              Object::ToNumber(isolate, value_obj), T);
358   double number = value->Number();
359   // 4. Let unit be ? ToString(unit).
360   Handle<String> unit;
361   ASSIGN_RETURN_ON_EXCEPTION(isolate, unit, Object::ToString(isolate, unit_obj),
362                              T);
363   // 4. If isFinite(value) is false, then throw a RangeError exception.
364   if (!std::isfinite(number)) {
365     THROW_NEW_ERROR(
366         isolate,
367         NewRangeError(MessageTemplate::kNotFiniteNumber,
368                       isolate->factory()->NewStringFromAsciiChecked(func_name)),
369         T);
370   }
371   icu::RelativeDateTimeFormatter* formatter = format->icu_formatter().raw();
372   DCHECK_NOT_NULL(formatter);
373   URelativeDateTimeUnit unit_enum;
374   if (!GetURelativeDateTimeUnit(unit, &unit_enum)) {
375     THROW_NEW_ERROR(
376         isolate,
377         NewRangeError(MessageTemplate::kInvalidUnit,
378                       isolate->factory()->NewStringFromAsciiChecked(func_name),
379                       unit),
380         T);
381   }
382   UErrorCode status = U_ZERO_ERROR;
383   icu::FormattedRelativeDateTime formatted =
384       (format->numeric() == JSRelativeTimeFormat::Numeric::ALWAYS)
385           ? formatter->formatNumericToValue(number, unit_enum, status)
386           : formatter->formatToValue(number, unit_enum, status);
387   if (U_FAILURE(status)) {
388     THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError), T);
389   }
390   return formatToResult(isolate, formatted, value,
391                         UnitAsString(isolate, unit_enum));
392 }
393 
FormatToString(Isolate * isolate,const icu::FormattedRelativeDateTime & formatted,Handle<Object> value,Handle<String> unit)394 MaybeHandle<String> FormatToString(
395     Isolate* isolate, const icu::FormattedRelativeDateTime& formatted,
396     Handle<Object> value, Handle<String> unit) {
397   UErrorCode status = U_ZERO_ERROR;
398   icu::UnicodeString result = formatted.toString(status);
399   if (U_FAILURE(status)) {
400     THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError), String);
401   }
402   return Intl::ToString(isolate, result);
403 }
404 
AddLiteral(Isolate * isolate,Handle<JSArray> array,const icu::UnicodeString & string,int32_t index,int32_t start,int32_t limit)405 Maybe<bool> AddLiteral(Isolate* isolate, Handle<JSArray> array,
406                        const icu::UnicodeString& string, int32_t index,
407                        int32_t start, int32_t limit) {
408   Handle<String> substring;
409   ASSIGN_RETURN_ON_EXCEPTION_VALUE(
410       isolate, substring, Intl::ToString(isolate, string, start, limit),
411       Nothing<bool>());
412   Intl::AddElement(isolate, array, index, isolate->factory()->literal_string(),
413                    substring);
414   return Just(true);
415 }
416 
AddUnit(Isolate * isolate,Handle<JSArray> array,const icu::UnicodeString & string,int32_t index,int32_t start,int32_t limit,int32_t field_id,Handle<Object> value,Handle<String> unit)417 Maybe<bool> AddUnit(Isolate* isolate, Handle<JSArray> array,
418                     const icu::UnicodeString& string, int32_t index,
419                     int32_t start, int32_t limit, int32_t field_id,
420                     Handle<Object> value, Handle<String> unit) {
421   Handle<String> substring;
422   ASSIGN_RETURN_ON_EXCEPTION_VALUE(
423       isolate, substring, Intl::ToString(isolate, string, start, limit),
424       Nothing<bool>());
425   Intl::AddElement(isolate, array, index,
426                    Intl::NumberFieldToType(isolate, value, field_id), substring,
427                    isolate->factory()->unit_string(), unit);
428   return Just(true);
429 }
430 
FormatToJSArray(Isolate * isolate,const icu::FormattedRelativeDateTime & formatted,Handle<Object> value,Handle<String> unit)431 MaybeHandle<JSArray> FormatToJSArray(
432     Isolate* isolate, const icu::FormattedRelativeDateTime& formatted,
433     Handle<Object> value, Handle<String> unit) {
434   UErrorCode status = U_ZERO_ERROR;
435   icu::UnicodeString string = formatted.toString(status);
436 
437   Factory* factory = isolate->factory();
438   Handle<JSArray> array = factory->NewJSArray(0);
439   icu::ConstrainedFieldPosition cfpos;
440   cfpos.constrainCategory(UFIELD_CATEGORY_NUMBER);
441   int32_t index = 0;
442 
443   int32_t previous_end = 0;
444   Handle<String> substring;
445   std::vector<std::pair<int32_t, int32_t>> groups;
446   while (formatted.nextPosition(cfpos, status) && U_SUCCESS(status)) {
447     int32_t category = cfpos.getCategory();
448     int32_t field = cfpos.getField();
449     int32_t start = cfpos.getStart();
450     int32_t limit = cfpos.getLimit();
451     if (category == UFIELD_CATEGORY_NUMBER) {
452       if (field == UNUM_GROUPING_SEPARATOR_FIELD) {
453         groups.push_back(std::pair<int32_t, int32_t>(start, limit));
454         continue;
455       }
456       if (start > previous_end) {
457         Maybe<bool> maybe_added =
458             AddLiteral(isolate, array, string, index++, previous_end, start);
459         MAYBE_RETURN(maybe_added, Handle<JSArray>());
460       }
461       if (field == UNUM_INTEGER_FIELD) {
462         for (auto start_limit : groups) {
463           if (start_limit.first > start) {
464             Maybe<bool> maybe_added =
465                 AddUnit(isolate, array, string, index++, start,
466                         start_limit.first, field, value, unit);
467             MAYBE_RETURN(maybe_added, Handle<JSArray>());
468             maybe_added = AddUnit(isolate, array, string, index++,
469                                   start_limit.first, start_limit.second,
470                                   UNUM_GROUPING_SEPARATOR_FIELD, value, unit);
471             MAYBE_RETURN(maybe_added, Handle<JSArray>());
472             start = start_limit.second;
473           }
474         }
475       }
476       Maybe<bool> maybe_added = AddUnit(isolate, array, string, index++, start,
477                                         limit, field, value, unit);
478       MAYBE_RETURN(maybe_added, Handle<JSArray>());
479       previous_end = limit;
480     }
481   }
482   if (U_FAILURE(status)) {
483     THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError), JSArray);
484   }
485   if (string.length() > previous_end) {
486     Maybe<bool> maybe_added = AddLiteral(isolate, array, string, index,
487                                          previous_end, string.length());
488     MAYBE_RETURN(maybe_added, Handle<JSArray>());
489   }
490 
491   JSObject::ValidateElements(*array);
492   return array;
493 }
494 
495 }  // namespace
496 
Format(Isolate * isolate,Handle<Object> value_obj,Handle<Object> unit_obj,Handle<JSRelativeTimeFormat> format)497 MaybeHandle<String> JSRelativeTimeFormat::Format(
498     Isolate* isolate, Handle<Object> value_obj, Handle<Object> unit_obj,
499     Handle<JSRelativeTimeFormat> format) {
500   return FormatCommon<String>(isolate, format, value_obj, unit_obj,
501                               "Intl.RelativeTimeFormat.prototype.format",
502                               FormatToString);
503 }
504 
FormatToParts(Isolate * isolate,Handle<Object> value_obj,Handle<Object> unit_obj,Handle<JSRelativeTimeFormat> format)505 MaybeHandle<JSArray> JSRelativeTimeFormat::FormatToParts(
506     Isolate* isolate, Handle<Object> value_obj, Handle<Object> unit_obj,
507     Handle<JSRelativeTimeFormat> format) {
508   return FormatCommon<JSArray>(
509       isolate, format, value_obj, unit_obj,
510       "Intl.RelativeTimeFormat.prototype.formatToParts", FormatToJSArray);
511 }
512 
GetAvailableLocales()513 const std::set<std::string>& JSRelativeTimeFormat::GetAvailableLocales() {
514   // Since RelativeTimeFormatter does not have a method to list all
515   // available locales, work around by calling the DateFormat.
516   return Intl::GetAvailableLocalesForDateFormat();
517 }
518 
519 }  // namespace internal
520 }  // namespace v8
521