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