5 #ifndef V8_INTL_SUPPORT 6 #error Internationalization is expected to be enabled. 7 #endif // V8_INTL_SUPPORT 9 #include "src/objects/js-relative-time-format.h" 15 #include "src/heap/factory.h" 16 #include "src/isolate.h" 17 #include "src/objects-inl.h" 18 #include "src/objects/intl-objects.h" 19 #include "src/objects/js-number-format.h" 20 #include "src/objects/js-relative-time-format-inl.h" 21 #include "unicode/datefmt.h" 22 #include "unicode/numfmt.h" 23 #include "unicode/reldatefmt.h" 29 UDateRelativeDateTimeFormatterStyle getIcuStyle(
30 JSRelativeTimeFormat::Style style) {
32 case JSRelativeTimeFormat::Style::LONG:
33 return UDAT_STYLE_LONG;
34 case JSRelativeTimeFormat::Style::SHORT:
35 return UDAT_STYLE_SHORT;
36 case JSRelativeTimeFormat::Style::NARROW:
37 return UDAT_STYLE_NARROW;
38 case JSRelativeTimeFormat::Style::COUNT:
44 JSRelativeTimeFormat::Style JSRelativeTimeFormat::getStyle(
const char* str) {
45 if (strcmp(str,
"long") == 0)
return JSRelativeTimeFormat::Style::LONG;
46 if (strcmp(str,
"short") == 0)
return JSRelativeTimeFormat::Style::SHORT;
47 if (strcmp(str,
"narrow") == 0)
return JSRelativeTimeFormat::Style::NARROW;
51 JSRelativeTimeFormat::Numeric JSRelativeTimeFormat::getNumeric(
53 if (strcmp(str,
"auto") == 0)
return JSRelativeTimeFormat::Numeric::AUTO;
54 if (strcmp(str,
"always") == 0)
return JSRelativeTimeFormat::Numeric::ALWAYS;
58 MaybeHandle<JSRelativeTimeFormat> JSRelativeTimeFormat::Initialize(
59 Isolate* isolate, Handle<JSRelativeTimeFormat> relative_time_format_holder,
60 Handle<Object> locales, Handle<Object> input_options) {
61 relative_time_format_holder->set_flags(0);
64 Maybe<std::vector<std::string>> maybe_requested_locales =
65 Intl::CanonicalizeLocaleList(isolate, locales);
66 MAYBE_RETURN(maybe_requested_locales, Handle<JSRelativeTimeFormat>());
67 std::vector<std::string> requested_locales =
68 maybe_requested_locales.FromJust();
71 Handle<JSReceiver> options;
72 if (input_options->IsUndefined(isolate)) {
74 options = isolate->factory()->NewJSObjectWithNullProto();
78 ASSIGN_RETURN_ON_EXCEPTION(isolate, options,
79 Object::ToObject(isolate, input_options),
80 JSRelativeTimeFormat);
87 Maybe<Intl::MatcherOption> maybe_locale_matcher =
88 Intl::GetLocaleMatcher(isolate, options,
"Intl.RelativeTimeFormat");
89 MAYBE_RETURN(maybe_locale_matcher, MaybeHandle<JSRelativeTimeFormat>());
90 Intl::MatcherOption matcher = maybe_locale_matcher.FromJust();
97 Intl::ResolvedLocale r =
98 Intl::ResolveLocale(isolate, JSRelativeTimeFormat::GetAvailableLocales(),
99 requested_locales, matcher, {});
104 Handle<String> locale_str =
105 isolate->factory()->NewStringFromAsciiChecked(r.locale.c_str());
106 relative_time_format_holder->set_locale(*locale_str);
110 Maybe<Style> maybe_style = Intl::GetStringOption<Style>(
111 isolate, options,
"style",
"Intl.RelativeTimeFormat",
112 {
"long",
"short",
"narrow"}, {Style::LONG, Style::SHORT, Style::NARROW},
114 MAYBE_RETURN(maybe_style, MaybeHandle<JSRelativeTimeFormat>());
115 Style style_enum = maybe_style.FromJust();
118 relative_time_format_holder->set_style(style_enum);
122 Maybe<Numeric> maybe_numeric = Intl::GetStringOption<Numeric>(
123 isolate, options,
"numeric",
"Intl.RelativeTimeFormat",
124 {
"always",
"auto"}, {Numeric::ALWAYS, Numeric::AUTO}, Numeric::ALWAYS);
125 MAYBE_RETURN(maybe_numeric, MaybeHandle<JSRelativeTimeFormat>());
126 Numeric numeric_enum = maybe_numeric.FromJust();
129 relative_time_format_holder->set_numeric(numeric_enum);
131 icu::Locale icu_locale = r.icu_locale;
132 UErrorCode status = U_ZERO_ERROR;
136 icu::NumberFormat* number_format =
137 icu::NumberFormat::createInstance(icu_locale, UNUM_DECIMAL, status);
138 if (U_FAILURE(status)) {
139 delete number_format;
140 FATAL(
"Failed to create ICU number format, are ICU data files missing?");
142 CHECK_NOT_NULL(number_format);
147 icu::RelativeDateTimeFormatter* icu_formatter =
148 new icu::RelativeDateTimeFormatter(icu_locale, number_format,
149 getIcuStyle(style_enum),
150 UDISPCTX_CAPITALIZATION_NONE, status);
151 if (U_FAILURE(status)) {
152 delete icu_formatter;
154 "Failed to create ICU relative date time formatter, are ICU data files " 157 CHECK_NOT_NULL(icu_formatter);
159 Handle<Managed<icu::RelativeDateTimeFormatter>> managed_formatter =
160 Managed<icu::RelativeDateTimeFormatter>::FromRawPtr(isolate, 0,
164 relative_time_format_holder->set_icu_formatter(*managed_formatter);
167 return relative_time_format_holder;
170 Handle<JSObject> JSRelativeTimeFormat::ResolvedOptions(
171 Isolate* isolate, Handle<JSRelativeTimeFormat> format_holder) {
172 Factory* factory = isolate->factory();
173 Handle<JSObject> result = factory->NewJSObject(isolate->object_function());
174 Handle<String> locale(format_holder->locale(), isolate);
175 JSObject::AddProperty(isolate, result, factory->locale_string(), locale,
177 JSObject::AddProperty(isolate, result, factory->style_string(),
178 format_holder->StyleAsString(), NONE);
179 JSObject::AddProperty(isolate, result, factory->numeric_string(),
180 format_holder->NumericAsString(), NONE);
184 Handle<String> JSRelativeTimeFormat::StyleAsString()
const {
187 return GetReadOnlyRoots().long_string_handle();
189 return GetReadOnlyRoots().short_string_handle();
191 return GetReadOnlyRoots().narrow_string_handle();
197 Handle<String> JSRelativeTimeFormat::NumericAsString()
const {
199 case Numeric::ALWAYS:
200 return GetReadOnlyRoots().always_string_handle();
202 return GetReadOnlyRoots().auto_string_handle();
210 Handle<String> UnitAsString(Isolate* isolate, URelativeDateTimeUnit unit_enum) {
211 Factory* factory = isolate->factory();
213 case UDAT_REL_UNIT_SECOND:
214 return factory->second_string();
215 case UDAT_REL_UNIT_MINUTE:
216 return factory->minute_string();
217 case UDAT_REL_UNIT_HOUR:
218 return factory->hour_string();
219 case UDAT_REL_UNIT_DAY:
220 return factory->day_string();
221 case UDAT_REL_UNIT_WEEK:
222 return factory->week_string();
223 case UDAT_REL_UNIT_MONTH:
224 return factory->month_string();
225 case UDAT_REL_UNIT_QUARTER:
226 return factory->quarter_string();
227 case UDAT_REL_UNIT_YEAR:
228 return factory->year_string();
234 MaybeHandle<JSArray> GenerateRelativeTimeFormatParts(
235 Isolate* isolate,
const icu::UnicodeString& formatted,
236 const icu::UnicodeString& integer_part, URelativeDateTimeUnit unit_enum,
237 double number,
const icu::NumberFormat& nf) {
238 Factory* factory = isolate->factory();
239 Handle<JSArray> array = factory->NewJSArray(0);
240 int32_t found = formatted.indexOf(integer_part);
242 Handle<String> substring;
246 ASSIGN_RETURN_ON_EXCEPTION(isolate, substring,
247 Intl::ToString(isolate, formatted), JSArray);
248 Intl::AddElement(isolate, array,
250 factory->literal_string(),
260 ASSIGN_RETURN_ON_EXCEPTION(isolate, substring,
261 Intl::ToString(isolate, formatted, 0, found),
263 Intl::AddElement(isolate, array, index++,
264 factory->literal_string(),
268 Handle<String> unit = UnitAsString(isolate, unit_enum);
270 Maybe<int> maybe_format_to_parts =
271 JSNumberFormat::FormatToParts(isolate, array, index, nf, number, unit);
272 MAYBE_RETURN(maybe_format_to_parts, Handle<JSArray>());
273 index = maybe_format_to_parts.FromJust();
279 if (found + integer_part.length() < formatted.length()) {
280 ASSIGN_RETURN_ON_EXCEPTION(
282 Intl::ToString(isolate, formatted, found + integer_part.length(),
285 Intl::AddElement(isolate, array, index,
286 factory->literal_string(),
293 bool GetURelativeDateTimeUnit(Handle<String> unit,
294 URelativeDateTimeUnit* unit_enum) {
295 std::unique_ptr<char[]> unit_str = unit->ToCString();
296 if ((strcmp(
"second", unit_str.get()) == 0) ||
297 (strcmp(
"seconds", unit_str.get()) == 0)) {
298 *unit_enum = UDAT_REL_UNIT_SECOND;
299 }
else if ((strcmp(
"minute", unit_str.get()) == 0) ||
300 (strcmp(
"minutes", unit_str.get()) == 0)) {
301 *unit_enum = UDAT_REL_UNIT_MINUTE;
302 }
else if ((strcmp(
"hour", unit_str.get()) == 0) ||
303 (strcmp(
"hours", unit_str.get()) == 0)) {
304 *unit_enum = UDAT_REL_UNIT_HOUR;
305 }
else if ((strcmp(
"day", unit_str.get()) == 0) ||
306 (strcmp(
"days", unit_str.get()) == 0)) {
307 *unit_enum = UDAT_REL_UNIT_DAY;
308 }
else if ((strcmp(
"week", unit_str.get()) == 0) ||
309 (strcmp(
"weeks", unit_str.get()) == 0)) {
310 *unit_enum = UDAT_REL_UNIT_WEEK;
311 }
else if ((strcmp(
"month", unit_str.get()) == 0) ||
312 (strcmp(
"months", unit_str.get()) == 0)) {
313 *unit_enum = UDAT_REL_UNIT_MONTH;
314 }
else if ((strcmp(
"quarter", unit_str.get()) == 0) ||
315 (strcmp(
"quarters", unit_str.get()) == 0)) {
316 *unit_enum = UDAT_REL_UNIT_QUARTER;
317 }
else if ((strcmp(
"year", unit_str.get()) == 0) ||
318 (strcmp(
"years", unit_str.get()) == 0)) {
319 *unit_enum = UDAT_REL_UNIT_YEAR;
328 MaybeHandle<Object> JSRelativeTimeFormat::Format(
329 Isolate* isolate, Handle<Object> value_obj, Handle<Object> unit_obj,
330 Handle<JSRelativeTimeFormat> format_holder,
const char* func_name,
332 Factory* factory = isolate->factory();
335 Handle<Object> value;
336 ASSIGN_RETURN_ON_EXCEPTION(isolate, value,
337 Object::ToNumber(isolate, value_obj), Object);
338 double number = value->Number();
341 ASSIGN_RETURN_ON_EXCEPTION(isolate, unit, Object::ToString(isolate, unit_obj),
345 if (!std::isfinite(number)) {
348 NewRangeError(MessageTemplate::kNotFiniteNumber,
349 isolate->factory()->NewStringFromAsciiChecked(func_name)),
353 icu::RelativeDateTimeFormatter* formatter =
354 format_holder->icu_formatter()->raw();
355 CHECK_NOT_NULL(formatter);
357 URelativeDateTimeUnit unit_enum;
358 if (!GetURelativeDateTimeUnit(unit, &unit_enum)) {
361 NewRangeError(MessageTemplate::kInvalidUnit,
362 isolate->factory()->NewStringFromAsciiChecked(func_name),
367 UErrorCode status = U_ZERO_ERROR;
368 icu::UnicodeString formatted;
370 if (format_holder->numeric() == JSRelativeTimeFormat::Numeric::ALWAYS) {
371 formatter->formatNumeric(number, unit_enum, formatted, status);
373 DCHECK_EQ(JSRelativeTimeFormat::Numeric::AUTO, format_holder->numeric());
374 formatter->format(number, unit_enum, formatted, status);
377 if (U_FAILURE(status)) {
378 THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError), Object);
382 icu::UnicodeString number_str;
383 icu::FieldPosition pos;
384 double abs_number = std::abs(number);
385 formatter->getNumberFormat().format(abs_number, number_str, pos, status);
386 if (U_FAILURE(status)) {
387 THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError),
391 Handle<JSArray> elements;
392 ASSIGN_RETURN_ON_EXCEPTION(isolate, elements,
393 GenerateRelativeTimeFormatParts(
394 isolate, formatted, number_str, unit_enum,
395 abs_number, formatter->getNumberFormat()),
400 return factory->NewStringFromTwoByte(Vector<const uint16_t>(
401 reinterpret_cast<const uint16_t*>(formatted.getBuffer()),
402 formatted.length()));
405 std::set<std::string> JSRelativeTimeFormat::GetAvailableLocales() {
406 int32_t num_locales = 0;
407 const icu::Locale* icu_available_locales =
408 icu::DateFormat::getAvailableLocales(num_locales);
409 return Intl::BuildLocaleSet(icu_available_locales, num_locales);