6#error Internationalization is expected to be enabled.
23#include "unicode/decimfmt.h"
24#include "unicode/numfmt.h"
25#include "unicode/reldatefmt.h"
26#include "unicode/unum.h"
42UDateRelativeDateTimeFormatterStyle toIcuStyle(Style
style) {
45 return UDAT_STYLE_LONG;
47 return UDAT_STYLE_SHORT;
49 return UDAT_STYLE_NARROW;
54Style fromIcuStyle(UDateRelativeDateTimeFormatterStyle icu_style) {
58 case UDAT_STYLE_SHORT:
60 case UDAT_STYLE_NARROW:
62 case UDAT_STYLE_COUNT:
76 std::vector<std::string> requested_locales =
81 const char* service =
"Intl.RelativeTimeFormat";
96 std::unique_ptr<char[]> numbering_system_str =
nullptr;
98 isolate, options, service, &numbering_system_str);
115 requested_locales, matcher, {
"nu"});
121 UErrorCode status = U_ZERO_ERROR;
123 icu::Locale icu_locale =
r.icu_locale;
124 if (numbering_system_str !=
nullptr) {
125 auto nu_extension_it =
r.extensions.find(
"nu");
126 if (nu_extension_it !=
r.extensions.end() &&
127 nu_extension_it->second != numbering_system_str.get()) {
128 icu_locale.setUnicodeKeywordValue(
"nu",
nullptr, status);
129 DCHECK(U_SUCCESS(status));
138 isolate->factory()->NewStringFromAsciiChecked(
139 maybe_locale_str.
FromJust().c_str());
142 if (numbering_system_str !=
nullptr &&
144 icu_locale.setUnicodeKeywordValue(
"nu", numbering_system_str.get(), status);
145 DCHECK(U_SUCCESS(status));
152 isolate, options,
"style", service, {
"long",
"short",
"narrow"},
153 {Style::LONG, Style::SHORT, Style::NARROW}, Style::LONG);
155 Style style_enum = maybe_style.
FromJust();
162 isolate, options,
"numeric", service, {
"always",
"auto"},
163 {Numeric::ALWAYS, Numeric::AUTO}, Numeric::ALWAYS);
171 icu::NumberFormat* number_format =
172 icu::NumberFormat::createInstance(icu_locale, UNUM_DECIMAL, status);
173 if (U_FAILURE(status)) {
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));
184 icu::NumberFormat::createInstance(icu_locale, UNUM_DECIMAL, status);
186 if (U_FAILURE(status) || number_format ==
nullptr) {
187 delete number_format;
192 if (number_format->getDynamicClassID() ==
193 icu::DecimalFormat::getStaticClassID()) {
194 icu::DecimalFormat* decimal_format =
195 static_cast<icu::DecimalFormat*
>(number_format);
196 decimal_format->setMinimumGroupingDigits(-2);
202 std::shared_ptr<icu::RelativeDateTimeFormatter> icu_formatter =
203 std::make_shared<icu::RelativeDateTimeFormatter>(
204 icu_locale, number_format, toIcuStyle(style_enum),
205 UDISPCTX_CAPITALIZATION_NONE, status);
206 if (U_FAILURE(status) || icu_formatter ==
nullptr) {
211 isolate->factory()->NewStringFromAsciiChecked(
216 std::move(icu_formatter));
220 isolate->factory()->NewFastOrSlowJSObjectFromMap(map));
223 relative_time_format_holder->set_flags(0);
224 relative_time_format_holder->set_locale(*locale_str);
225 relative_time_format_holder->set_numberingSystem(*numbering_system_string);
226 relative_time_format_holder->set_numeric(numeric_enum);
227 relative_time_format_holder->set_icu_formatter(*managed_formatter);
230 return relative_time_format_holder;
238 return isolate->factory()->long_string();
240 return isolate->factory()->short_string();
242 return isolate->factory()->narrow_string();
251 Factory* factory = isolate->factory();
252 icu::RelativeDateTimeFormatter* formatter =
253 format_holder->icu_formatter()->raw();
263 isolate,
result, factory->style_string(),
264 StyleAsString(isolate, fromIcuStyle(formatter->getFormatStyle())),
NONE);
266 format_holder->NumericAsString(isolate),
NONE);
268 numberingSystem,
NONE);
274 case Numeric::ALWAYS:
275 return isolate->factory()->always_string();
277 return isolate->factory()->auto_string();
285 URelativeDateTimeUnit unit_enum) {
286 Factory* factory = isolate->factory();
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();
309bool GetURelativeDateTimeUnit(DirectHandle<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;
343MaybeDirectHandle<T> FormatCommon(
344 Isolate* isolate, DirectHandle<JSRelativeTimeFormat> format,
345 Handle<Object> value_obj, Handle<Object> unit_obj,
const char* func_name,
346 MaybeDirectHandle<T> (*formatToResult)(
347 Isolate*,
const icu::FormattedRelativeDateTime&, DirectHandle<String>,
350 DirectHandle<Object>
value;
359 if (!std::isfinite(number)) {
361 isolate, NewRangeError(
362 MessageTemplate::kNotFiniteNumber,
363 isolate->factory()->NewStringFromAsciiChecked(func_name)));
365 icu::RelativeDateTimeFormatter* formatter = format->icu_formatter()->raw();
367 URelativeDateTimeUnit unit_enum;
368 if (!GetURelativeDateTimeUnit(
unit, &unit_enum)) {
371 NewRangeError(MessageTemplate::kInvalidUnit,
372 isolate->factory()->NewStringFromAsciiChecked(func_name),
375 UErrorCode status = U_ZERO_ERROR;
376 icu::FormattedRelativeDateTime
formatted =
377 (format->numeric() == JSRelativeTimeFormat::Numeric::ALWAYS)
378 ? formatter->formatNumericToValue(number, unit_enum, status)
379 : formatter->formatToValue(number, unit_enum, status);
380 if (U_FAILURE(status)) {
383 return formatToResult(isolate,
formatted, UnitAsString(isolate, unit_enum),
387MaybeDirectHandle<String> FormatToString(
388 Isolate* isolate,
const icu::FormattedRelativeDateTime&
formatted,
389 DirectHandle<String>
unit,
bool is_nan) {
390 UErrorCode status = U_ZERO_ERROR;
392 if (U_FAILURE(status)) {
398Maybe<bool> AddLiteral(Isolate* isolate, DirectHandle<JSArray> array,
399 const icu::UnicodeString&
string, int32_t index,
400 int32_t
start, int32_t limit) {
405 Intl::AddElement(isolate, array, index, isolate->factory()->literal_string(),
410Maybe<bool> AddUnit(Isolate* isolate, DirectHandle<JSArray> array,
411 const icu::UnicodeString&
string, int32_t index,
412 const NumberFormatSpan& part, DirectHandle<String>
unit,
421 substring, isolate->factory()->unit_string(),
unit);
425MaybeDirectHandle<JSArray> FormatToJSArray(
426 Isolate* isolate,
const icu::FormattedRelativeDateTime&
formatted,
427 DirectHandle<String>
unit,
bool is_nan) {
428 UErrorCode status = U_ZERO_ERROR;
429 icu::UnicodeString
string =
formatted.toString(status);
431 Factory* factory = isolate->factory();
432 DirectHandle<JSArray> array = factory->
NewJSArray(0);
433 icu::ConstrainedFieldPosition cfpos;
434 cfpos.constrainCategory(UFIELD_CATEGORY_NUMBER);
439 std::vector<std::pair<int32_t, int32_t>> groups;
440 while (
formatted.nextPosition(cfpos, status) && U_SUCCESS(status)) {
441 int32_t category = cfpos.getCategory();
442 int32_t field = cfpos.getField();
444 int32_t limit = cfpos.getLimit();
445 if (category == UFIELD_CATEGORY_NUMBER) {
446 if (field == UNUM_GROUPING_SEPARATOR_FIELD) {
447 groups.push_back(std::pair<int32_t, int32_t>(
start, limit));
450 if (
start > previous_end) {
451 Maybe<bool> maybe_added =
452 AddLiteral(isolate, array,
string, index++, previous_end,
start);
455 if (field == UNUM_INTEGER_FIELD) {
456 for (
auto start_limit : groups) {
457 if (start_limit.first >
start) {
458 Maybe<bool> maybe_added =
459 AddUnit(isolate, array,
string, index++,
460 NumberFormatSpan(field,
start, start_limit.first),
unit,
464 AddUnit(isolate, array,
string, index++,
465 NumberFormatSpan(UNUM_GROUPING_SEPARATOR_FIELD,
466 start_limit.first, start_limit.second),
469 start = start_limit.second;
473 Maybe<bool> maybe_added =
474 AddUnit(isolate, array,
string, index++,
475 NumberFormatSpan(field,
start, limit),
unit, is_nan);
477 previous_end = limit;
480 if (U_FAILURE(status)) {
483 if (
string.
length() > previous_end) {
484 Maybe<bool> maybe_added = AddLiteral(isolate, array,
string, index,
485 previous_end,
string.
length());
498 return FormatCommon<String>(isolate, format, value_obj, unit_obj,
499 "Intl.RelativeTimeFormat.prototype.format",
506 return FormatCommon<JSArray>(
507 isolate, format, value_obj, unit_obj,
508 "Intl.RelativeTimeFormat.prototype.formatToParts", FormatToJSArray);
V8_INLINE T FromJust() const &
V8_INLINE bool IsNothing() const
Handle< JSArray > NewJSArray(ElementsKind elements_kind, int length, int capacity, ArrayStorageAllocationMode mode=ArrayStorageAllocationMode::DONT_INITIALIZE_ARRAY_ELEMENTS, AllocationType allocation=AllocationType::kYoung)
Handle< JSObject > NewJSObject(DirectHandle< JSFunction > constructor, AllocationType allocation=AllocationType::kYoung, NewJSObjectType=NewJSObjectType::kNoAPIWrapper)
static void AddElement(Isolate *isolate, DirectHandle< JSArray > array, int index, DirectHandle< String > field_type_string, DirectHandle< String > value)
static std::string GetNumberingSystem(const icu::Locale &icu_locale)
static V8_WARN_UNUSED_RESULT MaybeHandle< String > ToString(Isolate *isolate, const icu::UnicodeString &string)
static Maybe< std::vector< std::string > > CanonicalizeLocaleList(Isolate *isolate, DirectHandle< Object > locales, bool only_return_one_result=false)
static const std::set< std::string > & GetAvailableLocalesForDateFormat()
static Maybe< ResolvedLocale > ResolveLocale(Isolate *isolate, const std::set< std::string > &available_locales, const std::vector< std::string > &requested_locales, MatcherOption options, const std::set< std::string > &relevant_extension_keys)
static V8_WARN_UNUSED_RESULT Maybe< MatcherOption > GetLocaleMatcher(Isolate *isolate, DirectHandle< JSReceiver > options, const char *method_name)
static Maybe< std::string > ToLanguageTag(const icu::Locale &locale)
static bool IsValidNumberingSystem(const std::string &value)
static DirectHandle< String > NumberFieldToType(Isolate *isolate, const NumberFormatSpan &part, const icu::UnicodeString &text, bool is_nan)
static V8_EXPORT_PRIVATE void AddProperty(Isolate *isolate, DirectHandle< JSObject > object, DirectHandle< Name > name, DirectHandle< Object > value, PropertyAttributes attributes)
static void ValidateElements(Tagged< JSObject > object)
static DirectHandle< Managed< CppType > > From(Isolate *isolate, size_t estimated_size, std::shared_ptr< CppType > shared_ptr, AllocationType allocation_type=AllocationType::kYoung)
static V8_WARN_UNUSED_RESULT HandleType< String >::MaybeType ToString(Isolate *isolate, HandleType< T > input)
static V8_WARN_UNUSED_RESULT HandleType< Number >::MaybeType ToNumber(Isolate *isolate, HandleType< T > input)
static double NumberValue(Tagged< Number > obj)
#define ASSIGN_RETURN_ON_EXCEPTION(isolate, dst, call)
#define THROW_NEW_ERROR(isolate, call)
#define ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, dst, call, value)
#define MAYBE_RETURN(call, value)
DirectHandle< JSReceiver > options
ZoneVector< RpoNumber > & result
bool IsNaN(Tagged< Object > obj)
MaybeDirectHandle< JSReceiver > CoerceOptionsToObject(Isolate *isolate, DirectHandle< Object > options, const char *method_name)
Maybe< bool > GetStringOption(Isolate *isolate, DirectHandle< JSReceiver > options, const char *property, const std::vector< const char * > &values, const char *method_name, std::unique_ptr< char[]> *result)
Tagged< To > Cast(Tagged< From > value, const v8::SourceLocation &loc=INIT_SOURCE_LOCATION_IN_DEBUG)
Maybe< T > Just(const T &t)
#define DCHECK_NOT_NULL(val)
#define DCHECK(condition)