Implement Temporal.Duration.{ add, subtract, with, toLocalString, toJSON }

Signed-off-by: Seonghyun Kim <sh8281.kim@samsung.com>
This commit is contained in:
Seonghyun Kim 2025-09-08 13:16:52 +09:00 committed by Patrick Kim
commit 8d140c3c0f
9 changed files with 369 additions and 304 deletions

View file

@ -87,12 +87,60 @@ static Value builtinTemporalDurationToString(ExecutionState& state, Value thisVa
return duration->toString(state, argc ? argv[0] : Value());
}
static Value builtinTemporalDurationToJSON(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
RESOLVE_THIS_BINDING_TO_DURATION2(duration, toJSON);
return duration->toString(state, Value());
}
static Value builtinTemporalDurationToLocaleString(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
RESOLVE_THIS_BINDING_TO_DURATION2(duration, toLocaleString);
return duration->toString(state, Value());
}
static Value builtinTemporalDurationValueOf(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "can't convert Duration to primitive type");
return Value();
}
static Value builtinTemporalDurationNegated(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
RESOLVE_THIS_BINDING_TO_DURATION(duration, Negated);
return new TemporalDurationObject(state, TemporalDurationObject::createNegatedTemporalDuration(duration->duration()));
}
static Value builtinTemporalDurationAbs(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
RESOLVE_THIS_BINDING_TO_DURATION2(duration, abs);
auto newDuration = duration->duration();
for (auto& n : newDuration) {
n = std::abs(n);
}
return new TemporalDurationObject(state, newDuration);
}
static Value builtinTemporalDurationWith(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
RESOLVE_THIS_BINDING_TO_DURATION2(duration, with);
return duration->with(state, argv[0]);
}
static Value builtinTemporalDurationAdd(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
RESOLVE_THIS_BINDING_TO_DURATION2(duration, add);
return new TemporalDurationObject(state, duration->addDurations(state, TemporalDurationObject::AddDurationsOperation::Add, argv[0]));
}
static Value builtinTemporalDurationSubtract(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
RESOLVE_THIS_BINDING_TO_DURATION(duration, Subtract);
return new TemporalDurationObject(state, duration->addDurations(state, TemporalDurationObject::AddDurationsOperation::Subtract, argv[0]));
}
static Value builtinTemporalInstantConstructor(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
// If NewTarget is undefined, throw a TypeError exception.
@ -429,8 +477,15 @@ void GlobalObject::installTemporal(ExecutionState& state)
ObjectPropertyDescriptor(Value(strings->lazyTemporalDotDuration().string()), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::ConfigurablePresent)));
m_temporalDurationPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->toString), ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->toString, builtinTemporalDurationToString, 0, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
m_temporalDurationPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->toJSON), ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->toJSON, builtinTemporalDurationToJSON, 0, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
m_temporalDurationPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->toLocaleString), ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->toLocaleString, builtinTemporalDurationToLocaleString, 0, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
m_temporalDurationPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->valueOf), ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->valueOf, builtinTemporalDurationValueOf, 0, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
m_temporalDurationPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->lazyNegated()), ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->lazyNegated(), builtinTemporalDurationNegated, 0, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
m_temporalDurationPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->abs), ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->abs, builtinTemporalDurationAbs, 0, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
m_temporalDurationPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->with), ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->with, builtinTemporalDurationWith, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
m_temporalDurationPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->add), ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->add, builtinTemporalDurationAdd, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
m_temporalDurationPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->lazySubtract()), ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->lazySubtract(), builtinTemporalDurationSubtract, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
#define DEFINE_DURATION_PROTOTYPE_GETTER_PROPERTY(name, stringName, Name) \

View file

@ -367,122 +367,6 @@ Object* IntlDurationFormatObject::resolvedOptions(ExecutionState& state)
return options;
}
static bool isValidDurationWork(double v, int& sign)
{
// If 𝔽(v) is not finite, return false.
if (!std::isfinite(v)) {
return false;
}
// If v < 0, then
if (v < 0) {
// If sign > 0, return false.
if (sign > 0) {
return false;
}
// Set sign to -1.
sign = -1;
} else if (v > 0) {
// Else if v > 0, then
// If sign < 0, return false.
if (sign < 0) {
return false;
}
// Set sign to 1.
sign = 1;
}
return true;
}
static BigIntData totalNanoseconds(const DurationRecord& record)
{
BigIntData resultNs;
constexpr int64_t nanoMultiplier = 1000000000ULL;
constexpr int64_t milliMultiplier = 1000000ULL;
constexpr int64_t microMultiplier = 1000ULL;
{
BigIntData s(record.days());
s = s.multiply(86400);
s = s.multiply(nanoMultiplier);
resultNs = resultNs.addition(s);
}
{
BigIntData s(record.hours());
s = s.multiply(3600);
s = s.multiply(nanoMultiplier);
resultNs = resultNs.addition(s);
}
{
BigIntData s(record.minutes());
s = s.multiply(60);
s = s.multiply(nanoMultiplier);
resultNs = resultNs.addition(s);
}
{
BigIntData s(record.seconds());
s = s.multiply(nanoMultiplier);
resultNs = resultNs.addition(s);
}
{
BigIntData s(record.milliseconds());
s = s.multiply(milliMultiplier);
resultNs = resultNs.addition(s);
}
{
BigIntData s(record.microseconds());
s = s.multiply(microMultiplier);
resultNs = resultNs.addition(s);
}
{
BigIntData s(record.nanoseconds());
resultNs = resultNs.addition(s);
}
return resultNs;
}
// https://tc39.es/ecma402/#sec-isvalidduration
static bool isValidDuration(const DurationRecord& record)
{
// Let sign be 0.
int sign = 0;
// For each value v of « years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds », do
for (double v : record) {
if (!isValidDurationWork(v, sign)) {
return false;
}
}
// If abs(years) ≥ 2**32, return false.
if (std::abs(record.years()) >= (1ULL << 32)) {
return false;
}
// If abs(months) ≥ 2**32, return false.
if (std::abs(record.months()) >= (1ULL << 32)) {
return false;
}
// If abs(weeks) ≥ 2**32, return false.
if (std::abs(record.weeks()) >= (1ULL << 32)) {
return false;
}
// Let normalizedSeconds be days × 86,400 + hours × 3600 + minutes × 60 + seconds + (𝔽(milliseconds)) × 10**-3 + (𝔽(microseconds)) × 10**-6 + (𝔽(nanoseconds)) × 10**-9.
// NOTE: The above step cannot be implemented directly using floating-point arithmetic. Multiplying by 10**-3, 10**-6, and 10**-9 respectively may be imprecise when milliseconds, microseconds, or nanoseconds is an unsafe integer. This multiplication can be implemented in C++ with an implementation of std::remquo() with sufficient bits in the quotient. String manipulation will also give an exact result, since the multiplication is by a power of 10.
BigIntData normalizedNanoSeconds = totalNanoseconds(record);
// If abs(normalizedSeconds) ≥ 2**53, return false.
BigIntData limit(int64_t(1ULL << 53));
limit = limit.multiply(1000000000ULL);
if (normalizedNanoSeconds.greaterThanEqual(limit)) {
return false;
}
limit = limit.multiply(-1);
if (normalizedNanoSeconds.lessThanEqual(limit)) {
return false;
}
// Return true.
return true;
}
static DurationRecord toDurationRecord(ExecutionState& state, const Value& input)
{
// If input is not an Object, then
@ -541,7 +425,7 @@ static DurationRecord toDurationRecord(ExecutionState& state, const Value& input
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "Invalid input for ToDurationRecord");
}
// If IsValidDuration( result.[[Years]], result.[[Months]], result.[[Weeks]], result.[[Days]], result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]]) is false, then
if (!isValidDuration(result)) {
if (!result.isValid()) {
// Throw a RangeError exception.
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, "Invalid input for ToDurationRecord");
}

View file

@ -234,11 +234,11 @@ static void appendInteger(StringBuilder& builder, Int128 value)
String* TemporalDurationObject::temporalDurationToString(ISO8601::Duration duration, Value precision)
{
auto balancedMicroseconds = static_cast<Int128>(duration.microseconds()) + static_cast<Int128>(std::trunc(duration.nanoseconds() / 1000));
auto balancedNanoseconds = static_cast<Int128>(duration.nanoseconds()) % 1000;
auto balancedMilliseconds = static_cast<Int128>(duration.milliseconds()) + (balancedMicroseconds / 1000);
auto balancedMicroseconds = static_cast<Int128>(static_cast<int64_t>(duration.microseconds())) + static_cast<Int128>(static_cast<int64_t>(std::trunc(duration.nanoseconds() / 1000)));
auto balancedNanoseconds = static_cast<Int128>(static_cast<int64_t>(duration.nanoseconds())) % 1000;
auto balancedMilliseconds = static_cast<Int128>(static_cast<int64_t>(duration.milliseconds())) + (balancedMicroseconds / 1000);
balancedMicroseconds = balancedMicroseconds % 1000;
auto balancedSeconds = static_cast<Int128>(duration.seconds()) + (balancedMilliseconds / 1000);
auto balancedSeconds = static_cast<Int128>(static_cast<int64_t>(duration.seconds())) + (balancedMilliseconds / 1000);
balancedMilliseconds = balancedMilliseconds % 1000;
StringBuilder builder;
@ -335,6 +335,104 @@ String* TemporalDurationObject::toString(ExecutionState& state, Value options)
return temporalDurationToString(roundedDuration, precision.precision);
}
// https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.with
TemporalDurationObject* TemporalDurationObject::with(ExecutionState& state, Value temporalDurationLike)
{
// Let duration be the this value.
// Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
// Let temporalDurationLike be ? ToTemporalPartialDurationRecord(temporalDurationLike).
auto temporalPartialDuration = toTemporalPartialDurationRecord(state, temporalDurationLike);
// Step 4 to 23
ISO8601::Duration newDuration;
size_t idx = 0;
for (auto n : temporalPartialDuration) {
if (n) {
newDuration[idx] = n.value();
} else {
newDuration[idx] = m_duration[idx];
}
idx++;
}
// Return ? CreateTemporalDuration(years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds).
if (!newDuration.isValid()) {
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, "Invalid duration value");
}
return new TemporalDurationObject(state, newDuration);
}
ISO8601::PartialDuration TemporalDurationObject::toTemporalPartialDurationRecord(ExecutionState& state, Value temporalDurationLike)
{
// If temporalDurationLike is not an Object, then
if (!temporalDurationLike.isObject()) {
// Throw a TypeError exception.
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "toTemporalPartialDurationRecord needs Object");
}
// Let result be a new partial Duration Record with each field set to undefined.
ISO8601::PartialDuration result;
// NOTE: The following steps read properties and perform independent validation in alphabetical order.
// Let days be ? Get(temporalDurationLike, "days").
// If days is not undefined, set result.[[Days]] to ? ToIntegerIfIntegral(days).
// Let hours be ? Get(temporalDurationLike, "hours").
// If hours is not undefined, set result.[[Hours]] to ? ToIntegerIfIntegral(hours).
// Let microseconds be ? Get(temporalDurationLike, "microseconds").
// If microseconds is not undefined, set result.[[Microseconds]] to ? ToIntegerIfIntegral(microseconds).
// Let milliseconds be ? Get(temporalDurationLike, "milliseconds").
// If milliseconds is not undefined, set result.[[Milliseconds]] to ? ToIntegerIfIntegral(milliseconds).
// Let minutes be ? Get(temporalDurationLike, "minutes").
// If minutes is not undefined, set result.[[Minutes]] to ? ToIntegerIfIntegral(minutes).
// Let months be ? Get(temporalDurationLike, "months").
// If months is not undefined, set result.[[Months]] to ? ToIntegerIfIntegral(months).
// Let nanoseconds be ? Get(temporalDurationLike, "nanoseconds").
// If nanoseconds is not undefined, set result.[[Nanoseconds]] to ? ToIntegerIfIntegral(nanoseconds).
// Let seconds be ? Get(temporalDurationLike, "seconds").
// If seconds is not undefined, set result.[[Seconds]] to ? ToIntegerIfIntegral(seconds).
// Let weeks be ? Get(temporalDurationLike, "weeks").
// If weeks is not undefined, set result.[[Weeks]] to ? ToIntegerIfIntegral(weeks).
// Let years be ? Get(temporalDurationLike, "years").
// If years is not undefined, set result.[[Years]] to ? ToIntegerIfIntegral(years).
Object* s = temporalDurationLike.asObject();
Value v;
#define SET_ONCE(name) \
v = s->get(state, ObjectPropertyName(state.context()->staticStrings().lazy##name())).value(state, s); \
if (!v.isUndefined()) { \
result.set##name(v.toIntegerIfIntergral(state)); \
}
SET_ONCE(Days);
SET_ONCE(Hours);
SET_ONCE(Microseconds);
SET_ONCE(Milliseconds);
SET_ONCE(Minutes);
SET_ONCE(Months);
SET_ONCE(Nanoseconds);
SET_ONCE(Seconds);
SET_ONCE(Weeks);
SET_ONCE(Years);
#undef SET_ONCE
// If years is undefined, and months is undefined, and weeks is undefined, and days is undefined, and hours is undefined, and minutes is undefined, and seconds is undefined, and milliseconds is undefined, and microseconds is undefined, and nanoseconds is undefined, throw a TypeError exception.
bool allUndefined = true;
for (const auto& s : result) {
if (s.hasValue()) {
allUndefined = false;
break;
}
}
if (allUndefined) {
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "toTemporalPartialDurationRecord needs at least one duration value");
}
// Return result.
return result;
}
ISO8601::InternalDuration TemporalDurationObject::toInternalDurationRecord(ISO8601::Duration duration)
{
ISO8601::Duration dateDuration{
@ -357,6 +455,54 @@ Int128 TemporalDurationObject::add24HourDaysToTimeDuration(ExecutionState& state
return result;
}
Int128 TemporalDurationObject::addTimeDuration(ExecutionState& state, Int128 one, Int128 two)
{
// Let result be one + two.
auto result = one + two;
// If abs(result) > maxTimeDuration, throw a RangeError exception.
if (std::abs(result) > ISO8601::InternalDuration::maxTimeDuration) {
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, "Total time in duration is out of range");
return {};
}
// Return result.
return result;
}
ISO8601::Duration TemporalDurationObject::addDurations(ExecutionState& state, AddDurationsOperation operation, Value otherInput)
{
// Set other to ? ToTemporalDuration(other).
auto otherDurationObject = Temporal::toTemporalDuration(state, otherInput);
ISO8601::Duration other = otherDurationObject->duration();
// If operation is subtract, set other to CreateNegatedTemporalDuration(other).
if (operation == AddDurationsOperation::Subtract) {
other = createNegatedTemporalDuration(otherDurationObject->duration());
}
// Let largestUnit1 be DefaultTemporalLargestUnit(duration).
auto largestUnit1 = m_duration.defaultTemporalLargestUnit();
// Let largestUnit2 be DefaultTemporalLargestUnit(other).
auto largestUnit2 = other.defaultTemporalLargestUnit();
// Let largestUnit be LargerOfTwoTemporalUnits(largestUnit1, largestUnit2).
auto largestUnit = Temporal::largerOfTwoTemporalUnits(largestUnit1, largestUnit2);
// If IsCalendarUnit(largestUnit) is true, throw a RangeError exception.
if (Temporal::isCalendarUnit(largestUnit)) {
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, "can not add/subtract Calendar Unit duration");
}
// Let d1 be ToInternalDurationRecordWith24HourDays(duration).
auto d1 = toInternalDurationRecordWith24HourDays(state, m_duration);
// Let d2 be ToInternalDurationRecordWith24HourDays(other).
auto d2 = toInternalDurationRecordWith24HourDays(state, other);
// Let timeResult be ? AddTimeDuration(d1.[[Time]], d2.[[Time]]).
auto timeResult = addTimeDuration(state, d1.time(), d2.time());
// Let result be CombineDateAndTimeDuration(ZeroDateDuration(), timeResult).
auto result = ISO8601::InternalDuration::combineDateAndTimeDuration({}, timeResult);
// Return ? TemporalDurationFromInternal(result, largestUnit).
auto resultDuration = temporalDurationFromInternal(state, result, largestUnit);
if (!resultDuration.isValid()) {
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, "Result duration is out of range");
}
return resultDuration;
}
ISO8601::InternalDuration TemporalDurationObject::toInternalDurationRecordWith24HourDays(ExecutionState& state, ISO8601::Duration duration)
{
// Let timeDuration be TimeDurationFromComponents(duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]], duration.[[Milliseconds]], duration.[[Microseconds]], duration.[[Nanoseconds]]).

View file

@ -80,6 +80,12 @@ public:
String* toString(ExecutionState& state, Value options);
static String* temporalDurationToString(ISO8601::Duration duration, Value precision);
// https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.with
TemporalDurationObject* with(ExecutionState& state, Value temporalDurationLike);
// https://tc39.es/proposal-temporal/#sec-temporal-totemporalpartialdurationrecord
static ISO8601::PartialDuration toTemporalPartialDurationRecord(ExecutionState& state, Value temporalDurationLike);
// https://tc39.es/proposal-temporal/#sec-temporal-tointernaldurationrecord
static ISO8601::InternalDuration toInternalDurationRecord(ISO8601::Duration duration);
@ -98,6 +104,16 @@ public:
// https://tc39.es/proposal-temporal/#sec-temporal-add24hourdaystonormalizedtimeduration
static Int128 add24HourDaysToTimeDuration(ExecutionState& state, Int128 d, double days);
// https://tc39.es/proposal-temporal/#sec-temporal-addtimeduration
static Int128 addTimeDuration(ExecutionState& state, Int128 one, Int128 two);
// https://tc39.es/proposal-temporal/#sec-temporal-adddurations
enum class AddDurationsOperation {
Add,
Subtract
};
ISO8601::Duration addDurations(ExecutionState& state, AddDurationsOperation operation, Value other);
private:
ISO8601::Duration m_duration;
};

View file

@ -459,7 +459,7 @@ TemporalDurationObject* Temporal::toTemporalDuration(ExecutionState& state, cons
// Let result be a new Partial Duration Record with each field set to 0.
// Let partial be ? ToTemporalPartialDurationRecord(item).
auto partial = ISO8601::PartialDuration::toTemporalPartialDurationRecord(state, item);
auto partial = TemporalDurationObject::toTemporalPartialDurationRecord(state, item);
// If partial.[[Years]] is not undefined, set result.[[Years]] to partial.[[Years]].
// If partial.[[Months]] is not undefined, set result.[[Months]] to partial.[[Months]].
// If partial.[[Weeks]] is not undefined, set result.[[Weeks]] to partial.[[Weeks]].
@ -480,6 +480,10 @@ TemporalDurationObject* Temporal::toTemporalDuration(ExecutionState& state, cons
idx++;
}
if (!duration.isValid()) {
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, msg);
}
// Return ? CreateTemporalDuration(result.[[Years]], result.[[Months]], result.[[Weeks]], result.[[Days]], result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]]).
return new TemporalDurationObject(state, duration);
}
@ -1017,6 +1021,14 @@ Int128 Temporal::timeDurationFromEpochNanosecondsDifference(Int128 one, Int128 t
return result;
}
bool Temporal::isCalendarUnit(ISO8601::DateTimeUnit unit)
{
if (unit == ISO8601::DateTimeUnit::Year || unit == ISO8601::DateTimeUnit::Month || unit == ISO8601::DateTimeUnit::Week) {
return true;
}
return false;
}
TemporalObject::TemporalObject(ExecutionState& state)
: TemporalObject(state, state.context()->globalObject()->objectPrototype())
{

View file

@ -372,6 +372,9 @@ public:
// https://tc39.es/proposal-temporal/#sec-temporal-timedurationfromepochnanosecondsdifference
static Int128 timeDurationFromEpochNanosecondsDifference(Int128 one, Int128 two);
// https://tc39.es/proposal-temporal/#sec-temporal-iscalendarunit
static bool isCalendarUnit(ISO8601::DateTimeUnit unit);
};
class TemporalObject : public DerivedObject {

View file

@ -339,6 +339,124 @@ Optional<Duration> Duration::parseDurationString(String* input)
return result;
}
static bool isValidDurationWork(double v, int& sign)
{
// If 𝔽(v) is not finite, return false.
if (!std::isfinite(v)) {
return false;
}
// If v < 0, then
if (v < 0) {
// If sign > 0, return false.
if (sign > 0) {
return false;
}
// Set sign to -1.
sign = -1;
} else if (v > 0) {
// Else if v > 0, then
// If sign < 0, return false.
if (sign < 0) {
return false;
}
// Set sign to 1.
sign = 1;
}
return true;
}
static BigIntData totalDateTimeNanoseconds(const Duration& record)
{
BigIntData resultNs;
constexpr int64_t nanoMultiplier = 1000000000ULL;
constexpr int64_t milliMultiplier = 1000000ULL;
constexpr int64_t microMultiplier = 1000ULL;
{
BigIntData s(record.days());
s = s.multiply(86400);
s = s.multiply(nanoMultiplier);
resultNs = resultNs.addition(s);
}
{
BigIntData s(record.hours());
s = s.multiply(3600);
s = s.multiply(nanoMultiplier);
resultNs = resultNs.addition(s);
}
{
BigIntData s(record.minutes());
s = s.multiply(60);
s = s.multiply(nanoMultiplier);
resultNs = resultNs.addition(s);
}
{
BigIntData s(record.seconds());
s = s.multiply(nanoMultiplier);
resultNs = resultNs.addition(s);
}
{
BigIntData s(record.milliseconds());
s = s.multiply(milliMultiplier);
resultNs = resultNs.addition(s);
}
{
BigIntData s(record.microseconds());
s = s.multiply(microMultiplier);
resultNs = resultNs.addition(s);
}
{
BigIntData s(record.nanoseconds());
resultNs = resultNs.addition(s);
}
return resultNs;
}
// https://tc39.es/ecma402/#sec-isvalidduration
bool Duration::isValid() const
{
// Let sign be 0.
int sign = 0;
// For each value v of « years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds », do
for (double v : m_data) {
if (!isValidDurationWork(v, sign)) {
return false;
}
}
// If abs(years) ≥ 2**32, return false.
if (std::abs(years()) >= (1ULL << 32)) {
return false;
}
// If abs(months) ≥ 2**32, return false.
if (std::abs(months()) >= (1ULL << 32)) {
return false;
}
// If abs(weeks) ≥ 2**32, return false.
if (std::abs(weeks()) >= (1ULL << 32)) {
return false;
}
// Let normalizedSeconds be days × 86,400 + hours × 3600 + minutes × 60 + seconds + (𝔽(milliseconds)) × 10**-3 + (𝔽(microseconds)) × 10**-6 + (𝔽(nanoseconds)) × 10**-9.
// NOTE: The above step cannot be implemented directly using floating-point arithmetic. Multiplying by 10**-3, 10**-6, and 10**-9 respectively may be imprecise when milliseconds, microseconds, or nanoseconds is an unsafe integer. This multiplication can be implemented in C++ with an implementation of std::remquo() with sufficient bits in the quotient. String manipulation will also give an exact result, since the multiplication is by a power of 10.
BigIntData normalizedNanoSeconds = totalDateTimeNanoseconds(*this);
// If abs(normalizedSeconds) ≥ 2**53, return false.
BigIntData limit(int64_t(1ULL << 53));
limit = limit.multiply(1000000000ULL);
if (normalizedNanoSeconds.greaterThanEqual(limit)) {
return false;
}
limit = limit.multiply(-1);
if (normalizedNanoSeconds.lessThanEqual(limit)) {
return false;
}
// Return true.
return true;
}
String* Duration::typeName(ExecutionState& state, ISO8601::DateTimeUnit t)
{
switch (t) {
@ -367,116 +485,46 @@ Int128 Duration::totalNanoseconds(ISO8601::DateTimeUnit unit) const
constexpr int64_t microMultiplier = 1000ULL;
if (unit <= ISO8601::DateTimeUnit::Day) {
Int128 s(days());
Int128 s((int64_t)days());
s *= 86400;
s *= nanoMultiplier;
resultNs += s;
}
if (unit <= ISO8601::DateTimeUnit::Hour) {
Int128 s(hours());
Int128 s((int64_t)hours());
s *= 3600;
s *= nanoMultiplier;
resultNs += s;
}
if (unit <= ISO8601::DateTimeUnit::Minute) {
Int128 s(minutes());
Int128 s((int64_t)minutes());
s *= 60;
s *= nanoMultiplier;
resultNs += s;
}
if (unit <= ISO8601::DateTimeUnit::Second) {
Int128 s(seconds());
Int128 s((int64_t)seconds());
s *= nanoMultiplier;
resultNs += s;
}
if (unit <= ISO8601::DateTimeUnit::Millisecond) {
Int128 s(milliseconds());
Int128 s((int64_t)milliseconds());
s *= milliMultiplier;
resultNs += s;
}
if (unit <= ISO8601::DateTimeUnit::Microsecond) {
Int128 s(microseconds());
Int128 s((int64_t)microseconds());
s *= microMultiplier;
resultNs += s;
}
if (unit <= ISO8601::DateTimeUnit::Nanosecond) {
Int128 s(nanoseconds());
Int128 s((int64_t)nanoseconds());
resultNs += s;
}
return resultNs;
}
PartialDuration PartialDuration::toTemporalPartialDurationRecord(ExecutionState& state, const Value& temporalDurationLike)
{
// If temporalDurationLike is not an Object, then
if (!temporalDurationLike.isObject()) {
// Throw a TypeError exception.
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "toTemporalPartialDurationRecord needs Object");
}
// Let result be a new partial Duration Record with each field set to undefined.
PartialDuration result;
// NOTE: The following steps read properties and perform independent validation in alphabetical order.
// Let days be ? Get(temporalDurationLike, "days").
// If days is not undefined, set result.[[Days]] to ? ToIntegerIfIntegral(days).
// Let hours be ? Get(temporalDurationLike, "hours").
// If hours is not undefined, set result.[[Hours]] to ? ToIntegerIfIntegral(hours).
// Let microseconds be ? Get(temporalDurationLike, "microseconds").
// If microseconds is not undefined, set result.[[Microseconds]] to ? ToIntegerIfIntegral(microseconds).
// Let milliseconds be ? Get(temporalDurationLike, "milliseconds").
// If milliseconds is not undefined, set result.[[Milliseconds]] to ? ToIntegerIfIntegral(milliseconds).
// Let minutes be ? Get(temporalDurationLike, "minutes").
// If minutes is not undefined, set result.[[Minutes]] to ? ToIntegerIfIntegral(minutes).
// Let months be ? Get(temporalDurationLike, "months").
// If months is not undefined, set result.[[Months]] to ? ToIntegerIfIntegral(months).
// Let nanoseconds be ? Get(temporalDurationLike, "nanoseconds").
// If nanoseconds is not undefined, set result.[[Nanoseconds]] to ? ToIntegerIfIntegral(nanoseconds).
// Let seconds be ? Get(temporalDurationLike, "seconds").
// If seconds is not undefined, set result.[[Seconds]] to ? ToIntegerIfIntegral(seconds).
// Let weeks be ? Get(temporalDurationLike, "weeks").
// If weeks is not undefined, set result.[[Weeks]] to ? ToIntegerIfIntegral(weeks).
// Let years be ? Get(temporalDurationLike, "years").
// If years is not undefined, set result.[[Years]] to ? ToIntegerIfIntegral(years).
Object* s = temporalDurationLike.asObject();
Value v;
#define SET_ONCE(name) \
v = s->get(state, ObjectPropertyName(state.context()->staticStrings().lazy##name())).value(state, s); \
if (!v.isUndefined()) { \
result.set##name(v.toIntegerIfIntergral(state)); \
}
SET_ONCE(Days);
SET_ONCE(Hours);
SET_ONCE(Microseconds);
SET_ONCE(Milliseconds);
SET_ONCE(Minutes);
SET_ONCE(Months);
SET_ONCE(Nanoseconds);
SET_ONCE(Seconds);
SET_ONCE(Weeks);
SET_ONCE(Years);
#undef SET_ONCE
// If years is undefined, and months is undefined, and weeks is undefined, and days is undefined, and hours is undefined, and minutes is undefined, and seconds is undefined, and milliseconds is undefined, and microseconds is undefined, and nanoseconds is undefined, throw a TypeError exception.
bool allUndefined = true;
for (const auto& s : result) {
if (s.hasValue()) {
allUndefined = false;
break;
}
}
if (allUndefined) {
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "toTemporalPartialDurationRecord needs at least one duration value");
}
// Return result.
return result;
}
static bool canBeRFC9557Annotation(const ParserString& buffer)
{
// https://tc39.es/proposal-temporal/#sec-temporal-parseisodatetime

View file

@ -225,6 +225,9 @@ public:
return 0;
}
// https://tc39.es/proposal-temporal/#sec-temporal-isvalidduration
bool isValid() const;
// https://tc39.es/proposal-temporal/#sec-temporal-defaulttemporallargestunit
DateTimeUnit defaultTemporalLargestUnit() const
{
@ -261,6 +264,9 @@ public:
return operator[](static_cast<size_t>(idx));
}
std::array<double, 10>::iterator begin() { return m_data.begin(); }
std::array<double, 10>::iterator end() { return m_data.end(); }
std::array<double, 10>::const_iterator begin() const { return m_data.begin(); }
std::array<double, 10>::const_iterator end() const { return m_data.end(); }
@ -279,9 +285,6 @@ class PartialDuration {
std::array<Optional<double>, 10> m_data;
public:
// https://tc39.es/proposal-temporal/#sec-temporal-totemporalpartialdurationrecord
static PartialDuration toTemporalPartialDurationRecord(ExecutionState& state, const Value& temporalDurationLike);
Optional<double> operator[](size_t idx) const
{
return m_data[static_cast<unsigned>(idx)];

View file

@ -182,54 +182,14 @@
<test id="built-ins/Temporal/Duration/from/argument-duration-max"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/from/argument-duration-out-of-range"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/from/argument-duration-precision-exact-numerical-values"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/from/argument-object-invalid"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/from/argument-string-is-infinity"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/from/infinity-throws-rangeerror"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/from/negative-inifinity-throws-rangeerror"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/infinity-throws-rangeerror"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/large-number"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/max"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/mixed"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/negative-infinity-throws-rangeerror"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/out-of-range"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/abs/basic"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/abs/branding"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/abs/builtin"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/abs/length"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/abs/name"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/abs/new-object"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/abs/not-a-constructor"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/abs/prop-desc"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/abs/subclassing-ignored"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/add/argument-duration-max"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/add/argument-duration-out-of-range"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/add/argument-duration-precision-exact-numerical-values"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/add/argument-mixed-sign"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/add/argument-not-object"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/add/argument-string"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/add/argument-string-fractional-units-rounding-mode"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/add/argument-string-negative-fractional-units"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/add/balance-negative-result"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/add/balance-negative-time-units"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/add/basic"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/add/branding"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/add/builtin"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/add/infinity-throws-rangeerror"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/add/length"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/add/name"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/add/nanoseconds-is-number-max-safe-integer"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/add/negative-infinity-throws-rangeerror"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/add/no-calendar-units"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/add/non-integer-throws-rangeerror"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/add/not-a-constructor"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/add/order-of-operations"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/add/precision-exact-mathematical-values"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/add/precision-no-floating-point-loss"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/add/prop-desc"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/add/result-out-of-range-1"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/add/result-out-of-range-2"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/add/result-out-of-range-3"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/add/subclassing-ignored"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/round/accepts-datetime-strings-for-relative-to"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/round/balance-negative-result"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/round/balance-subseconds"><reason>TODO</reason></test>
@ -338,47 +298,6 @@
<test id="built-ins/Temporal/Duration/prototype/round/valid-increments"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/round/year-zero"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/subtract/argument-duration-max"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/subtract/argument-duration-out-of-range"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/subtract/argument-duration-precision-exact-numerical-values"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/subtract/argument-mixed-sign"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/subtract/argument-not-object"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/subtract/argument-string"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/subtract/argument-string-fractional-units-rounding-mode"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/subtract/argument-string-negative-fractional-units"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/subtract/balance-negative-result"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/subtract/balance-negative-time-units"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/subtract/basic"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/subtract/branding"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/subtract/builtin"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/subtract/infinity-throws-rangeerror"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/subtract/length"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/subtract/name"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/subtract/nanoseconds-is-number-max-safe-integer"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/subtract/negative-infinity-throws-rangeerror"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/subtract/no-calendar-units"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/subtract/non-integer-throws-rangeerror"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/subtract/not-a-constructor"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/subtract/order-of-operations"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/subtract/precision-exact-mathematical-values"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/subtract/precision-no-floating-point-loss"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/subtract/prop-desc"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/subtract/result-out-of-range-1"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/subtract/result-out-of-range-2"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/subtract/result-out-of-range-3"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/subtract/subclassing-ignored"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/toJSON/balance-subseconds"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/toJSON/basic"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/toJSON/branding"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/toJSON/builtin"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/toJSON/length"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/toJSON/max-value"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/toJSON/name"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/toJSON/negative-components"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/toJSON/not-a-constructor"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/toJSON/options"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/toJSON/prop-desc"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/toLocaleString/branding"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/toLocaleString/prop-desc"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/toString/smallestunit-plurals-accepted"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/toString/total-of-duration-time-units-out-of-range"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/total/balance-negative-result"><reason>TODO</reason></test>
@ -444,27 +363,6 @@
<test id="built-ins/Temporal/Duration/prototype/total/unit-string-shorthand-string"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/total/unit-wrong-type"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/total/year-zero"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/valueOf/basic"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/valueOf/branding"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/valueOf/prop-desc"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/with/all-negative"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/with/all-positive"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/with/argument-mixed-sign"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/with/branding"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/with/builtin"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/with/copy-properties-not-undefined"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/with/infinity-throws-rangeerror"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/with/length"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/with/name"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/with/negative-infinity-throws-rangeerror"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/with/non-integer-throws-rangeerror"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/with/not-a-constructor"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/with/order-of-operations"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/with/partial-positive"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/with/prop-desc"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/with/sign-conflict-throws-rangeerror"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/with/sign-replace"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Duration/prototype/with/subclassing-ignored"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Instant/compare/argument-zoneddatetime"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Instant/from/argument-zoneddatetime"><reason>TODO</reason></test>
<test id="built-ins/Temporal/Instant/prototype/equals/argument-zoneddatetime"><reason>TODO</reason></test>