Impelement Intl.DateTimeFormat.prototype.formatRange

Signed-off-by: Seonghyun Kim <sh8281.kim@samsung.com>
This commit is contained in:
Seonghyun Kim 2025-07-24 17:40:18 +09:00 committed by MuHong Byun
commit 05553d2264
12 changed files with 294 additions and 32 deletions

View file

@ -288,7 +288,8 @@ extern "C" {
#include "RuntimeICUBinder.h"
#include "ICUPolyfill.h"
#else
#define U_SHOW_CPLUSPLUS_API 0
#define U_SHOW_CPLUSPLUS_HEADER_API 0
#if defined(OS_WINDOWS)
#include <icu.h>
#else
@ -313,6 +314,8 @@ extern "C" {
#include <unicode/uloc.h> // for Intl
#include <unicode/uldnames.h> // for Intl
#include <unicode/ulistformatter.h> // for Intl
#include <unicode/ures.h> // for Intl
#include <unicode/udateintervalformat.h> // for Intl
// FIXME replace these vzone decl into include
// I declare vzone api because there is no header file in include folder

View file

@ -98,7 +98,7 @@ static Value builtinDateConstructor(ExecutionState& state, Value thisValue, size
} else {
// Let tv be ToNumber(v).
double V = v.toNumber(state);
thisObject->setTimeValue(DateObject::timeClip(state, V));
thisObject->setTimeValue(DateObject::timeClipToTime64(state, V));
}
}
} else {
@ -320,7 +320,7 @@ static Value builtinDateSetHelper(ExecutionState& state, DateSetterType setterTy
if (setterType == DateSetterType::Day && length == 3) {
// setFullYear, setUTCFullYear case
if (!isOriginalDateValid) {
d->setTimeValue(DateObject::timeClip(state, 0));
d->setTimeValue(DateObject::timeClipToTime64(state, 0));
d->setTimeValue(d->getTimezoneOffset(state) * TimeConstant::MsPerMinute);
originalDateValue = d->primitiveValue();
isOriginalDateValid = true;
@ -409,7 +409,7 @@ static Value builtinDateSetTime(ExecutionState& state, Value thisValue, size_t a
{
RESOLVE_THIS_BINDING_TO_DATE(thisObject, Date, setTime);
if (argc > 0) {
thisObject->setTimeValue(DateObject::timeClip(state, argv[0].toNumber(state)));
thisObject->setTimeValue(DateObject::timeClipToTime64(state, argv[0].toNumber(state)));
return Value(Value::DoubleToIntConvertibleTestNeeds, thisObject->primitiveValue());
} else {
thisObject->setTimeValueAsNaN();
@ -433,7 +433,7 @@ static Value builtinDateSetYear(ExecutionState& state, Value thisValue, size_t a
DateObject* d = thisObject;
if (!(d->isValid())) {
d->setTimeValue(DateObject::timeClip(state, 0));
d->setTimeValue(DateObject::timeClipToTime64(state, 0));
d->setTimeValue(d->getTimezoneOffset(state) * TimeConstant::MsPerMinute);
}
ASSERT(d->isValid());

View file

@ -248,6 +248,23 @@ static Value builtinIntlDateTimeFormatFormatToParts(ExecutionState& state, Value
return dtf->formatToParts(state, x);
}
static Value builtinIntlDateTimeFormatFormatRange(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
if (!thisValue.isObject() || !thisValue.asObject()->isIntlDateTimeFormatObject()) {
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "Method called on incompatible receiver");
}
if (argv[0].isUndefined() || argv[1].isUndefined()) {
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "get invalid date value");
}
IntlDateTimeFormatObject* dtf = thisValue.asObject()->asIntlDateTimeFormatObject();
double x = argv[0].toNumber(state);
double y = argv[1].toNumber(state);
auto result = dtf->formatRange(state, x, y);
return Value(new UTF16String(result.data(), result.length()));
}
static void setFormatOpt(ExecutionState& state, Object* internalSlot, Object* result, String* prop)
{
ObjectGetResult r;
@ -1479,6 +1496,9 @@ void GlobalObject::installIntl(ExecutionState& state)
m_intlDateTimeFormatPrototype->directDefineOwnProperty(state, state.context()->staticStrings().lazyFormatToParts(),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->lazyFormatToParts(), builtinIntlDateTimeFormatFormatToParts, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::ConfigurablePresent | ObjectPropertyDescriptor::WritablePresent)));
m_intlDateTimeFormatPrototype->directDefineOwnProperty(state, state.context()->staticStrings().lazyFormatRange(),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->lazyFormatRange(), builtinIntlDateTimeFormatFormatRange, 2, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::ConfigurablePresent | ObjectPropertyDescriptor::WritablePresent)));
m_intlDateTimeFormatPrototype->directDefineOwnProperty(state, state.context()->staticStrings().resolvedOptions,
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->resolvedOptions, builtinIntlDateTimeFormatResolvedOptions, 0, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::ConfigurablePresent | ObjectPropertyDescriptor::WritablePresent)));

View file

@ -491,6 +491,7 @@ IntlDateTimeFormatObject::IntlDateTimeFormatObject(ExecutionState& state, Object
, m_calendar(String::emptyString())
, m_numberingSystem(String::emptyString())
, m_timeZone(String::emptyString())
, m_timeZoneICU(String::emptyString())
, m_icuDateFormat(nullptr)
{
// Let requestedLocales be ? CanonicalizeLocaleList(locales).
@ -1026,7 +1027,9 @@ void IntlDateTimeFormatObject::initDateTimeFormatOtherHelper(ExecutionState& sta
+ pad('0', 2, std::to_string(absMinutes / 60)) + pad('0', 2, std::to_string(absMinutes % 60));
timeZoneView = utf8StringToUTF16String(timeZoneForICU.data(), timeZoneForICU.length());
}
m_icuDateFormat = udat_open(UDAT_PATTERN, UDAT_PATTERN, dataLocaleWithExtensions.data(), (UChar*)timeZoneView.data(), timeZoneView.length(), (UChar*)patternBuffer.data(), patternBuffer.length(), &status);
m_timeZoneICU = new UTF16String(std::move(timeZoneView));
m_icuDateFormat = udat_open(UDAT_PATTERN, UDAT_PATTERN, dataLocaleWithExtensions.data(), m_timeZoneICU->bufferAccessData().bufferAs16Bit,
m_timeZoneICU->length(), (UChar*)patternBuffer.data(), patternBuffer.length(), &status);
if (U_FAILURE(status)) {
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "failed to initialize DateTimeFormat");
return;
@ -1035,6 +1038,9 @@ void IntlDateTimeFormatObject::initDateTimeFormatOtherHelper(ExecutionState& sta
addFinalizer([](PointerValue* obj, void* data) {
IntlDateTimeFormatObject* self = (IntlDateTimeFormatObject*)obj;
udat_close(self->m_icuDateFormat);
if (self->m_icuDateIntervalFormat) {
udtitvfmt_close(self->m_icuDateIntervalFormat.value());
}
},
nullptr);
@ -1404,5 +1410,198 @@ Value IntlDateTimeFormatObject::toDateTimeOptions(ExecutionState& state, Value o
return options;
}
void IntlDateTimeFormatObject::initICUIntervalFormatIfNecessary(ExecutionState& state)
{
if (m_icuDateIntervalFormat) {
return;
}
auto toPatternResult = INTL_ICU_STRING_BUFFER_OPERATION(udat_toPattern, m_icuDateFormat, false);
if (U_FAILURE(toPatternResult.first)) {
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "failed to initialize DateIntervalFormat");
return;
}
auto pattern(std::move(toPatternResult.second));
auto getSkeletonResult = INTL_ICU_STRING_BUFFER_OPERATION(udatpg_getSkeleton, nullptr, pattern.data(), pattern.size());
if (U_FAILURE(getSkeletonResult.first)) {
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "failed to initialize DateIntervalFormat");
return;
}
auto skeleton(std::move(getSkeletonResult.second));
// While the pattern is including right HourCycle patterns, UDateIntervalFormat does not follow.
// We need to enforce HourCycle by setting "hc" extension if it is specified.
std::string locale = m_locale->toNonGCUTF8StringData();
locale += "-u-ca-";
locale += m_calendar->asString()->toNonGCUTF8StringData();
locale += "-nu-";
locale += m_numberingSystem->asString()->toNonGCUTF8StringData();
if (!m_hourCycle.toValue().isUndefined()) {
locale += "-hc-";
locale += m_hourCycle.toValue().asString()->toNonGCUTF8StringData();
}
UErrorCode status = U_ZERO_ERROR;
m_icuDateIntervalFormat = udtitvfmt_open(locale.data(), skeleton.data(), skeleton.size(), m_timeZoneICU->bufferAccessData().bufferAs16Bit, m_timeZoneICU->length(), &status);
if (U_FAILURE(status)) {
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "failed to initialize DateIntervalFormat");
return;
}
}
static LocalResourcePointer<UFormattedDateInterval> formattedValueFromDateRange(ExecutionState& state, UDateIntervalFormat* dateIntervalFormat,
UDateFormat* dateFormat, double startDate, double endDate, UErrorCode& status)
{
#if defined(ENABLE_RUNTIME_ICU_BINDER)
UVersionInfo versionArray;
u_getVersion(versionArray);
if (versionArray[0] < 67) {
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "some function of Intl.DateTimeFormat needs 67+ version of ICU");
}
#endif
LocalResourcePointer<UFormattedDateInterval> result(udtitvfmt_openResult(&status),
[](UFormattedDateInterval* d) { udtitvfmt_closeResult(d); });
if (U_FAILURE(status)) {
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "failed to format DateIntervalFormat");
return result;
}
// If a date is after Oct 15, 1582, the configuration of gregorian calendar change date in UCalendar does not affect
// on the formatted string. To ensure that it is after Oct 15 in all timezones, we add one day to gregorian calendar
// change date in UTC, so that this check can conservatively answer whether the date is definitely after gregorian
// calendar change date.
auto definitelyAfterGregorianCalendarChangeDate = [](double millisecondsFromEpoch) {
constexpr double gregorianCalendarReformDateInUTC = -12219292800000.0;
return millisecondsFromEpoch >= (gregorianCalendarReformDateInUTC + TimeConstant::MsPerDay);
};
// UFormattedDateInterval does not have a way to configure gregorian calendar change date while ECMAScript requires that
// gregorian calendar change should not have effect (we are setting ucal_setGregorianChange(cal, minECMAScriptTime, &status) explicitly).
// As a result, if the input date is older than gregorian calendar change date (Oct 15, 1582), the formatted string becomes
// julian calendar date.
// udtitvfmt_formatCalendarToResult API offers the way to set calendar to each date of the input, so that we can use UDateFormat's
// calendar which is already configured to meet ECMAScript's requirement (effectively clearing gregorian calendar change date).
//
// If we can ensure that startDate is after gregorian calendar change date, we can just use udtitvfmt_formatToResult since gregorian
// calendar change date does not affect on the formatted string.
//
// https://unicode-org.atlassian.net/browse/ICU-20705
if (definitelyAfterGregorianCalendarChangeDate(startDate)) {
udtitvfmt_formatToResult(dateIntervalFormat, startDate, endDate, result.get(), &status);
} else {
auto createCalendarForDate = [](const UCalendar* calendar, double date, UErrorCode& status) -> LocalResourcePointer<UCalendar> {
auto result = LocalResourcePointer<UCalendar>(ucal_clone(calendar, &status), [](UCalendar* cal) {
ucal_close(cal);
});
if (U_FAILURE(status)) {
result.reset();
return result;
}
ucal_setMillis(result.get(), date, &status);
if (U_FAILURE(status)) {
result.reset();
return result;
}
return result;
};
auto calendar = udat_getCalendar(dateFormat);
auto startCalendar = createCalendarForDate(calendar, startDate, status);
if (U_FAILURE(status)) {
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "failed to format DateIntervalFormat");
return result;
}
auto endCalendar = createCalendarForDate(calendar, endDate, status);
if (U_FAILURE(status)) {
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "failed to format DateIntervalFormat");
return result;
}
udtitvfmt_formatCalendarToResult(dateIntervalFormat, startCalendar.get(), endCalendar.get(), result.get(), &status);
}
return result;
}
static bool dateFieldsPracticallyEqual(const UFormattedValue* formattedValue, UErrorCode& status)
{
LocalResourcePointer<UConstrainedFieldPosition> iterator(ucfpos_open(&status), [](UConstrainedFieldPosition* pos) {
ucfpos_close(pos);
});
if (U_FAILURE(status)) {
return false;
}
// We only care about UFIELD_CATEGORY_DATE_INTERVAL_SPAN category.
ucfpos_constrainCategory(iterator.get(), UFIELD_CATEGORY_DATE_INTERVAL_SPAN, &status);
if (U_FAILURE(status)) {
return false;
}
bool hasSpan = ufmtval_nextPosition(formattedValue, iterator.get(), &status);
if (U_FAILURE(status)) {
return false;
}
return !hasSpan;
}
UTF16StringDataNonGCStd IntlDateTimeFormatObject::formatRange(ExecutionState& state, double startDate, double endDate)
{
startDate = DateObject::timeClip(startDate);
endDate = DateObject::timeClip(endDate);
if (std::isnan(startDate) || std::isnan(endDate)) {
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, "get invalid date value");
}
initICUIntervalFormatIfNecessary(state);
UErrorCode status = U_ZERO_ERROR;
auto result = formattedValueFromDateRange(state, m_icuDateIntervalFormat.value(), m_icuDateFormat, startDate, endDate, status);
if (U_FAILURE(status)) {
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "failed to format DateIntervalFormat");
return UTF16StringDataNonGCStd();
}
// UFormattedValue is owned by UFormattedDateInterval. We do not need to close it.
auto formattedValue = udtitvfmt_resultAsValue(result.get(), &status);
if (U_FAILURE(status)) {
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "failed to format DateIntervalFormat");
return UTF16StringDataNonGCStd();
}
// If the formatted parts of startDate and endDate are the same, it is possible that the resulted string does not look like range.
// For example, if the requested format only includes "year" and startDate and endDate are the same year, the result just contains one year.
// In that case, startDate and endDate are *practically-equal* (spec term), and we generate parts as we call `formatToParts(startDate)` with
// `source: "shared"` additional fields.
bool equal = dateFieldsPracticallyEqual(formattedValue, status);
if (U_FAILURE(status)) {
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "failed to format DateIntervalFormat");
return UTF16StringDataNonGCStd();
}
if (equal) {
return format(state, startDate);
}
int32_t formattedStringLength = 0;
const UChar* formattedStringPointer = ufmtval_getString(formattedValue, &formattedStringLength, &status);
if (U_FAILURE(status)) {
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "failed to format DateIntervalFormat");
return UTF16StringDataNonGCStd();
}
UTF16StringDataNonGCStd buffer(formattedStringPointer, formattedStringLength);
replaceNarrowNoBreakSpaceOrThinSpaceWithNormalSpace(buffer);
return buffer;
}
} // namespace Escargot
#endif

View file

@ -38,6 +38,7 @@ public:
UTF16StringDataNonGCStd format(ExecutionState& state, double x);
ArrayObject* formatToParts(ExecutionState& state, double x);
UTF16StringDataNonGCStd formatRange(ExecutionState& state, double startDate, double endDate);
static Value toDateTimeOptions(ExecutionState& state, Value options, Value required, Value defaults);
static std::string readHourCycleFromPattern(const UTF16StringDataNonGCStd& patternString);
String* locale() const
@ -139,11 +140,12 @@ protected:
String* initDateTimeFormatMainHelper(ExecutionState& state, StringMap& opt, const Value& options, const Value& hour12, std::function<void(String* prop, Value* values, size_t valuesSize)>& doTable4, StringBuilder& skeletonBuilder);
void initDateTimeFormatOtherHelper(ExecutionState& state, const Value& dataLocale, const Value& dateStyle, const Value& timeStyle, const Value& hourCycle, const Value& hour12, String* hour, const StringMap& opt, std::string& dataLocaleWithExtensions, StringBuilder& skeletonBuilder);
void setDateFromPattern(ExecutionState& state, UTF16StringDataNonGCStd& patternBuffer, bool hasHourOption);
void initICUIntervalFormatIfNecessary(ExecutionState& state);
String* m_locale;
String* m_calendar;
String* m_numberingSystem;
String* m_timeZone;
String* m_timeZoneICU;
EncodedValue m_hour12;
EncodedValue m_era;
@ -162,6 +164,7 @@ protected:
EncodedValue m_timeStyle;
UDateFormat* m_icuDateFormat;
Optional<UDateIntervalFormat*> m_icuDateIntervalFormat;
};
} // namespace Escargot

View file

@ -209,7 +209,7 @@ void DateObject::setTimeValue(ExecutionState& state, const Value& v)
{
Value pv = v.toPrimitive(state);
if (pv.isNumber()) {
setTimeValue(DateObject::timeClip(state, pv.asNumber()));
setTimeValue(DateObject::timeClipToTime64(state, pv.asNumber()));
} else {
String* istr = v.toString(state);
setTimeValue(parseStringToDate(state, istr));

View file

@ -79,7 +79,15 @@ public:
return true;
}
static time64_t timeClip(ExecutionState& state, double V)
static double timeClip(double t)
{
if (std::abs(t) > TimeConstant::MaximumDatePrimitiveValue) {
return std::numeric_limits<double>::quiet_NaN();
}
return std::trunc(t) + 0.0;
}
static time64_t timeClipToTime64(ExecutionState& state, double V)
{
if (std::isinf(V) || std::isnan(V)) {
return TIME64NAN;

View file

@ -527,13 +527,23 @@ static std::string findTimezone()
#else
static std::string findTimezone()
{
auto tz = icu::TimeZone::detectHostTimeZone();
icu::UnicodeString id;
tz->getID(id);
delete tz;
std::string r;
id.toUTF8String(r);
return r;
UChar result[256];
UErrorCode status = U_ZERO_ERROR;
int32_t len = ucal_getHostTimeZone(result, sizeof(result) / sizeof(UChar), &status);
if (U_SUCCESS(status)) {
std::string u8Result;
for (int32_t i = 0; i < len; i++) {
u8Result.push_back(result[i]);
}
return u8Result;
}
// fallback
time_t t;
tm lt;
t = time(NULL);
localtime_r(&t, &lt);
return lt.tm_zone;
}
#endif

View file

@ -130,6 +130,7 @@
#define ucal_getDayOfWeekType RuntimeICUBinder::ICU::instance().ucal_getDayOfWeekType
#define ucal_setGregorianChange RuntimeICUBinder::ICU::instance().ucal_setGregorianChange
#define ucal_setMillis RuntimeICUBinder::ICU::instance().ucal_setMillis
#define ucal_clone RuntimeICUBinder::ICU::instance().ucal_clone
#define udatpg_close RuntimeICUBinder::ICU::instance().udatpg_close
#define udatpg_open RuntimeICUBinder::ICU::instance().udatpg_open
@ -262,4 +263,12 @@
#define ulistfmt_resultAsValue RuntimeICUBinder::ICU::instance().ulistfmt_resultAsValue
#define ulistfmt_closeResult RuntimeICUBinder::ICU::instance().ulistfmt_closeResult
#define udtitvfmt_open RuntimeICUBinder::ICU::instance().udtitvfmt_open
#define udtitvfmt_openResult RuntimeICUBinder::ICU::instance().udtitvfmt_openResult
#define udtitvfmt_close RuntimeICUBinder::ICU::instance().udtitvfmt_close
#define udtitvfmt_closeResult RuntimeICUBinder::ICU::instance().udtitvfmt_closeResult
#define udtitvfmt_formatToResult RuntimeICUBinder::ICU::instance().udtitvfmt_formatToResult
#define udtitvfmt_formatCalendarToResult RuntimeICUBinder::ICU::instance().udtitvfmt_formatCalendarToResult
#define udtitvfmt_resultAsValue RuntimeICUBinder::ICU::instance().udtitvfmt_resultAsValue
#endif

View file

@ -8411,6 +8411,22 @@ typedef enum UListFormatterWidth {
} UListFormatterWidth;
#endif /* U_HIDE_DRAFT_API */
// udateintervalformat.h
/**
* Opaque UDateIntervalFormat object for use in C programs.
* @stable ICU 4.8
*/
struct UDateIntervalFormat;
typedef struct UDateIntervalFormat UDateIntervalFormat; /**< C typedef for struct UDateIntervalFormat. @stable ICU 4.8 */
struct UFormattedDateInterval;
/**
* Opaque struct to contain the results of a UDateIntervalFormat operation.
* @stable ICU 64
*/
typedef struct UFormattedDateInterval UFormattedDateInterval;
// uversion.h
/** Maximum length of the copyright string.

View file

@ -140,6 +140,7 @@ namespace RuntimeICUBinder {
F(ucal_getType, const char* (*)(const UCalendar* cal, UErrorCode* status), const char*) \
F(ucal_getAttribute, int32_t (*)(const UCalendar* cal, UCalendarAttribute attr), int32_t) \
F(ucal_getDayOfWeekType, UCalendarWeekdayType (*)(const UCalendar* cal, UCalendarDaysOfWeek dayOfWeek, UErrorCode* status), UCalendarWeekdayType) \
F(ucal_clone, UCalendar* (*)(const UCalendar *cal, UErrorCode *status), UCalendar*) \
F(udatpg_open, UDateTimePatternGenerator* (*)(const char* locale, UErrorCode* pErrorCode), UDateTimePatternGenerator*) \
F(udatpg_getBestPattern, int32_t (*)(UDateTimePatternGenerator * dtpg, const UChar* skeleton, int32_t length, UChar* bestPattern, int32_t capacity, UErrorCode* pErrorCode), int32_t) \
F(udatpg_getBestPatternWithOptions, int32_t (*)(UDateTimePatternGenerator*, const UChar*, int32_t, UDateTimePatternMatchOptions, UChar*, int32_t, UErrorCode*), int32_t) \
@ -199,7 +200,10 @@ namespace RuntimeICUBinder {
F(ulistfmt_openForType, UListFormatter* (*)(const char* locale, UListFormatterType type, UListFormatterWidth width, UErrorCode* status), UListFormatter*) \
F(ulistfmt_format, int32_t (*)(const UListFormatter* listfmt, const UChar* const strings[], const int32_t*, int32_t, UChar*, int32_t, UErrorCode*), int32_t) \
F(ulistfmt_openResult, UFormattedList* (*)(UErrorCode * ec), UFormattedList*) \
F(ulistfmt_resultAsValue, const UFormattedValue* (*)(const UFormattedList* uresult, UErrorCode* ec), const UFormattedValue*)
F(ulistfmt_resultAsValue, const UFormattedValue* (*)(const UFormattedList* uresult, UErrorCode* ec), const UFormattedValue*) \
F(udtitvfmt_open, UDateIntervalFormat* (*)(const char*, const UChar*, int32_t, const UChar*, int32_t, UErrorCode*), UDateIntervalFormat*) \
F(udtitvfmt_openResult, UFormattedDateInterval* (*)(UErrorCode* ec), UFormattedDateInterval*) \
F(udtitvfmt_resultAsValue, const UFormattedValue* (*)(const UFormattedDateInterval*, UErrorCode* ec), const UFormattedValue*)
#define FOR_EACH_I18N_VOID_OP(F) \
F(udat_close, void (*)(UDateFormat * format), void) \
@ -246,7 +250,11 @@ namespace RuntimeICUBinder {
F(ucfpos_getIndexes, void (*)(const UConstrainedFieldPosition* ucfpos, int32_t* pStart, int32_t* pLimit, UErrorCode* ec), void) \
F(ulistfmt_close, void (*)(UListFormatter * listfmt), void) \
F(ulistfmt_formatStringsToResult, void (*)(const UListFormatter*, const UChar* const[], const int32_t*, int32_t, UFormattedList*, UErrorCode*), void) \
F(ulistfmt_closeResult, void (*)(UFormattedList * uresult), void)
F(ulistfmt_closeResult, void (*)(UFormattedList * uresult), void) \
F(udtitvfmt_close, void (*)(UDateIntervalFormat *formatter), void) \
F(udtitvfmt_closeResult, void (*)(UFormattedDateInterval* uresult), void) \
F(udtitvfmt_formatToResult, void (*)(const UDateIntervalFormat*, UDate, UDate, UFormattedDateInterval*, UErrorCode*), void) \
F(udtitvfmt_formatCalendarToResult, void (*)(const UDateIntervalFormat*, UCalendar*, UCalendar*, UFormattedDateInterval*, UErrorCode*), void)
#define FOR_EACH_I18N_STICKY_OP(F) \
F(vzone_getOffset3)

View file

@ -4605,20 +4605,8 @@
<test id="intl402/DateTimeFormat/prototype/format/temporal-plaintime-formatting-datetime-style"><reason>TODO</reason></test>
<test id="intl402/DateTimeFormat/prototype/format/temporal-plainyearmonth-formatting-datetime-style"><reason>TODO</reason></test>
<test id="intl402/DateTimeFormat/prototype/format/temporal-zoneddatetime-not-supported"><reason>TODO</reason></test>
<test id="intl402/DateTimeFormat/prototype/formatRange/argument-date-string"><reason>TODO</reason></test>
<test id="intl402/DateTimeFormat/prototype/formatRange/argument-near-time-boundaries"><reason>TODO</reason></test>
<test id="intl402/DateTimeFormat/prototype/formatRange/argument-to-integer"><reason>TODO</reason></test>
<test id="intl402/DateTimeFormat/prototype/formatRange/argument-tonumber-throws"><reason>TODO</reason></test>
<test id="intl402/DateTimeFormat/prototype/formatRange/builtin"><reason>TODO</reason></test>
<test id="intl402/DateTimeFormat/prototype/formatRange/date-is-infinity-throws"><reason>TODO</reason></test>
<test id="intl402/DateTimeFormat/prototype/formatRange/date-is-nan-throws"><reason>TODO</reason></test>
<test id="intl402/DateTimeFormat/prototype/formatRange/date-same-returns-single-date"><reason>TODO</reason></test>
<test id="intl402/DateTimeFormat/prototype/formatRange/date-x-greater-than-y-not-throws"><reason>TODO</reason></test>
<test id="intl402/DateTimeFormat/prototype/formatRange/en-US"><reason>TODO</reason></test>
<test id="intl402/DateTimeFormat/prototype/formatRange/fractionalSecondDigits"><reason>TODO</reason></test>
<test id="intl402/DateTimeFormat/prototype/formatRange/length"><reason>TODO</reason></test>
<test id="intl402/DateTimeFormat/prototype/formatRange/name"><reason>TODO</reason></test>
<test id="intl402/DateTimeFormat/prototype/formatRange/prop-desc"><reason>TODO</reason></test>
<test id="intl402/DateTimeFormat/prototype/formatRange/temporal-objects-not-overlapping-options"><reason>TODO</reason></test>
<test id="intl402/DateTimeFormat/prototype/formatRange/temporal-objects-resolved-time-zone"><reason>TODO</reason></test>
<test id="intl402/DateTimeFormat/prototype/formatRange/temporal-zoneddatetime-not-supported"><reason>TODO</reason></test>
@ -4652,8 +4640,6 @@
<test id="intl402/DateTimeFormat/prototype/resolvedOptions/offset-timezone-basic"><reason>TODO</reason></test>
<test id="intl402/DateTimeFormat/prototype/resolvedOptions/offset-timezone-change"><reason>TODO</reason></test>
<test id="intl402/DateTimeFormat/prototype/resolvedOptions/resolved-hour-cycle-unicode-extensions-and-options"><reason>TODO</reason></test>
<test id="intl402/DateTimeFormat/prototype/toStringTag/toString"><reason>TODO</reason></test>
<test id="intl402/DateTimeFormat/prototype/toStringTag/toStringTag"><reason>TODO</reason></test>
<test id="intl402/DateTimeFormat/timezone-case-insensitive"><reason>TODO</reason></test>
<test id="intl402/DateTimeFormat/timezone-legacy-non-iana"><reason>TODO</reason></test>
<test id="intl402/DateTimeFormat/timezone-not-canonicalized"><reason>TODO</reason></test>