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