* Copyright (c) 2021-2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ecmascript/js_relative_time_format.h"
#include "ecmascript/js_function.h"
#include "ecmascript/object_factory-inl.h"
namespace panda::ecmascript {
JSHandle<JSRelativeTimeFormat> JSRelativeTimeFormat::InitializeRelativeTimeFormat(
JSThread *thread, const JSHandle<JSRelativeTimeFormat> &relativeTimeFormat, const JSHandle<JSTaggedValue> &locales,
const JSHandle<JSTaggedValue> &options)
{
EcmaVM *ecmaVm = thread->GetEcmaVM();
ObjectFactory *factory = ecmaVm->GetFactory();
JSHandle<TaggedArray> requestedLocales = intl::LocaleHelper::CanonicalizeLocaleList(thread, locales);
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSRelativeTimeFormat, thread);
JSHandle<JSObject> rtfOptions;
if (!options->IsUndefined()) {
rtfOptions = JSTaggedValue::ToObject(thread, options);
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSRelativeTimeFormat, thread);
} else {
rtfOptions = factory->CreateNullJSObject();
}
auto globalConst = thread->GlobalConstants();
LocaleMatcherOption matcher =
JSLocale::GetOptionOfString(thread, rtfOptions, globalConst->GetHandledLocaleMatcherString(),
{LocaleMatcherOption::LOOKUP, LocaleMatcherOption::BEST_FIT},
{"lookup", "best fit"}, LocaleMatcherOption::BEST_FIT);
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSRelativeTimeFormat, thread);
JSHandle<JSTaggedValue> property = globalConst->GetHandledNumberingSystemString();
JSHandle<JSTaggedValue> undefinedValue(thread, JSTaggedValue::Undefined());
JSHandle<JSTaggedValue> numberingSystemValue =
JSLocale::GetOption(thread, rtfOptions, property, OptionType::STRING, undefinedValue, undefinedValue);
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSRelativeTimeFormat, thread);
std::string numberingSystemStdStr;
if (!numberingSystemValue->IsUndefined()) {
JSHandle<EcmaString> numberingSystemString = JSHandle<EcmaString>::Cast(numberingSystemValue);
if (EcmaStringAccessor(numberingSystemString).IsUtf16()) {
THROW_RANGE_ERROR_AND_RETURN(thread, "invalid numberingSystem", relativeTimeFormat);
}
numberingSystemStdStr = intl::LocaleHelper::ConvertToStdString(thread, numberingSystemString);
if (!JSLocale::IsNormativeNumberingSystem(numberingSystemStdStr)) {
THROW_RANGE_ERROR_AND_RETURN(thread, "invalid numberingSystem", relativeTimeFormat);
}
}
JSHandle<TaggedArray> availableLocales;
if (requestedLocales->GetLength() == 0) {
availableLocales = factory->EmptyArray();
} else {
std::vector<std::string> availableStringLocales =
intl::LocaleHelper::GetAvailableLocales(thread, "calendar", nullptr);
availableLocales = JSLocale::ConstructLocaleList(thread, availableStringLocales);
}
std::set<std::string> relevantExtensionKeys{"nu"};
ResolvedLocale r =
JSLocale::ResolveLocale(thread, availableLocales, requestedLocales, matcher, relevantExtensionKeys);
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSRelativeTimeFormat, thread);
icu::Locale icuLocale = r.localeData;
JSHandle<EcmaString> localeStr = intl::LocaleHelper::ToLanguageTag(thread, icuLocale);
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSRelativeTimeFormat, thread);
relativeTimeFormat->SetLocale(thread, localeStr.GetTaggedValue());
UErrorCode status = U_ZERO_ERROR;
if (!numberingSystemStdStr.empty()) {
if (JSLocale::IsWellNumberingSystem(numberingSystemStdStr)) {
icuLocale.setUnicodeKeywordValue("nu", numberingSystemStdStr, status);
ASSERT(U_SUCCESS(status));
}
}
property = globalConst->GetHandledStyleString();
RelativeStyleOption styleOption = JSLocale::GetOptionOfString(thread, rtfOptions, property,
{RelativeStyleOption::LONG, RelativeStyleOption::SHORT, RelativeStyleOption::NARROW},
{"long", "short", "narrow"}, RelativeStyleOption::LONG);
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSRelativeTimeFormat, thread);
relativeTimeFormat->SetStyle(styleOption);
property = globalConst->GetHandledNumericString();
NumericOption numericOption =
JSLocale::GetOptionOfString(thread, rtfOptions, property, {NumericOption::ALWAYS, NumericOption::AUTO},
{"always", "auto"}, NumericOption::ALWAYS);
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSRelativeTimeFormat, thread);
relativeTimeFormat->SetNumeric(numericOption);
icu::NumberFormat *icuNumberFormat = icu::NumberFormat::createInstance(icuLocale, UNUM_DECIMAL, status);
if (U_FAILURE(status) != 0) {
delete icuNumberFormat;
if (status == UErrorCode::U_MISSING_RESOURCE_ERROR) {
THROW_REFERENCE_ERROR_AND_RETURN(thread, "can not find icu data resources", relativeTimeFormat);
}
THROW_RANGE_ERROR_AND_RETURN(thread, "create icu::NumberFormat failed", relativeTimeFormat);
}
if (icuNumberFormat->getDynamicClassID() == icu::DecimalFormat::getStaticClassID()) {
icu::DecimalFormat* icuDecimalFormat = static_cast<icu::DecimalFormat*>(icuNumberFormat);
icuDecimalFormat->setMinimumGroupingDigits(UNUM_MINIMUM_GROUPING_DIGITS_AUTO);
}
UDateRelativeDateTimeFormatterStyle uStyle;
switch (styleOption) {
case RelativeStyleOption::LONG:
uStyle = UDAT_STYLE_LONG;
break;
case RelativeStyleOption::SHORT:
uStyle = UDAT_STYLE_SHORT;
break;
case RelativeStyleOption::NARROW:
uStyle = UDAT_STYLE_NARROW;
break;
default:
LOG_ECMA(FATAL) << "this branch is unreachable";
UNREACHABLE();
}
icu::RelativeDateTimeFormatter rtfFormatter(icuLocale, icuNumberFormat, uStyle, UDISPCTX_CAPITALIZATION_NONE,
status);
if (U_FAILURE(status) != 0) {
THROW_RANGE_ERROR_AND_RETURN(thread, "icu Formatter Error", relativeTimeFormat);
}
std::string numberingSystem = JSLocale::GetNumberingSystem(icuLocale);
auto result = factory->NewFromStdString(numberingSystem);
relativeTimeFormat->SetNumberingSystem(thread, result);
factory->NewJSIntlIcuData(relativeTimeFormat, rtfFormatter, JSRelativeTimeFormat::FreeIcuRTFFormatter);
return relativeTimeFormat;
}
bool SingularUnitToIcuUnit(JSThread *thread, const JSHandle<EcmaString> &unit, URelativeDateTimeUnit *unitEnum)
{
ASSERT(JSHandle<JSTaggedValue>::Cast(unit)->IsString());
auto globalConst = thread->GlobalConstants();
JSHandle<EcmaString> second = JSHandle<EcmaString>::Cast(globalConst->GetHandledSecondString());
JSHandle<EcmaString> minute = JSHandle<EcmaString>::Cast(globalConst->GetHandledMinuteString());
JSHandle<EcmaString> hour = JSHandle<EcmaString>::Cast(globalConst->GetHandledHourString());
JSHandle<EcmaString> day = JSHandle<EcmaString>::Cast(globalConst->GetHandledDayString());
JSHandle<EcmaString> week = JSHandle<EcmaString>::Cast(globalConst->GetHandledWeekString());
JSHandle<EcmaString> month = JSHandle<EcmaString>::Cast(globalConst->GetHandledMonthString());
JSHandle<EcmaString> quarter = JSHandle<EcmaString>::Cast(globalConst->GetHandledQuarterString());
JSHandle<EcmaString> year = JSHandle<EcmaString>::Cast(globalConst->GetHandledYearString());
JSHandle<EcmaString> seconds = JSHandle<EcmaString>::Cast(globalConst->GetHandledSecondsString());
JSHandle<EcmaString> minutes = JSHandle<EcmaString>::Cast(globalConst->GetHandledMinutesString());
JSHandle<EcmaString> hours = JSHandle<EcmaString>::Cast(globalConst->GetHandledHoursString());
JSHandle<EcmaString> days = JSHandle<EcmaString>::Cast(globalConst->GetHandledDaysString());
JSHandle<EcmaString> weeks = JSHandle<EcmaString>::Cast(globalConst->GetHandledWeeksString());
JSHandle<EcmaString> months = JSHandle<EcmaString>::Cast(globalConst->GetHandledMonthsString());
JSHandle<EcmaString> quarters = JSHandle<EcmaString>::Cast(globalConst->GetHandledQuartersString());
JSHandle<EcmaString> years = JSHandle<EcmaString>::Cast(globalConst->GetHandledYearsString());
if (EcmaStringAccessor::StringsAreEqual(thread, *second, *unit) ||
EcmaStringAccessor::StringsAreEqual(thread, *seconds, *unit)) {
*unitEnum = UDAT_REL_UNIT_SECOND;
} else if (EcmaStringAccessor::StringsAreEqual(thread, *minute, *unit) ||
EcmaStringAccessor::StringsAreEqual(thread, *minutes, *unit)) {
*unitEnum = UDAT_REL_UNIT_MINUTE;
} else if (EcmaStringAccessor::StringsAreEqual(thread, *hour, *unit) ||
EcmaStringAccessor::StringsAreEqual(thread, *hours, *unit)) {
*unitEnum = UDAT_REL_UNIT_HOUR;
} else if (EcmaStringAccessor::StringsAreEqual(thread, *day, *unit) ||
EcmaStringAccessor::StringsAreEqual(thread, *days, *unit)) {
*unitEnum = UDAT_REL_UNIT_DAY;
} else if (EcmaStringAccessor::StringsAreEqual(thread, *week, *unit) ||
EcmaStringAccessor::StringsAreEqual(thread, *weeks, *unit)) {
*unitEnum = UDAT_REL_UNIT_WEEK;
} else if (EcmaStringAccessor::StringsAreEqual(thread, *month, *unit) ||
EcmaStringAccessor::StringsAreEqual(thread, *months, *unit)) {
*unitEnum = UDAT_REL_UNIT_MONTH;
} else if (EcmaStringAccessor::StringsAreEqual(thread, *quarter, *unit) ||
EcmaStringAccessor::StringsAreEqual(thread, *quarters, *unit)) {
*unitEnum = UDAT_REL_UNIT_QUARTER;
} else if (EcmaStringAccessor::StringsAreEqual(thread, *year, *unit) ||
EcmaStringAccessor::StringsAreEqual(thread, *years, *unit)) {
*unitEnum = UDAT_REL_UNIT_YEAR;
} else {
return false;
}
return true;
}
JSHandle<JSTaggedValue> JSRelativeTimeFormat::UnwrapRelativeTimeFormat(JSThread *thread,
const JSHandle<JSTaggedValue> &rtf)
{
ASSERT_PRINT(rtf->IsJSObject(), "rtf is not a JSObject");
JSHandle<GlobalEnv> env = thread->GetGlobalEnv();
JSHandle<JSTaggedValue> result = JSIntl::LegacyUnwrapReceiver(
thread, rtf, env->GetRelativeTimeFormatFunction(), rtf->IsJSRelativeTimeFormat());
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle<JSTaggedValue>(thread, JSTaggedValue::Exception()));
if (!result->IsJSRelativeTimeFormat()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Method called on incompatible receiver",
JSHandle<JSTaggedValue>(thread, JSTaggedValue::Exception()));
}
return result;
}
icu::FormattedRelativeDateTime GetIcuFormatted(JSThread *thread,
const JSHandle<JSRelativeTimeFormat> &relativeTimeFormat,
double value, const JSHandle<EcmaString> &unit)
{
icu::RelativeDateTimeFormatter *formatter = relativeTimeFormat->GetIcuRTFFormatter(thread);
ASSERT_PRINT(formatter != nullptr, "rtfFormatter is null");
if (!std::isfinite(value)) {
THROW_RANGE_ERROR_AND_RETURN(thread, "", icu::FormattedRelativeDateTime());
}
URelativeDateTimeUnit unitEnum;
if (!SingularUnitToIcuUnit(thread, unit, &unitEnum)) {
THROW_RANGE_ERROR_AND_RETURN(thread, "", icu::FormattedRelativeDateTime());
}
UErrorCode status = U_ZERO_ERROR;
NumericOption numeric = relativeTimeFormat->GetNumeric();
icu::FormattedRelativeDateTime formatted;
switch (numeric) {
case NumericOption::ALWAYS:
formatted = formatter->formatNumericToValue(value, unitEnum, status);
ASSERT_PRINT(U_SUCCESS(status), "icu format to value error");
break;
case NumericOption::AUTO:
formatted = formatter->formatToValue(value, unitEnum, status);
ASSERT_PRINT(U_SUCCESS(status), "icu format to value error");
break;
default:
LOG_ECMA(FATAL) << "this branch is unreachable";
UNREACHABLE();
}
return formatted;
}
JSHandle<EcmaString> SingularUnitString(JSThread *thread, const JSHandle<EcmaString> &unit)
{
auto globalConst = thread->GlobalConstants();
JSHandle<EcmaString> second = JSHandle<EcmaString>::Cast(globalConst->GetHandledSecondString());
JSHandle<EcmaString> minute = JSHandle<EcmaString>::Cast(globalConst->GetHandledMinuteString());
JSHandle<EcmaString> hour = JSHandle<EcmaString>::Cast(globalConst->GetHandledHourString());
JSHandle<EcmaString> day = JSHandle<EcmaString>::Cast(globalConst->GetHandledDayString());
JSHandle<EcmaString> week = JSHandle<EcmaString>::Cast(globalConst->GetHandledWeekString());
JSHandle<EcmaString> month = JSHandle<EcmaString>::Cast(globalConst->GetHandledMonthString());
JSHandle<EcmaString> quarter = JSHandle<EcmaString>::Cast(globalConst->GetHandledQuarterString());
JSHandle<EcmaString> year = JSHandle<EcmaString>::Cast(globalConst->GetHandledYearString());
JSHandle<EcmaString> seconds = JSHandle<EcmaString>::Cast(globalConst->GetHandledSecondsString());
JSHandle<EcmaString> minutes = JSHandle<EcmaString>::Cast(globalConst->GetHandledMinutesString());
JSHandle<EcmaString> hours = JSHandle<EcmaString>::Cast(globalConst->GetHandledHoursString());
JSHandle<EcmaString> days = JSHandle<EcmaString>::Cast(globalConst->GetHandledDaysString());
JSHandle<EcmaString> weeks = JSHandle<EcmaString>::Cast(globalConst->GetHandledWeeksString());
JSHandle<EcmaString> months = JSHandle<EcmaString>::Cast(globalConst->GetHandledMonthsString());
JSHandle<EcmaString> quarters = JSHandle<EcmaString>::Cast(globalConst->GetHandledQuartersString());
JSHandle<EcmaString> years = JSHandle<EcmaString>::Cast(globalConst->GetHandledYearsString());
if (EcmaStringAccessor::StringsAreEqual(thread, *second, *unit) ||
EcmaStringAccessor::StringsAreEqual(thread, *seconds, *unit)) {
return second;
}
if (EcmaStringAccessor::StringsAreEqual(thread, *minute, *unit) ||
EcmaStringAccessor::StringsAreEqual(thread, *minutes, *unit)) {
return minute;
}
if (EcmaStringAccessor::StringsAreEqual(thread, *hour, *unit) ||
EcmaStringAccessor::StringsAreEqual(thread, *hours, *unit)) {
return hour;
}
if (EcmaStringAccessor::StringsAreEqual(thread, *day, *unit) ||
EcmaStringAccessor::StringsAreEqual(thread, *days, *unit)) {
return day;
}
if (EcmaStringAccessor::StringsAreEqual(thread, *week, *unit) ||
EcmaStringAccessor::StringsAreEqual(thread, *weeks, *unit)) {
return week;
}
if (EcmaStringAccessor::StringsAreEqual(thread, *month, *unit) ||
EcmaStringAccessor::StringsAreEqual(thread, *months, *unit)) {
return month;
}
if (EcmaStringAccessor::StringsAreEqual(thread, *quarter, *unit) ||
EcmaStringAccessor::StringsAreEqual(thread, *quarters, *unit)) {
return quarter;
}
if (EcmaStringAccessor::StringsAreEqual(thread, *year, *unit) ||
EcmaStringAccessor::StringsAreEqual(thread, *years, *unit)) {
return year;
}
JSHandle<JSTaggedValue> undefinedValue(thread, JSTaggedValue::Undefined());
return JSHandle<EcmaString>::Cast(undefinedValue);
}
JSHandle<EcmaString> JSRelativeTimeFormat::Format(JSThread *thread, double value, const JSHandle<EcmaString> &unit,
const JSHandle<JSRelativeTimeFormat> &relativeTimeFormat)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
icu::FormattedRelativeDateTime formatted = GetIcuFormatted(thread, relativeTimeFormat, value, unit);
RETURN_HANDLE_IF_ABRUPT_COMPLETION(EcmaString, thread);
UErrorCode status = U_ZERO_ERROR;
icu::UnicodeString uString = formatted.toString(status);
if (U_FAILURE(status) != 0) {
THROW_RANGE_ERROR_AND_RETURN(thread, "icu formatted toString error", factory->GetEmptyString());
}
JSHandle<EcmaString> string =
factory->NewFromUtf16(reinterpret_cast<const uint16_t *>(uString.getBuffer()), uString.length());
return string;
}
void FormatToArray(JSThread *thread, const JSHandle<JSArray> &array,
const icu::FormattedRelativeDateTime &formatted, double value,
const JSHandle<EcmaString> &unit)
{
UErrorCode status = U_ZERO_ERROR;
icu::UnicodeString formattedText = formatted.toString(status);
if (U_FAILURE(status)) {
THROW_TYPE_ERROR(thread, "formattedRelativeDateTime toString failed");
}
icu::ConstrainedFieldPosition cfpo;
cfpo.constrainCategory(UFIELD_CATEGORY_NUMBER);
int32_t index = 0;
int32_t previousLimit = 0;
auto globalConst = thread->GlobalConstants();
JSHandle<JSTaggedValue> taggedValue(thread, JSTaggedValue(value));
JSMutableHandle<JSTaggedValue> typeString(thread, JSTaggedValue::Undefined());
JSHandle<JSTaggedValue> unitString = globalConst->GetHandledUnitString();
std::vector<std::pair<int32_t, int32_t>> separatorFields;
* From ICU header file document @unumberformatter.h
* Sets a constraint on the field category.
*
* When this instance of ConstrainedFieldPosition is passed to FormattedValue#nextPosition,
* positions are skipped unless they have the given category.
*
* Any previously set constraints are cleared.
*
* For example, to loop over only the number-related fields:
*
* ConstrainedFieldPosition cfpo;
* cfpo.constrainCategory(UFIELDCATEGORY_NUMBER_FORMAT);
* while (fmtval.nextPosition(cfpo, status)) {
* // handle the number-related field position
* }
*/
while ((formatted.nextPosition(cfpo, status) != 0)) {
int32_t fieldId = cfpo.getField();
int32_t start = cfpo.getStart();
int32_t limit = cfpo.getLimit();
if (static_cast<UNumberFormatFields>(fieldId) == UNUM_GROUPING_SEPARATOR_FIELD) {
separatorFields.push_back(std::pair<int32_t, int32_t>(start, limit));
continue;
}
if (start > previousLimit) {
typeString.Update(globalConst->GetLiteralString());
JSHandle<EcmaString> substring =
intl::LocaleHelper::UStringToString(thread, formattedText, previousLimit, start);
JSLocale::PutElement(thread, index++, array, typeString, JSHandle<JSTaggedValue>::Cast(substring));
RETURN_IF_ABRUPT_COMPLETION(thread);
}
for (auto it = separatorFields.begin(); it != separatorFields.end(); it++) {
if (it->first > start) {
JSHandle<EcmaString> resString =
intl::LocaleHelper::UStringToString(thread, formattedText, start, it->first);
typeString.Update(
JSLocale::GetNumberFieldType(thread, taggedValue.GetTaggedValue(), fieldId).GetTaggedValue());
JSHandle<JSObject> record =
JSLocale::PutElement(thread, index++, array, typeString, JSHandle<JSTaggedValue>::Cast(resString));
RETURN_IF_ABRUPT_COMPLETION(thread);
JSObject::CreateDataPropertyOrThrow(thread, record, unitString, JSHandle<JSTaggedValue>::Cast(unit));
RETURN_IF_ABRUPT_COMPLETION(thread);
resString = intl::LocaleHelper::UStringToString(thread, formattedText, it->first, it->second);
typeString.Update(JSLocale::GetNumberFieldType(thread, taggedValue.GetTaggedValue(),
UNUM_GROUPING_SEPARATOR_FIELD).GetTaggedValue());
record =
JSLocale::PutElement(thread, index++, array, typeString, JSHandle<JSTaggedValue>::Cast(resString));
RETURN_IF_ABRUPT_COMPLETION(thread);
JSObject::CreateDataPropertyOrThrow(thread, record, unitString, JSHandle<JSTaggedValue>::Cast(unit));
RETURN_IF_ABRUPT_COMPLETION(thread);
start = it->second;
}
}
JSHandle<EcmaString> subString = intl::LocaleHelper::UStringToString(thread, formattedText, start, limit);
typeString.Update(JSLocale::GetNumberFieldType(thread, taggedValue.GetTaggedValue(), fieldId).GetTaggedValue());
JSHandle<JSObject> record =
JSLocale::PutElement(thread, index++, array, typeString, JSHandle<JSTaggedValue>::Cast(subString));
RETURN_IF_ABRUPT_COMPLETION(thread);
JSObject::CreateDataPropertyOrThrow(thread, record, unitString, JSHandle<JSTaggedValue>::Cast(unit));
RETURN_IF_ABRUPT_COMPLETION(thread);
previousLimit = limit;
}
if (formattedText.length() > previousLimit) {
typeString.Update(globalConst->GetLiteralString());
JSHandle<EcmaString> substring =
intl::LocaleHelper::UStringToString(thread, formattedText, previousLimit, formattedText.length());
JSLocale::PutElement(thread, index, array, typeString, JSHandle<JSTaggedValue>::Cast(substring));
RETURN_IF_ABRUPT_COMPLETION(thread);
}
}
JSHandle<JSArray> JSRelativeTimeFormat::FormatToParts(JSThread *thread, double value, const JSHandle<EcmaString> &unit,
const JSHandle<JSRelativeTimeFormat> &relativeTimeFormat)
{
icu::FormattedRelativeDateTime formatted = GetIcuFormatted(thread, relativeTimeFormat, value, unit);
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSArray, thread);
JSHandle<EcmaString> singularUnit = SingularUnitString(thread, unit);
JSHandle<JSArray> array = JSHandle<JSArray>::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)));
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSArray, thread);
FormatToArray(thread, array, formatted, value, singularUnit);
return array;
}
void JSRelativeTimeFormat::ResolvedOptions(JSThread *thread, const JSHandle<JSRelativeTimeFormat> &relativeTimeFormat,
const JSHandle<JSObject> &options)
{
if (relativeTimeFormat->GetIcuRTFFormatter(thread) != nullptr) {
[[maybe_unused]] icu::RelativeDateTimeFormatter *formatter = relativeTimeFormat->GetIcuRTFFormatter(thread);
} else {
THROW_ERROR(thread, ErrorType::RANGE_ERROR, "rtf is not initialized");
}
auto globalConst = thread->GlobalConstants();
JSHandle<JSTaggedValue> property = globalConst->GetHandledLocaleString();
JSHandle<EcmaString> locale(thread, relativeTimeFormat->GetLocale(thread));
PropertyDescriptor localeDesc(thread, JSHandle<JSTaggedValue>::Cast(locale), true, true, true);
JSObject::DefineOwnProperty(thread, options, property, localeDesc);
property = globalConst->GetHandledStyleString();
RelativeStyleOption style = relativeTimeFormat->GetStyle();
JSHandle<JSTaggedValue> styleValue;
if (style == RelativeStyleOption::LONG) {
styleValue = globalConst->GetHandledLongString();
} else if (style == RelativeStyleOption::SHORT) {
styleValue = globalConst->GetHandledShortString();
} else if (style == RelativeStyleOption::NARROW) {
styleValue = globalConst->GetHandledNarrowString();
}
PropertyDescriptor styleDesc(thread, styleValue, true, true, true);
JSObject::DefineOwnProperty(thread, options, property, styleDesc);
property = globalConst->GetHandledNumericString();
NumericOption numeric = relativeTimeFormat->GetNumeric();
JSHandle<JSTaggedValue> numericValue;
if (numeric == NumericOption::ALWAYS) {
numericValue = globalConst->GetHandledAlwaysString();
} else if (numeric == NumericOption::AUTO) {
numericValue = globalConst->GetHandledAutoString();
} else {
THROW_ERROR(thread, ErrorType::RANGE_ERROR, "numeric is exception");
}
PropertyDescriptor numericDesc(thread, numericValue, true, true, true);
JSObject::DefineOwnProperty(thread, options, property, numericDesc);
property = JSHandle<JSTaggedValue>::Cast(globalConst->GetHandledNumberingSystemString());
JSHandle<JSTaggedValue> numberingSystem(thread, relativeTimeFormat->GetNumberingSystem(thread));
PropertyDescriptor numberingSystemDesc(thread, numberingSystem, true, true, true);
JSObject::DefineOwnProperty(thread, options, property, numberingSystemDesc);
}
}