mirror of
https://github.com/Samsung/escargot.git
synced 2026-06-22 10:01:50 +00:00
Implement Temporal.Instant.{since, until}, Temporal.duration.{toString, negated}
Signed-off-by: Seonghyun Kim <sh8281.kim@samsung.com>
This commit is contained in:
parent
5711241b99
commit
34c2f0a20e
15 changed files with 1092 additions and 373 deletions
|
|
@ -75,6 +75,24 @@ static Value builtinTemporalDurationFrom(ExecutionState& state, Value thisValue,
|
|||
} \
|
||||
TemporalDurationObject* NAME = thisValue.asObject()->asTemporalDurationObject();
|
||||
|
||||
#define RESOLVE_THIS_BINDING_TO_DURATION2(NAME, BUILT_IN_METHOD) \
|
||||
if (!thisValue.isObject() || !thisValue.asObject()->isTemporalDurationObject()) { \
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, state.context()->staticStrings().lazyCapitalDuration().string(), true, state.context()->staticStrings().BUILT_IN_METHOD.string(), ErrorObject::Messages::GlobalObject_CalledOnIncompatibleReceiver); \
|
||||
} \
|
||||
TemporalDurationObject* NAME = thisValue.asObject()->asTemporalDurationObject();
|
||||
|
||||
static Value builtinTemporalDurationToString(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
RESOLVE_THIS_BINDING_TO_DURATION2(duration, toString);
|
||||
return duration->toString(state, argc ? argv[0] : 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 builtinTemporalInstantConstructor(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
// If NewTarget is undefined, throw a TypeError exception.
|
||||
|
|
@ -201,6 +219,18 @@ static Value builtinTemporalInstantRound(ExecutionState& state, Value thisValue,
|
|||
return instant->round(state, argv[0]);
|
||||
}
|
||||
|
||||
static Value builtinTemporalInstantUntil(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
RESOLVE_THIS_BINDING_TO_INSTANT(instant, Until);
|
||||
return instant->differenceTemporalInstant(state, TemporalInstantObject::DifferenceTemporalInstantOperation::Until, argv[0], argc > 1 ? argv[1] : Value());
|
||||
}
|
||||
|
||||
static Value builtinTemporalInstantSince(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
RESOLVE_THIS_BINDING_TO_INSTANT(instant, Since);
|
||||
return instant->differenceTemporalInstant(state, TemporalInstantObject::DifferenceTemporalInstantOperation::Since, argv[0], argc > 1 ? argv[1] : Value());
|
||||
}
|
||||
|
||||
#define RESOLVE_THIS_BINDING_TO_PLAINDATE(NAME, BUILT_IN_METHOD) \
|
||||
if (!thisValue.isObject() || !thisValue.asObject()->isTemporalPlainDateObject()) { \
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, state.context()->staticStrings().lazyCapitalPlainDate().string(), true, state.context()->staticStrings().lazy##BUILT_IN_METHOD().string(), ErrorObject::Messages::GlobalObject_CalledOnIncompatibleReceiver); \
|
||||
|
|
@ -371,6 +401,11 @@ void GlobalObject::installTemporal(ExecutionState& state)
|
|||
m_temporalDurationPrototype->directDefineOwnProperty(state, ObjectPropertyName(state.context()->vmInstance()->globalSymbols().toStringTag),
|
||||
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->lazyNegated()), ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->lazyNegated(), builtinTemporalDurationNegated, 0, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
||||
|
||||
#define DEFINE_DURATION_PROTOTYPE_GETTER_PROPERTY(name, stringName, Name) \
|
||||
{ \
|
||||
AtomicString name(state.context(), "get " stringName); \
|
||||
|
|
@ -445,6 +480,8 @@ void GlobalObject::installTemporal(ExecutionState& state)
|
|||
m_temporalInstantPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->toJSON), ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->toJSON, builtinTemporalInstantToJSON, 0, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
m_temporalInstantPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->toLocaleString), ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->toLocaleString, builtinTemporalInstantToLocaleString, 0, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
m_temporalInstantPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->round), ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->round, builtinTemporalInstantRound, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
m_temporalInstantPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->lazyUntil()), ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->lazyUntil(), builtinTemporalInstantUntil, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
m_temporalInstantPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->lazySince()), ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->lazySince(), builtinTemporalInstantSince, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
||||
{
|
||||
auto getter = new NativeFunctionObject(state, NativeFunctionInfo(strings->lazyGetEpochMilliseconds(), builtinTemporalInstantGetEpochMilliseconds, 0, NativeFunctionInfo::Strict));
|
||||
|
|
|
|||
|
|
@ -2884,6 +2884,17 @@ Value Intl::supportedLocales(ExecutionState& state, const Vector<String*, GCUtil
|
|||
return Object::createArrayFromList(state, subset);
|
||||
}
|
||||
|
||||
Optional<Object*> Intl::getOptionsObject(ExecutionState& state, const Value& options)
|
||||
{
|
||||
Optional<Object*> resolvedOptions;
|
||||
if (options.isObject()) {
|
||||
resolvedOptions = options.asObject();
|
||||
} else if (!options.isUndefined()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "Invalid options value");
|
||||
}
|
||||
return resolvedOptions;
|
||||
}
|
||||
|
||||
Value Intl::getOption(ExecutionState& state, Object* options, Value property, Intl::OptionValueType type, Value* values, size_t valuesLength, const Value& fallback)
|
||||
{
|
||||
// http://www.ecma-international.org/ecma-402/1.0/index.html#sec-9.2.9
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ public:
|
|||
static ValueVector canonicalizeLocaleList(ExecutionState& state, Value locales);
|
||||
static StringMap resolveLocale(ExecutionState& state, const Vector<String*, GCUtil::gc_malloc_allocator<String*>>& availableLocales, const ValueVector& requestedLocales, const StringMap& options, const char* const relevantExtensionKeys[], size_t relevantExtensionKeyCount, LocaleDataImplFunction localeData);
|
||||
static Value supportedLocales(ExecutionState& state, const Vector<String*, GCUtil::gc_malloc_allocator<String*>>& availableLocales, const ValueVector& requestedLocales, Value options);
|
||||
static Optional<Object*> getOptionsObject(ExecutionState& state, const Value& options); // https://tc39.es/proposal-temporal/#sec-getoptionsobject
|
||||
enum OptionValueType {
|
||||
StringValue,
|
||||
BooleanValue,
|
||||
|
|
|
|||
|
|
@ -588,20 +588,20 @@ inline double purifyNaN(double value)
|
|||
return value;
|
||||
}
|
||||
|
||||
static std::string buildDecimalFormat(DurationRecord::Type unit, Int128 ns)
|
||||
static std::string buildDecimalFormat(ISO8601::DateTimeUnit unit, Int128 ns)
|
||||
{
|
||||
ASSERT(unit == DurationRecord::Type::Seconds || unit == DurationRecord::Type::Milliseconds || unit == DurationRecord::Type::Microseconds);
|
||||
ASSERT(unit == ISO8601::DateTimeUnit::Second || unit == ISO8601::DateTimeUnit::Millisecond || unit == ISO8601::DateTimeUnit::Microsecond);
|
||||
|
||||
int flactionalDigits = 0;
|
||||
int64_t exponent = 0;
|
||||
if (unit == DurationRecord::Type::Seconds) {
|
||||
if (unit == ISO8601::DateTimeUnit::Second) {
|
||||
flactionalDigits = 9;
|
||||
exponent = 1000000000;
|
||||
} else if (unit == DurationRecord::Type::Milliseconds) {
|
||||
} else if (unit == ISO8601::DateTimeUnit::Millisecond) {
|
||||
flactionalDigits = 6;
|
||||
exponent = 1000000;
|
||||
} else {
|
||||
ASSERT(unit == DurationRecord::Type::Microseconds);
|
||||
ASSERT(unit == ISO8601::DateTimeUnit::Microsecond);
|
||||
flactionalDigits = 3;
|
||||
exponent = 1000;
|
||||
}
|
||||
|
|
@ -681,7 +681,7 @@ std::vector<IntlDurationFormatObject::Element> IntlDurationFormatObject::collect
|
|||
std::vector<Element> elements;
|
||||
Optional<DurationSignType> durationSign;
|
||||
for (uint8_t index = 0; index < 10 && !done; ++index) {
|
||||
DurationRecord::Type unit = static_cast<DurationRecord::Type>(index);
|
||||
ISO8601::DateTimeUnit unit = static_cast<ISO8601::DateTimeUnit>(index);
|
||||
auto unitData = durationFormat->data(index);
|
||||
double value = duration[unit];
|
||||
Optional<Int128> totalNanosecondsValue;
|
||||
|
|
@ -690,27 +690,27 @@ std::vector<IntlDurationFormatObject::Element> IntlDurationFormatObject::collect
|
|||
|
||||
switch (unit) {
|
||||
// 3.j. If unit is "seconds", "milliseconds", or "microseconds", then
|
||||
case DurationRecord::Type::Seconds:
|
||||
case DurationRecord::Type::Milliseconds:
|
||||
case DurationRecord::Type::Microseconds: {
|
||||
case ISO8601::DateTimeUnit::Second:
|
||||
case ISO8601::DateTimeUnit::Millisecond:
|
||||
case ISO8601::DateTimeUnit::Microsecond: {
|
||||
skeletonBuilder = u"rounding-mode-down";
|
||||
String* nextStyle = state.context()->staticStrings().lazyLong().string();
|
||||
if (unit == DurationRecord::Type::Seconds)
|
||||
nextStyle = durationFormat->data(static_cast<unsigned>(DurationRecord::Type::Milliseconds)).first;
|
||||
else if (unit == DurationRecord::Type::Milliseconds)
|
||||
nextStyle = durationFormat->data(static_cast<unsigned>(DurationRecord::Type::Microseconds)).first;
|
||||
if (unit == ISO8601::DateTimeUnit::Second)
|
||||
nextStyle = durationFormat->data(static_cast<unsigned>(ISO8601::DateTimeUnit::Millisecond)).first;
|
||||
else if (unit == ISO8601::DateTimeUnit::Millisecond)
|
||||
nextStyle = durationFormat->data(static_cast<unsigned>(ISO8601::DateTimeUnit::Microsecond)).first;
|
||||
else {
|
||||
ASSERT(unit == DurationRecord::Type::Microseconds);
|
||||
nextStyle = durationFormat->data(static_cast<unsigned>(DurationRecord::Type::Nanoseconds)).first;
|
||||
ASSERT(unit == ISO8601::DateTimeUnit::Microsecond);
|
||||
nextStyle = durationFormat->data(static_cast<unsigned>(ISO8601::DateTimeUnit::Nanosecond)).first;
|
||||
}
|
||||
if (nextStyle->equals("numeric")) {
|
||||
if (unit == DurationRecord::Type::Seconds) {
|
||||
totalNanosecondsValue = duration.totalNanoseconds(DurationRecord::Type::Seconds);
|
||||
} else if (unit == DurationRecord::Type::Milliseconds) {
|
||||
totalNanosecondsValue = duration.totalNanoseconds(DurationRecord::Type::Milliseconds);
|
||||
if (unit == ISO8601::DateTimeUnit::Second) {
|
||||
totalNanosecondsValue = duration.totalNanoseconds(ISO8601::DateTimeUnit::Second);
|
||||
} else if (unit == ISO8601::DateTimeUnit::Millisecond) {
|
||||
totalNanosecondsValue = duration.totalNanoseconds(ISO8601::DateTimeUnit::Millisecond);
|
||||
} else {
|
||||
ASSERT(unit == DurationRecord::Type::Microseconds);
|
||||
totalNanosecondsValue = duration.totalNanoseconds(DurationRecord::Type::Microseconds);
|
||||
ASSERT(unit == ISO8601::DateTimeUnit::Microsecond);
|
||||
totalNanosecondsValue = duration.totalNanoseconds(ISO8601::DateTimeUnit::Microsecond);
|
||||
}
|
||||
ASSERT(totalNanosecondsValue);
|
||||
|
||||
|
|
@ -807,7 +807,7 @@ std::vector<IntlDurationFormatObject::Element> IntlDurationFormatObject::collect
|
|||
|
||||
auto formatIntl128AsDecimal = [&](const UTF16StringDataNonGCStd& skeleton) -> LocalResourcePointer<UFormattedNumber> {
|
||||
ASSERT(totalNanosecondsValue);
|
||||
ASSERT(unit == DurationRecord::Type::Seconds || unit == DurationRecord::Type::Milliseconds || unit == DurationRecord::Type::Microseconds);
|
||||
ASSERT(unit == ISO8601::DateTimeUnit::Second || unit == ISO8601::DateTimeUnit::Millisecond || unit == ISO8601::DateTimeUnit::Microsecond);
|
||||
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
LocalResourcePointer<UNumberFormatter> numberFormatter(unumf_openForSkeletonAndLocale(skeleton.data(), skeleton.length(), durationFormat->m_dataLocaleWithExtensions->toNonGCUTF8StringData().data(), &status),
|
||||
|
|
@ -839,19 +839,19 @@ std::vector<IntlDurationFormatObject::Element> IntlDurationFormatObject::collect
|
|||
// 3.l.i. If style is "2-digit" or "numeric", then
|
||||
if (style->equals("2-digit") || style->equals("numeric")) {
|
||||
// https://tc39.es/proposal-intl-duration-format/#sec-formatnumericunits
|
||||
ASSERT(unit == DurationRecord::Type::Hours || unit == DurationRecord::Type::Minutes || unit == DurationRecord::Type::Seconds);
|
||||
ASSERT(unit == ISO8601::DateTimeUnit::Hour || unit == ISO8601::DateTimeUnit::Minute || unit == ISO8601::DateTimeUnit::Second);
|
||||
|
||||
double secondsValue = duration[DurationRecord::Type::Seconds];
|
||||
if (durationFormat->data(static_cast<unsigned>(DurationRecord::Type::Milliseconds)).first->equals("numeric")) {
|
||||
secondsValue = secondsValue + duration[DurationRecord::Type::Milliseconds] / 1000.0 + duration[DurationRecord::Type::Microseconds] / 1000000.0 + duration[DurationRecord::Type::Nanoseconds] / 1000000000.0;
|
||||
double secondsValue = duration[ISO8601::DateTimeUnit::Second];
|
||||
if (durationFormat->data(static_cast<unsigned>(ISO8601::DateTimeUnit::Millisecond)).first->equals("numeric")) {
|
||||
secondsValue = secondsValue + duration[ISO8601::DateTimeUnit::Millisecond] / 1000.0 + duration[ISO8601::DateTimeUnit::Microsecond] / 1000000.0 + duration[ISO8601::DateTimeUnit::Nanosecond] / 1000000000.0;
|
||||
}
|
||||
|
||||
bool needsFormatHours = duration[DurationRecord::Type::Hours] || !durationFormat->data(static_cast<unsigned>(DurationRecord::Type::Hours)).second->equals("auto");
|
||||
bool needsFormatSeconds = secondsValue || !durationFormat->data(static_cast<unsigned>(DurationRecord::Type::Seconds)).second->equals("auto");
|
||||
bool needsFormatMinutes = (needsFormatHours && needsFormatSeconds) || duration[DurationRecord::Type::Minutes] || !durationFormat->data(static_cast<unsigned>(DurationRecord::Type::Minutes)).second->equals("auto");
|
||||
bool needsFormatHours = duration[ISO8601::DateTimeUnit::Hour] || !durationFormat->data(static_cast<unsigned>(ISO8601::DateTimeUnit::Hour)).second->equals("auto");
|
||||
bool needsFormatSeconds = secondsValue || !durationFormat->data(static_cast<unsigned>(ISO8601::DateTimeUnit::Second)).second->equals("auto");
|
||||
bool needsFormatMinutes = (needsFormatHours && needsFormatSeconds) || duration[ISO8601::DateTimeUnit::Minute] || !durationFormat->data(static_cast<unsigned>(ISO8601::DateTimeUnit::Minute)).second->equals("auto");
|
||||
|
||||
bool needsFormat = (unit == DurationRecord::Type::Hours && needsFormatHours) || (unit == DurationRecord::Type::Minutes && needsFormatMinutes) || (unit == DurationRecord::Type::Seconds && needsFormatSeconds);
|
||||
bool needsSeparator = (unit == DurationRecord::Type::Hours && needsFormatHours && needsFormatMinutes) || (unit == DurationRecord::Type::Minutes && needsFormatSeconds);
|
||||
bool needsFormat = (unit == ISO8601::DateTimeUnit::Hour && needsFormatHours) || (unit == ISO8601::DateTimeUnit::Minute && needsFormatMinutes) || (unit == ISO8601::DateTimeUnit::Second && needsFormatSeconds);
|
||||
bool needsSeparator = (unit == ISO8601::DateTimeUnit::Hour && needsFormatHours && needsFormatMinutes) || (unit == ISO8601::DateTimeUnit::Minute && needsFormatSeconds);
|
||||
|
||||
if (needsFormat) {
|
||||
adjustSignDisplay();
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ protected:
|
|||
struct Element {
|
||||
ElementType m_type;
|
||||
bool m_valueSignBit;
|
||||
DurationRecord::Type m_unit;
|
||||
ISO8601::DateTimeUnit m_unit;
|
||||
UTF16StringDataNonGCStd m_string;
|
||||
LocalResourcePointer<UFormattedNumber> m_formattedNumber;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -983,18 +983,22 @@ namespace Escargot {
|
|||
F(InLeapYear, "inLeapYear") \
|
||||
F(Instant, "instant") \
|
||||
F(ISO8601, "iso8601") \
|
||||
F(LargestUnit, "largestUnit") \
|
||||
F(MonthCode, "monthCode") \
|
||||
F(MonthsInYear, "monthsInYear") \
|
||||
F(Negated, "negated") \
|
||||
F(Overflow, "overflow") \
|
||||
F(PlainDateISO, "plainDateISO") \
|
||||
F(PlainDateTimeISO, "plainDateTimeISO") \
|
||||
F(PlainTimeISO, "plainTimeISO") \
|
||||
F(Since, "since") \
|
||||
F(SmallestUnit, "smallestUnit") \
|
||||
F(TemporalDotDuration, "Temporal.Duration") \
|
||||
F(TemporalDotInstant, "Temporal.Instant") \
|
||||
F(TemporalDotNow, "Temporal.Now") \
|
||||
F(TemporalDotPlainDate, "Temporal.PlainDate") \
|
||||
F(TimeZoneId, "timeZoneId") \
|
||||
F(Until, "until") \
|
||||
F(WeekOfYear, "weekOfYear") \
|
||||
F(YearOfWeek, "yearOfWeek") \
|
||||
F(ZonedDateTimeISO, "zonedDateTimeISO")
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
#if defined(ENABLE_TEMPORAL)
|
||||
/*
|
||||
* Copyright (C) 2021 Sony Interactive Entertainment Inc.
|
||||
* Copyright (C) 2021-2023 Apple Inc. All rights reserved.
|
||||
* Copyright (c) 2025-present Samsung Electronics Co., Ltd
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
|
|
@ -58,6 +60,310 @@ TemporalDurationObject::TemporalDurationObject(ExecutionState& state, const ISO8
|
|||
{
|
||||
}
|
||||
|
||||
ISO8601::Duration TemporalDurationObject::temporalDurationFromInternal(ExecutionState& state, ISO8601::InternalDuration internalDuration, ISO8601::DateTimeUnit largestUnit)
|
||||
{
|
||||
// Let days, hours, minutes, seconds, milliseconds, and microseconds be 0.
|
||||
double days = 0, hours = 0, minutes = 0, seconds = 0;
|
||||
Int128 milliseconds = 0, microseconds = 0;
|
||||
|
||||
// Let sign be TimeDurationSign(internalDuration.[[Time]]).
|
||||
int32_t sign = internalDuration.sign();
|
||||
|
||||
// Let nanoseconds be abs(internalDuration.[[Time]]).
|
||||
auto nanoseconds = std::abs(internalDuration.time());
|
||||
|
||||
// If TemporalUnitCategory(largestUnit) is date, then
|
||||
if (ISO8601::toDateTimeCategory(largestUnit) == ISO8601::DateTimeUnitCategory::Date) {
|
||||
// Set microseconds to floor(nanoseconds / 1000).
|
||||
// Set nanoseconds to nanoseconds modulo 1000.
|
||||
// Set milliseconds to floor(microseconds / 1000).
|
||||
// Set microseconds to microseconds modulo 1000.
|
||||
// Set seconds to floor(milliseconds / 1000).
|
||||
// Set milliseconds to milliseconds modulo 1000.
|
||||
// Set minutes to floor(seconds / 60).
|
||||
// Set seconds to seconds modulo 60.
|
||||
// Set hours to floor(minutes / 60).
|
||||
// Set minutes to minutes modulo 60.
|
||||
// Set days to floor(hours / 24).
|
||||
// Set hours to hours modulo 24.
|
||||
microseconds = nanoseconds / 1000;
|
||||
nanoseconds = nanoseconds % 1000;
|
||||
milliseconds = microseconds / 1000;
|
||||
microseconds = microseconds % 1000;
|
||||
seconds = (double)(milliseconds / 1000);
|
||||
milliseconds = milliseconds % 1000;
|
||||
minutes = std::trunc(seconds / 60);
|
||||
seconds = std::fmod(seconds, 60);
|
||||
hours = std::trunc(minutes / 60);
|
||||
minutes = std::fmod(minutes, 60);
|
||||
days = std::trunc(hours / 24);
|
||||
hours = std::fmod(hours, 24);
|
||||
} else if (largestUnit == ISO8601::DateTimeUnit::Hour) {
|
||||
// Else if largestUnit is hour, then
|
||||
// Set microseconds to floor(nanoseconds / 1000).
|
||||
// Set nanoseconds to nanoseconds modulo 1000.
|
||||
// Set milliseconds to floor(microseconds / 1000).
|
||||
// Set microseconds to microseconds modulo 1000.
|
||||
// Set seconds to floor(milliseconds / 1000).
|
||||
// Set milliseconds to milliseconds modulo 1000.
|
||||
// Set minutes to floor(seconds / 60).
|
||||
// Set seconds to seconds modulo 60.
|
||||
// Set hours to floor(minutes / 60).
|
||||
// Set minutes to minutes modulo 60.
|
||||
microseconds = nanoseconds / 1000;
|
||||
nanoseconds = nanoseconds % 1000;
|
||||
milliseconds = microseconds / 1000;
|
||||
microseconds = microseconds % 1000;
|
||||
seconds = (double)(milliseconds / 1000);
|
||||
milliseconds = milliseconds % 1000;
|
||||
minutes = std::trunc(seconds / 60);
|
||||
seconds = std::fmod(seconds, 60);
|
||||
hours = std::trunc(minutes / 60);
|
||||
minutes = std::fmod(minutes, 60);
|
||||
} else if (largestUnit == ISO8601::DateTimeUnit::Minute) {
|
||||
// Else if largestUnit is minute, then
|
||||
// Set microseconds to floor(nanoseconds / 1000).
|
||||
// Set nanoseconds to nanoseconds modulo 1000.
|
||||
// Set milliseconds to floor(microseconds / 1000).
|
||||
// Set microseconds to microseconds modulo 1000.
|
||||
// Set seconds to floor(milliseconds / 1000).
|
||||
// Set milliseconds to milliseconds modulo 1000.
|
||||
// Set minutes to floor(seconds / 60).
|
||||
// Set seconds to seconds modulo 60.
|
||||
microseconds = nanoseconds / 1000;
|
||||
nanoseconds = nanoseconds % 1000;
|
||||
milliseconds = microseconds / 1000;
|
||||
microseconds = microseconds % 1000;
|
||||
seconds = (double)(milliseconds / 1000);
|
||||
milliseconds = milliseconds % 1000;
|
||||
minutes = std::trunc(seconds / 60);
|
||||
seconds = std::fmod(seconds, 60);
|
||||
} else if (largestUnit == ISO8601::DateTimeUnit::Second) {
|
||||
// Else if largestUnit is second, then
|
||||
// Set microseconds to floor(nanoseconds / 1000).
|
||||
// Set nanoseconds to nanoseconds modulo 1000.
|
||||
// Set milliseconds to floor(microseconds / 1000).
|
||||
// Set microseconds to microseconds modulo 1000.
|
||||
// Set seconds to floor(milliseconds / 1000).
|
||||
// Set milliseconds to milliseconds modulo 1000.
|
||||
microseconds = nanoseconds / 1000;
|
||||
nanoseconds = nanoseconds % 1000;
|
||||
milliseconds = microseconds / 1000;
|
||||
microseconds = microseconds % 1000;
|
||||
seconds = (double)(milliseconds / 1000);
|
||||
milliseconds = milliseconds % 1000;
|
||||
} else if (largestUnit == ISO8601::DateTimeUnit::Millisecond) {
|
||||
// Else if largestUnit is millisecond, then
|
||||
// Set microseconds to floor(nanoseconds / 1000).
|
||||
// Set nanoseconds to nanoseconds modulo 1000.
|
||||
// Set milliseconds to floor(microseconds / 1000).
|
||||
// Set microseconds to microseconds modulo 1000.
|
||||
microseconds = nanoseconds / 1000;
|
||||
nanoseconds = nanoseconds % 1000;
|
||||
milliseconds = microseconds / 1000;
|
||||
microseconds = microseconds % 1000;
|
||||
} else if (largestUnit == ISO8601::DateTimeUnit::Microsecond) {
|
||||
// Else if largestUnit is microsecond, then
|
||||
// Set microseconds to floor(nanoseconds / 1000).
|
||||
// Set nanoseconds to nanoseconds modulo 1000.
|
||||
microseconds = nanoseconds / 1000;
|
||||
nanoseconds = nanoseconds % 1000;
|
||||
} else {
|
||||
// Assert: largestUnit is nanosecond.
|
||||
}
|
||||
|
||||
// Return ? CreateTemporalDuration(internalDuration.[[Date]].[[Years]], internalDuration.[[Date]].[[Months]],
|
||||
// internalDuration.[[Date]].[[Weeks]], internalDuration.[[Date]].[[Days]] + days × sign,
|
||||
// hours × sign, minutes × sign, seconds × sign, milliseconds × sign, microseconds × sign, nanoseconds × sign).
|
||||
if (hours) {
|
||||
hours *= sign;
|
||||
}
|
||||
if (minutes) {
|
||||
minutes *= sign;
|
||||
}
|
||||
if (seconds) {
|
||||
seconds *= sign;
|
||||
}
|
||||
if (milliseconds) {
|
||||
milliseconds *= sign;
|
||||
}
|
||||
if (microseconds) {
|
||||
microseconds *= sign;
|
||||
}
|
||||
if (nanoseconds) {
|
||||
nanoseconds *= sign;
|
||||
}
|
||||
|
||||
return ISO8601::Duration{ internalDuration.dateDuration().years(), internalDuration.dateDuration().months(), internalDuration.dateDuration().weeks(), internalDuration.dateDuration().days() + days * sign, hours, minutes,
|
||||
static_cast<double>(seconds), static_cast<double>(milliseconds), static_cast<double>(microseconds), static_cast<double>(nanoseconds) };
|
||||
}
|
||||
|
||||
ISO8601::Duration TemporalDurationObject::createNegatedTemporalDuration(ISO8601::Duration duration)
|
||||
{
|
||||
// Return ! CreateTemporalDuration(-duration.[[Years]], -duration.[[Months]], -duration.[[Weeks]], -duration.[[Days]],
|
||||
// -duration.[[Hours]], -duration.[[Minutes]], -duration.[[Seconds]], -duration.[[Milliseconds]], -duration.[[Microseconds]], -duration.[[Nanoseconds]]).
|
||||
for (size_t i = 0; i < 10; i++) {
|
||||
// avoiding -0.0
|
||||
if (duration[i]) {
|
||||
duration[i] = -duration[i];
|
||||
}
|
||||
}
|
||||
return duration;
|
||||
}
|
||||
|
||||
static void appendInteger(StringBuilder& builder, double value)
|
||||
{
|
||||
ASSERT(std::isfinite(value));
|
||||
|
||||
auto absValue = std::abs(value);
|
||||
if (absValue <= 9007199254740991.0 /* MAX_SAFE_INTEGER */) {
|
||||
builder.appendString(String::fromDouble(absValue));
|
||||
return;
|
||||
}
|
||||
|
||||
BigIntData bi(absValue);
|
||||
std::string s = bi.toNonGCStdString();
|
||||
builder.appendString(String::fromASCII(s.data(), s.length()));
|
||||
}
|
||||
|
||||
static void appendInteger(StringBuilder& builder, Int128 value)
|
||||
{
|
||||
std::string s = std::to_string(std::abs(value));
|
||||
builder.appendString(String::fromASCII(s.data(), s.length()));
|
||||
}
|
||||
|
||||
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);
|
||||
balancedMicroseconds = balancedMicroseconds % 1000;
|
||||
auto balancedSeconds = static_cast<Int128>(duration.seconds()) + (balancedMilliseconds / 1000);
|
||||
balancedMilliseconds = balancedMilliseconds % 1000;
|
||||
|
||||
StringBuilder builder;
|
||||
|
||||
auto sign = duration.sign();
|
||||
if (sign < 0)
|
||||
builder.appendChar('-');
|
||||
|
||||
builder.appendChar('P');
|
||||
if (duration.years()) {
|
||||
appendInteger(builder, duration.years());
|
||||
builder.appendChar('Y');
|
||||
}
|
||||
if (duration.months()) {
|
||||
appendInteger(builder, duration.months());
|
||||
builder.appendChar('M');
|
||||
}
|
||||
if (duration.weeks()) {
|
||||
appendInteger(builder, duration.weeks());
|
||||
builder.appendChar('W');
|
||||
}
|
||||
if (duration.days()) {
|
||||
appendInteger(builder, duration.days());
|
||||
builder.appendChar('D');
|
||||
}
|
||||
|
||||
// The zero value is displayed in seconds.
|
||||
auto usesSeconds = balancedSeconds || balancedMilliseconds || balancedMicroseconds || balancedNanoseconds || !sign || !precision.isString() || !precision.asString()->equals("auto");
|
||||
if (!duration.hours() && !duration.minutes() && !usesSeconds)
|
||||
return builder.finalize();
|
||||
|
||||
builder.appendChar('T');
|
||||
if (duration.hours()) {
|
||||
appendInteger(builder, duration.hours());
|
||||
builder.appendChar('H');
|
||||
}
|
||||
if (duration.minutes()) {
|
||||
appendInteger(builder, duration.minutes());
|
||||
builder.appendChar('M');
|
||||
}
|
||||
if (usesSeconds) {
|
||||
appendInteger(builder, balancedSeconds);
|
||||
|
||||
// Int128 fraction = static_cast<int64_t>(std::abs(balancedMilliseconds) * 1e6 + std::abs(balancedMicroseconds) * 1e3 + std::abs(balancedNanoseconds));
|
||||
Int128 fraction = std::abs(balancedMilliseconds) * 1000000 + std::abs(balancedMicroseconds) * 1000 + std::abs(balancedNanoseconds);
|
||||
Temporal::formatSecondsStringFraction(builder, static_cast<int64_t>(fraction), precision);
|
||||
|
||||
builder.appendChar('S');
|
||||
}
|
||||
|
||||
return builder.finalize();
|
||||
}
|
||||
|
||||
String* TemporalDurationObject::toString(ExecutionState& state, Value options)
|
||||
{
|
||||
// Let duration be the this value.
|
||||
// Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
|
||||
// Let resolvedOptions be ? GetOptionsObject(options).
|
||||
auto resolvedOptions = Intl::getOptionsObject(state, options);
|
||||
// NOTE: The following steps read options and perform independent validation in alphabetical order (GetTemporalFractionalSecondDigitsOption reads "fractionalSecondDigits" and GetRoundingModeOption reads "roundingMode").
|
||||
// Let digits be ? GetTemporalFractionalSecondDigitsOption(resolvedOptions).
|
||||
auto digits = Temporal::getTemporalFractionalSecondDigitsOption(state, resolvedOptions);
|
||||
// Let roundingMode be ? GetRoundingModeOption(resolvedOptions, trunc).
|
||||
auto roundingMode = Temporal::getRoundingModeOption(state, resolvedOptions, state.context()->staticStrings().trunc.string());
|
||||
// Let smallestUnit be ? GetTemporalUnitValuedOption(resolvedOptions, "smallestUnit", unset).
|
||||
auto smallestUnit = Temporal::getTemporalUnitValuedOption(state, resolvedOptions, state.context()->staticStrings().lazySmallestUnit().string(), NullOption);
|
||||
// Perform ? ValidateTemporalUnitValue(smallestUnit, time).
|
||||
Temporal::validateTemporalUnitValue(state, smallestUnit, ISO8601::DateTimeUnitCategory::Time, nullptr, 0);
|
||||
// If smallestUnit is hour or minute, throw a RangeError exception.
|
||||
if (smallestUnit && (smallestUnit.value() == TemporalUnit::Hour || smallestUnit.value() == TemporalUnit::Minute)) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, "Invalid smallestUnit value");
|
||||
}
|
||||
// Let precision be ToSecondsStringPrecisionRecord(smallestUnit, digits).
|
||||
auto precision = Temporal::toSecondsStringPrecisionRecord(state, toDateTimeUnit(smallestUnit), digits);
|
||||
// If precision.[[Unit]] is nanosecond and precision.[[Increment]] = 1, then
|
||||
if (precision.unit == ISO8601::DateTimeUnit::Nanosecond && precision.increment == 1) {
|
||||
// Return TemporalDurationToString(duration, precision.[[Precision]]).
|
||||
return temporalDurationToString(m_duration, precision.precision);
|
||||
}
|
||||
|
||||
// Let largestUnit be DefaultTemporalLargestUnit(duration).
|
||||
auto largestUnit = m_duration.defaultTemporalLargestUnit();
|
||||
// Let internalDuration be ToInternalDurationRecord(duration).
|
||||
ISO8601::InternalDuration internalDuration = toInternalDurationRecord(m_duration);
|
||||
// Let timeDuration be ? RoundTimeDuration(internalDuration.[[Time]], precision.[[Increment]], precision.[[Unit]], roundingMode).
|
||||
auto timeDuration = roundTimeDuration(state, internalDuration.time(), precision.increment, precision.unit, roundingMode);
|
||||
// Set internalDuration to CombineDateAndTimeDuration(internalDuration.[[Date]], timeDuration).
|
||||
internalDuration = ISO8601::InternalDuration::combineDateAndTimeDuration(internalDuration.dateDuration(), timeDuration);
|
||||
// Let roundedLargestUnit be LargerOfTwoTemporalUnits(largestUnit, second).
|
||||
auto roundedLargestUnit = Temporal::largerOfTwoTemporalUnits(largestUnit, ISO8601::DateTimeUnit::Second);
|
||||
// Let roundedDuration be ? TemporalDurationFromInternal(internalDuration, roundedLargestUnit).
|
||||
auto roundedDuration = temporalDurationFromInternal(state, internalDuration, roundedLargestUnit);
|
||||
// Return TemporalDurationToString(roundedDuration, precision.[[Precision]]).
|
||||
return temporalDurationToString(roundedDuration, precision.precision);
|
||||
}
|
||||
|
||||
ISO8601::InternalDuration TemporalDurationObject::toInternalDurationRecord(ISO8601::Duration duration)
|
||||
{
|
||||
ISO8601::Duration dateDuration{
|
||||
duration[0], duration[1], duration[2], duration[3], 0.0, 0.0, 0.0, 0.0, 0.0, 0.0
|
||||
};
|
||||
auto t = duration.totalNanoseconds(ISO8601::DateTimeUnit::Hour);
|
||||
return ISO8601::InternalDuration(dateDuration, t);
|
||||
}
|
||||
|
||||
Int128 TemporalDurationObject::roundTimeDuration(ExecutionState& state, Int128 timeDuration, unsigned increment, ISO8601::DateTimeUnit unit, ISO8601::RoundingMode roundingMode)
|
||||
{
|
||||
// Let divisor be the value in the "Length in Nanoseconds" column of the row of Table 21 whose "Value" column contains unit.
|
||||
Int128 divisor = ISO8601::lengthInNanoseconds(unit);
|
||||
// Return ? RoundTimeDurationToIncrement(timeDuration, divisor × increment, roundingMode)
|
||||
return TemporalDurationObject::roundTimeDurationToIncrement(state, timeDuration, divisor * increment, roundingMode);
|
||||
}
|
||||
|
||||
Int128 TemporalDurationObject::roundTimeDurationToIncrement(ExecutionState& state, Int128 d, Int128 increment, ISO8601::RoundingMode roundingMode)
|
||||
{
|
||||
// Let rounded be RoundNumberToIncrement(d, increment, roundingMode).
|
||||
Int128 rounded = ISO8601::roundNumberToIncrement(d, increment, roundingMode);
|
||||
// 2. If abs(rounded) > maxTimeDuration, throw a RangeError exception.
|
||||
if (std::abs(rounded) > ISO8601::InternalDuration::maxTimeDuration) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, "Rounded time duration exceeds maximum");
|
||||
}
|
||||
// 3. Return rounded.
|
||||
return rounded;
|
||||
}
|
||||
|
||||
} // namespace Escargot
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -35,6 +35,12 @@ public:
|
|||
|
||||
TemporalDurationObject(ExecutionState& state, const ISO8601::Duration& duration);
|
||||
|
||||
// https://tc39.es/proposal-temporal/#sec-temporal-temporaldurationfrominternal
|
||||
static ISO8601::Duration temporalDurationFromInternal(ExecutionState& state, ISO8601::InternalDuration internalDuration, ISO8601::DateTimeUnit largestUnit);
|
||||
|
||||
// https://tc39.es/proposal-temporal/#sec-temporal-createnegatedtemporalduration
|
||||
static ISO8601::Duration createNegatedTemporalDuration(ISO8601::Duration duration);
|
||||
|
||||
virtual bool isTemporalDurationObject() const override
|
||||
{
|
||||
return true;
|
||||
|
|
@ -50,7 +56,7 @@ public:
|
|||
return m_duration[static_cast<unsigned>(idx)];
|
||||
}
|
||||
|
||||
double operator[](ISO8601::Duration::Type idx) const
|
||||
double operator[](ISO8601::DateTimeUnit idx) const
|
||||
{
|
||||
return operator[](static_cast<size_t>(idx));
|
||||
}
|
||||
|
|
@ -60,7 +66,7 @@ public:
|
|||
return m_duration[static_cast<unsigned>(idx)];
|
||||
}
|
||||
|
||||
double& operator[](ISO8601::Duration::Type idx)
|
||||
double& operator[](ISO8601::DateTimeUnit idx)
|
||||
{
|
||||
return operator[](static_cast<size_t>(idx));
|
||||
}
|
||||
|
|
@ -68,17 +74,20 @@ public:
|
|||
// https://tc39.es/proposal-temporal/#sec-durationsign
|
||||
int sign() const
|
||||
{
|
||||
for (const auto& v : m_duration) {
|
||||
if (v < 0) {
|
||||
return -1;
|
||||
}
|
||||
if (v > 0) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
return m_duration.sign();
|
||||
}
|
||||
|
||||
String* toString(ExecutionState& state, Value options);
|
||||
static String* temporalDurationToString(ISO8601::Duration duration, Value precision);
|
||||
// https://tc39.es/proposal-temporal/#sec-temporal-tointernaldurationrecord
|
||||
static ISO8601::InternalDuration toInternalDurationRecord(ISO8601::Duration duration);
|
||||
|
||||
// https://tc39.es/proposal-temporal/#sec-temporal-roundtimeduration
|
||||
static Int128 roundTimeDuration(ExecutionState& state, Int128 timeDuration, unsigned increment, ISO8601::DateTimeUnit unit, ISO8601::RoundingMode roundingMode);
|
||||
|
||||
// https://tc39.es/proposal-temporal/#sec-temporal-roundtimedurationtoincrement
|
||||
static Int128 roundTimeDurationToIncrement(ExecutionState& state, Int128 d, Int128 increment, ISO8601::RoundingMode roundingMode);
|
||||
|
||||
private:
|
||||
ISO8601::Duration m_duration;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#include "Escargot.h"
|
||||
#include "TemporalInstantObject.h"
|
||||
#include "TemporalDurationObject.h"
|
||||
#include "intl/Intl.h"
|
||||
#include "util/ISO8601.h"
|
||||
|
||||
|
|
@ -39,42 +40,11 @@ Value TemporalInstantObject::epochMilliseconds() const
|
|||
}
|
||||
|
||||
|
||||
static Int128 resolveNanosecondsValueByUnit(String* unit)
|
||||
{
|
||||
Int128 maximum = 0;
|
||||
constexpr int64_t hoursPerDay = 24;
|
||||
constexpr int64_t minutesPerHour = 60;
|
||||
constexpr int64_t secondsPerMinute = 60;
|
||||
constexpr int64_t msPerDay = hoursPerDay * minutesPerHour * secondsPerMinute * 1000;
|
||||
|
||||
if (unit->equals("hour")) {
|
||||
maximum = static_cast<Int128>(hoursPerDay);
|
||||
} else if (unit->equals("minute")) {
|
||||
maximum = static_cast<Int128>(minutesPerHour * hoursPerDay);
|
||||
} else if (unit->equals("second")) {
|
||||
maximum = static_cast<Int128>(secondsPerMinute * minutesPerHour * hoursPerDay);
|
||||
} else if (unit->equals("millisecond")) {
|
||||
maximum = static_cast<Int128>(msPerDay);
|
||||
} else if (unit->equals("microsecond")) {
|
||||
maximum = static_cast<Int128>(msPerDay * 1000);
|
||||
} else if (unit->equals("nanosecond")) {
|
||||
maximum = ISO8601::ExactTime::nsPerDay;
|
||||
}
|
||||
|
||||
return maximum;
|
||||
}
|
||||
|
||||
String* TemporalInstantObject::toString(ExecutionState& state, Value options)
|
||||
{
|
||||
const char* msg = "Invalid options value";
|
||||
// Let resolvedOptions be ? GetOptionsObject(options).
|
||||
Optional<Object*> resolvedOptions;
|
||||
if (options.isObject()) {
|
||||
resolvedOptions = options.asObject();
|
||||
} else if (!options.isUndefined()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, msg);
|
||||
}
|
||||
|
||||
Optional<Object*> resolvedOptions = Intl::getOptionsObject(state, options);
|
||||
// Let digits be ? GetTemporalFractionalSecondDigitsOption(resolvedOptions).
|
||||
auto digits = Temporal::getTemporalFractionalSecondDigitsOption(state, resolvedOptions);
|
||||
// Let roundingMode be ? GetRoundingModeOption(resolvedOptions, trunc).
|
||||
|
|
@ -91,7 +61,7 @@ String* TemporalInstantObject::toString(ExecutionState& state, Value options)
|
|||
// Perform ? ValidateTemporalUnitValue(smallestUnit, time).
|
||||
Temporal::validateTemporalUnitValue(state, smallestUnit, ISO8601::DateTimeUnitCategory::Time, nullptr, 0);
|
||||
// If smallestUnit is hour, throw a RangeError exception.
|
||||
if (smallestUnit.hasValue() && smallestUnit.value()->equals("hour")) {
|
||||
if (smallestUnit.hasValue() && smallestUnit == TemporalUnit::Hour) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, msg);
|
||||
}
|
||||
// If timeZone is not undefined, then
|
||||
|
|
@ -100,10 +70,10 @@ String* TemporalInstantObject::toString(ExecutionState& state, Value options)
|
|||
computedTimeZone = Temporal::toTemporalTimezoneIdentifier(state, timeZone);
|
||||
}
|
||||
// Let precision be ToSecondsStringPrecisionRecord(smallestUnit, digits).
|
||||
auto precision = Temporal::toSecondsStringPrecisionRecord(state, smallestUnit, digits);
|
||||
auto precision = Temporal::toSecondsStringPrecisionRecord(state, toDateTimeUnit(smallestUnit), digits);
|
||||
// Let roundedNs be RoundTemporalInstant(instant.[[EpochNanoseconds]], precision.[[Increment]], precision.[[Unit]], roundingMode).
|
||||
// Let roundedInstant be ! CreateTemporalInstant(roundedNs).
|
||||
Int128 maximum = resolveNanosecondsValueByUnit(precision.unit);
|
||||
Int128 maximum = ISO8601::resolveNanosecondsValueByUnit(precision.unit);
|
||||
|
||||
Temporal::validateTemporalRoundingIncrement(state, precision.increment, maximum, true);
|
||||
|
||||
|
|
@ -196,22 +166,7 @@ String* TemporalInstantObject::toString(ExecutionState& state, Int128 epochNanos
|
|||
builder.appendChar(':');
|
||||
auto s = pad('0', 2, std::to_string(second));
|
||||
builder.appendString(String::fromASCII(s.data(), s.length()));
|
||||
|
||||
if ((precision.isString() && precision.asString()->equals("auto") && fraction) || (precision.isInt32() && precision.asInt32())) {
|
||||
auto padded = pad('0', 9, std::to_string(fraction));
|
||||
padded = '.' + padded;
|
||||
|
||||
if (precision.isInt32()) {
|
||||
padded = padded.substr(0, padded.length() - (9 - precision.asInt32()));
|
||||
} else {
|
||||
auto lengthWithoutTrailingZeroes = padded.length();
|
||||
while (padded[lengthWithoutTrailingZeroes - 1] == '0') {
|
||||
lengthWithoutTrailingZeroes--;
|
||||
}
|
||||
padded = padded.substr(0, lengthWithoutTrailingZeroes);
|
||||
}
|
||||
builder.appendString(String::fromASCII(padded.data(), padded.length()));
|
||||
}
|
||||
Temporal::formatSecondsStringFraction(builder, fraction, precision);
|
||||
}
|
||||
|
||||
if (timeZone.empty()) {
|
||||
|
|
@ -290,16 +245,38 @@ TemporalInstantObject* TemporalInstantObject::round(ExecutionState& state, Value
|
|||
// Else,
|
||||
// Assert: smallestUnit is nanosecond.
|
||||
// Let maximum be nsPerDay.
|
||||
Int128 maximum = resolveNanosecondsValueByUnit(smallestUnit);
|
||||
Int128 maximum = ISO8601::resolveNanosecondsValueByUnit(toDateTimeUnit(smallestUnit));
|
||||
|
||||
// Perform ? ValidateTemporalRoundingIncrement(roundingIncrement, maximum, true).
|
||||
Temporal::validateTemporalRoundingIncrement(state, roundingIncrement, maximum, true);
|
||||
// Let roundedNs be RoundTemporalInstant(instant.[[EpochNanoseconds]], roundingIncrement, smallestUnit, roundingMode).
|
||||
// Return ! CreateTemporalInstant(roundedNs).
|
||||
auto roundedInstant = ISO8601::ExactTime(*m_nanoseconds).round(state, roundingIncrement, smallestUnit, roundingMode);
|
||||
auto roundedInstant = ISO8601::ExactTime(*m_nanoseconds).round(state, roundingIncrement, toDateTimeUnit(smallestUnit), roundingMode);
|
||||
return new TemporalInstantObject(state, state.context()->globalObject()->temporalInstantPrototype(), roundedInstant.epochNanoseconds());
|
||||
}
|
||||
|
||||
TemporalDurationObject* TemporalInstantObject::differenceTemporalInstant(ExecutionState& state, DifferenceTemporalInstantOperation operation, Value otherInput, Value options)
|
||||
{
|
||||
// Set other to ? ToTemporalInstant(other).
|
||||
TemporalInstantObject* other = Temporal::toTemporalInstant(state, otherInput);
|
||||
// Let resolvedOptions be ? GetOptionsObject(options).
|
||||
Optional<Object*> resolvedOptions = Intl::getOptionsObject(state, options);
|
||||
// Let settings be ? GetDifferenceSettings(operation, resolvedOptions, time, « », nanosecond, second).
|
||||
auto settings = Temporal::getDifferenceSettings(state, operation == DifferenceTemporalInstantOperation::Since, resolvedOptions, ISO8601::DateTimeUnitCategory::Time, nullptr, 0,
|
||||
TemporalUnit::Nanosecond, TemporalUnit::Second);
|
||||
// Let internalDuration be DifferenceInstant(instant.[[EpochNanoseconds]], other.[[EpochNanoseconds]], settings.[[RoundingIncrement]], settings.[[SmallestUnit]], settings.[[RoundingMode]]).
|
||||
auto internalDuration = differenceInstant(state, epochNanoseconds(), other->epochNanoseconds(), settings.roundingIncrement, settings.smallestUnit, settings.roundingMode);
|
||||
// Let result be ! TemporalDurationFromInternal(internalDuration, settings.[[LargestUnit]]).
|
||||
auto result = TemporalDurationObject::temporalDurationFromInternal(state, internalDuration, settings.largestUnit);
|
||||
// If operation is since, set result to CreateNegatedTemporalDuration(result).
|
||||
if (operation == DifferenceTemporalInstantOperation::Since) {
|
||||
result = TemporalDurationObject::createNegatedTemporalDuration(result);
|
||||
}
|
||||
|
||||
// Return result.
|
||||
return new TemporalDurationObject(state, result);
|
||||
}
|
||||
|
||||
TemporalInstantObject* TemporalInstantObject::addDurationToInstant(AddDurationOperation operation, const Value& temporalDurationLike)
|
||||
{
|
||||
TemporalInstantObject* instant = this;
|
||||
|
|
@ -307,6 +284,17 @@ TemporalInstantObject* TemporalInstantObject::addDurationToInstant(AddDurationOp
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
ISO8601::InternalDuration TemporalInstantObject::differenceInstant(ExecutionState& state, Int128 ns1, Int128 ns2, unsigned roundingIncrement,
|
||||
ISO8601::DateTimeUnit smallestUnit, ISO8601::RoundingMode roundingMode)
|
||||
{
|
||||
// Let timeDuration be TimeDurationFromEpochNanosecondsDifference(ns2, ns1).
|
||||
Int128 timeDuration = Temporal::timeDurationFromEpochNanosecondsDifference(ns2, ns1);
|
||||
// Set timeDuration to ! RoundTimeDuration(timeDuration, roundingIncrement, smallestUnit, roundingMode).
|
||||
timeDuration = TemporalDurationObject::roundTimeDuration(state, timeDuration, roundingIncrement, smallestUnit, roundingMode);
|
||||
// Return CombineDateAndTimeDuration(ZeroDateDuration(), timeDuration).
|
||||
return ISO8601::InternalDuration(ISO8601::Duration(), timeDuration);
|
||||
}
|
||||
|
||||
} // namespace Escargot
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@
|
|||
|
||||
namespace Escargot {
|
||||
|
||||
class TemporalDurationObject;
|
||||
|
||||
class TemporalInstantObject : public DerivedObject {
|
||||
public:
|
||||
TemporalInstantObject(ExecutionState& state, Object* proto, Int128 nanoseconds);
|
||||
|
|
@ -44,6 +46,13 @@ public:
|
|||
static String* toString(ExecutionState& state, Int128 epochNanoseconds, TimeZone timeZone, Value precision);
|
||||
TemporalInstantObject* round(ExecutionState& state, Value roundTo);
|
||||
|
||||
// https://tc39.es/proposal-temporal/#sec-temporal-differencetemporalinstant
|
||||
enum class DifferenceTemporalInstantOperation {
|
||||
Since,
|
||||
Until
|
||||
};
|
||||
TemporalDurationObject* differenceTemporalInstant(ExecutionState& state, DifferenceTemporalInstantOperation operation, Value other, Value options);
|
||||
|
||||
private:
|
||||
// https://tc39.es/proposal-temporal/#sec-temporal-adddurationtoinstant
|
||||
enum class AddDurationOperation {
|
||||
|
|
@ -52,6 +61,10 @@ private:
|
|||
};
|
||||
TemporalInstantObject* addDurationToInstant(AddDurationOperation operation, const Value& temporalDurationLike);
|
||||
|
||||
// https://tc39.es/proposal-temporal/#sec-temporal-differenceinstant
|
||||
static ISO8601::InternalDuration differenceInstant(ExecutionState& state, Int128 ns1, Int128 ns2, unsigned roundingIncrement,
|
||||
ISO8601::DateTimeUnit smallestUnit, ISO8601::RoundingMode roundingMode);
|
||||
|
||||
Int128* m_nanoseconds; // [[EpochNanoseconds]]
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -389,6 +389,25 @@ Value Temporal::toTemporalDate(ExecutionState& state, Value item, Value options)
|
|||
return Value();
|
||||
}
|
||||
|
||||
void Temporal::formatSecondsStringFraction(StringBuilder& builder, Int128 fraction, Value precision)
|
||||
{
|
||||
if ((precision.isString() && precision.asString()->equals("auto") && fraction) || (precision.isInt32() && precision.asInt32())) {
|
||||
auto padded = pad('0', 9, std::to_string(fraction));
|
||||
padded = '.' + padded;
|
||||
|
||||
if (precision.isInt32()) {
|
||||
padded = padded.substr(0, padded.length() - (9 - precision.asInt32()));
|
||||
} else {
|
||||
auto lengthWithoutTrailingZeroes = padded.length();
|
||||
while (padded[lengthWithoutTrailingZeroes - 1] == '0') {
|
||||
lengthWithoutTrailingZeroes--;
|
||||
}
|
||||
padded = padded.substr(0, lengthWithoutTrailingZeroes);
|
||||
}
|
||||
builder.appendString(String::fromASCII(padded.data(), padded.length()));
|
||||
}
|
||||
}
|
||||
|
||||
Int128 Temporal::systemUTCEpochNanoseconds()
|
||||
{
|
||||
std::chrono::system_clock::duration d = std::chrono::system_clock::now().time_since_epoch();
|
||||
|
|
@ -540,14 +559,37 @@ Optional<unsigned> Temporal::getTemporalFractionalSecondDigitsOption(ExecutionSt
|
|||
return digitCount;
|
||||
}
|
||||
|
||||
String* Temporal::getRoundingModeOption(ExecutionState& state, Optional<Object*> resolvedOptions, String* fallback)
|
||||
static ISO8601::RoundingMode toRoundingMode(String* roundingMode)
|
||||
{
|
||||
if (roundingMode->equals("ceil")) {
|
||||
return ISO8601::RoundingMode::Ceil;
|
||||
} else if (roundingMode->equals("floor")) {
|
||||
return ISO8601::RoundingMode::Floor;
|
||||
} else if (roundingMode->equals("expand")) {
|
||||
return ISO8601::RoundingMode::Expand;
|
||||
} else if (roundingMode->equals("trunc")) {
|
||||
return ISO8601::RoundingMode::Trunc;
|
||||
} else if (roundingMode->equals("halfCeil")) {
|
||||
return ISO8601::RoundingMode::HalfCeil;
|
||||
} else if (roundingMode->equals("halfFloor")) {
|
||||
return ISO8601::RoundingMode::HalfFloor;
|
||||
} else if (roundingMode->equals("halfExpand")) {
|
||||
return ISO8601::RoundingMode::HalfExpand;
|
||||
} else if (roundingMode->equals("halfTrunc")) {
|
||||
return ISO8601::RoundingMode::HalfTrunc;
|
||||
}
|
||||
ASSERT(roundingMode->equals("halfEven"));
|
||||
return ISO8601::RoundingMode::HalfEven;
|
||||
}
|
||||
|
||||
ISO8601::RoundingMode Temporal::getRoundingModeOption(ExecutionState& state, Optional<Object*> resolvedOptions, String* fallback)
|
||||
{
|
||||
// Let allowedStrings be the List of Strings from the "String Identifier" column of Table 27.
|
||||
// Let stringFallback be the value from the "String Identifier" column of the row with fallback in its "Rounding Mode" column.
|
||||
// Let stringValue be ? GetOption(options, "roundingMode", string, allowedStrings, stringFallback).
|
||||
// Return the value from the "Rounding Mode" column of the row with stringValue in its "String Identifier" column.
|
||||
if (!resolvedOptions) {
|
||||
return fallback;
|
||||
return toRoundingMode(fallback);
|
||||
}
|
||||
|
||||
Value values[] = {
|
||||
|
|
@ -562,10 +604,11 @@ String* Temporal::getRoundingModeOption(ExecutionState& state, Optional<Object*>
|
|||
state.context()->staticStrings().lazyHalfEven().string()
|
||||
};
|
||||
|
||||
return Intl::getOption(state, resolvedOptions.value(), state.context()->staticStrings().lazyRoundingMode().string(), Intl::OptionValueType::StringValue, values, 9, fallback).asString();
|
||||
String* roundingMode = Intl::getOption(state, resolvedOptions.value(), state.context()->staticStrings().lazyRoundingMode().string(), Intl::OptionValueType::StringValue, values, 9, fallback).asString();
|
||||
return toRoundingMode(roundingMode);
|
||||
}
|
||||
|
||||
Optional<String*> Temporal::getTemporalUnitValuedOption(ExecutionState& state, Optional<Object*> resolvedOptions, String* key, Optional<Value> defaultValue)
|
||||
Optional<TemporalUnit> Temporal::getTemporalUnitValuedOption(ExecutionState& state, Optional<Object*> resolvedOptions, String* key, Optional<Value> defaultValue)
|
||||
{
|
||||
// Let allowedStrings be a List containing all values in the "Singular property name" and "Plural property name" columns of Table 21, except the header row.
|
||||
// Append "auto" to allowedStrings.
|
||||
|
|
@ -605,32 +648,32 @@ Optional<String*> Temporal::getTemporalUnitValuedOption(ExecutionState& state, O
|
|||
}
|
||||
// If value is undefined, return unset.
|
||||
if (value.isUndefined()) {
|
||||
return nullptr;
|
||||
return NullOption;
|
||||
}
|
||||
String* stringValue = value.asString();
|
||||
// If value is "auto", return auto.
|
||||
if (stringValue->equals("auto")) {
|
||||
return stringValue;
|
||||
return TemporalUnit::Auto;
|
||||
}
|
||||
// Return the value in the "Value" column of Table 21 corresponding to the row with value in its "Singular property name" or "Plural property name" column.
|
||||
if (false) {}
|
||||
#define DEFINE_COMPARE(name, Name, names, Names, index, category) \
|
||||
else if (stringValue->equals(#name)) \
|
||||
{ \
|
||||
return state.context()->staticStrings().lazy##Name().string(); \
|
||||
} \
|
||||
else if (stringValue->equals(#names)) \
|
||||
{ \
|
||||
return state.context()->staticStrings().lazy##Name().string(); \
|
||||
#define DEFINE_COMPARE(name, Name, names, Names, index, category) \
|
||||
else if (stringValue->equals(#name)) \
|
||||
{ \
|
||||
return TemporalUnit::Name; \
|
||||
} \
|
||||
else if (stringValue->equals(#names)) \
|
||||
{ \
|
||||
return TemporalUnit::Name; \
|
||||
}
|
||||
PLAIN_DATETIME_UNITS(DEFINE_COMPARE)
|
||||
#undef DEFINE_COMPARE
|
||||
|
||||
ASSERT_NOT_REACHED();
|
||||
return String::emptyString();
|
||||
return TemporalUnit::Auto;
|
||||
}
|
||||
|
||||
void Temporal::validateTemporalUnitValue(ExecutionState& state, Optional<String*> value, ISO8601::DateTimeUnitCategory unitGroup, Optional<String*> extraValues, size_t extraValueSize)
|
||||
void Temporal::validateTemporalUnitValue(ExecutionState& state, Optional<TemporalUnit> value, ISO8601::DateTimeUnitCategory unitGroup, Optional<TemporalUnit*> extraValues, size_t extraValueSize)
|
||||
{
|
||||
// If value is unset, return unused.
|
||||
if (!value) {
|
||||
|
|
@ -639,24 +682,19 @@ void Temporal::validateTemporalUnitValue(ExecutionState& state, Optional<String*
|
|||
// If extraValues is present and extraValues contains value, return unused.
|
||||
if (extraValues && value) {
|
||||
for (size_t i = 0; i < extraValueSize; i++) {
|
||||
if (extraValues.value()[i].equals(value.value())) {
|
||||
if (extraValues.value()[i] == value.value()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
String* stringValue = value.value();
|
||||
ISO8601::DateTimeUnitCategory categoryValue = ISO8601::DateTimeUnitCategory::DateTime;
|
||||
const auto* msg = "Invalid temporal unit value";
|
||||
// Let category be the value in the “Category” column of the row of Table 21 whose “Value” column contains value. If there is no such row, throw a RangeError exception.
|
||||
if (false) {}
|
||||
#define DEFINE_COMPARE(name, Name, names, Names, index, category) \
|
||||
else if (stringValue->equals(#name)) \
|
||||
{ \
|
||||
categoryValue = category; \
|
||||
} \
|
||||
else if (stringValue->equals(#names)) \
|
||||
{ \
|
||||
categoryValue = category; \
|
||||
#define DEFINE_COMPARE(name, Name, names, Names, index, category) \
|
||||
else if (toDateTimeUnit(value.value()) == ISO8601::DateTimeUnit::Name) \
|
||||
{ \
|
||||
categoryValue = category; \
|
||||
}
|
||||
PLAIN_DATETIME_UNITS(DEFINE_COMPARE)
|
||||
#undef DEFINE_COMPARE
|
||||
|
|
@ -726,29 +764,29 @@ TimeZone Temporal::toTemporalTimezoneIdentifier(ExecutionState& state, const Val
|
|||
return TimeZone(String::emptyString());
|
||||
}
|
||||
|
||||
Temporal::StringPrecisionRecord Temporal::toSecondsStringPrecisionRecord(ExecutionState& state, Optional<String*> smallestUnit, Optional<unsigned> fractionalDigitCount)
|
||||
Temporal::StringPrecisionRecord Temporal::toSecondsStringPrecisionRecord(ExecutionState& state, Optional<ISO8601::DateTimeUnit> smallestUnit, Optional<unsigned> fractionalDigitCount)
|
||||
{
|
||||
if (smallestUnit) {
|
||||
// If smallestUnit is minute, then
|
||||
if (smallestUnit->equals("minute")) {
|
||||
if (smallestUnit.value() == ISO8601::DateTimeUnit::Minute) {
|
||||
// Return the Record { [[Precision]]: minute, [[Unit]]: minute, [[Increment]]: 1 }.
|
||||
return { state.context()->staticStrings().lazyMinute().string(), state.context()->staticStrings().lazyMinute().string(), 1 };
|
||||
} else if (smallestUnit->equals("second")) {
|
||||
return { state.context()->staticStrings().lazyMinute().string(), ISO8601::DateTimeUnit::Minute, 1 };
|
||||
} else if (smallestUnit.value() == ISO8601::DateTimeUnit::Second) {
|
||||
// If smallestUnit is second, then
|
||||
// Return the Record { [[Precision]]: 0, [[Unit]]: second, [[Increment]]: 1 }.
|
||||
return { Value(0), state.context()->staticStrings().lazySecond().string(), 1 };
|
||||
} else if (smallestUnit->equals("millisecond")) {
|
||||
return { Value(0), ISO8601::DateTimeUnit::Second, 1 };
|
||||
} else if (smallestUnit.value() == ISO8601::DateTimeUnit::Millisecond) {
|
||||
// If smallestUnit is millisecond, then
|
||||
// Return the Record { [[Precision]]: 3, [[Unit]]: millisecond, [[Increment]]: 1 }.
|
||||
return { Value(3), state.context()->staticStrings().lazyMillisecond().string(), 1 };
|
||||
} else if (smallestUnit->equals("microsecond")) {
|
||||
// If smallestUnit is millisecond, then
|
||||
return { Value(3), ISO8601::DateTimeUnit::Millisecond, 1 };
|
||||
} else if (smallestUnit.value() == ISO8601::DateTimeUnit::Microsecond) {
|
||||
// If smallestUnit is microsecond, then
|
||||
// Return the Record { [[Precision]]: 6, [[Unit]]: microsecond, [[Increment]]: 1 }.
|
||||
return { Value(6), state.context()->staticStrings().lazyMicrosecond().string(), 1 };
|
||||
} else if (smallestUnit->equals("nanosecond")) {
|
||||
return { Value(6), ISO8601::DateTimeUnit::Microsecond, 1 };
|
||||
} else if (smallestUnit.value() == ISO8601::DateTimeUnit::Nanosecond) {
|
||||
// If smallestUnit is nanosecond, then
|
||||
// Return the Record { [[Precision]]: 9, [[Unit]]: nanosecond, [[Increment]]: 1 }.
|
||||
return { Value(9), state.context()->staticStrings().lazyNanosecond().string(), 1 };
|
||||
return { Value(9), ISO8601::DateTimeUnit::Nanosecond, 1 };
|
||||
}
|
||||
}
|
||||
// Assert: smallestUnit is unset.
|
||||
|
|
@ -756,7 +794,7 @@ Temporal::StringPrecisionRecord Temporal::toSecondsStringPrecisionRecord(Executi
|
|||
// If fractionalDigitCount is auto, then
|
||||
if (!fractionalDigitCount.hasValue()) {
|
||||
// Return the Record { [[Precision]]: auto, [[Unit]]: nanosecond, [[Increment]]: 1 }.
|
||||
return { state.context()->staticStrings().lazyAuto().string(), state.context()->staticStrings().lazyNanosecond().string(), 1 };
|
||||
return { state.context()->staticStrings().lazyAuto().string(), ISO8601::DateTimeUnit::Nanosecond, 1 };
|
||||
}
|
||||
|
||||
auto pow10Unsigned = [](unsigned n) -> unsigned {
|
||||
|
|
@ -769,20 +807,20 @@ Temporal::StringPrecisionRecord Temporal::toSecondsStringPrecisionRecord(Executi
|
|||
// If fractionalDigitCount = 0, then
|
||||
if (fractionalDigitCount && fractionalDigitCount.value() == 0) {
|
||||
// Return the Record { [[Precision]]: 0, [[Unit]]: second, [[Increment]]: 1 }.
|
||||
return { Value(0), state.context()->staticStrings().lazySecond().string(), 1 };
|
||||
return { Value(0), ISO8601::DateTimeUnit::Second, 1 };
|
||||
} else if (fractionalDigitCount && fractionalDigitCount.value() >= 1 && fractionalDigitCount.value() <= 3) {
|
||||
// If fractionalDigitCount is in the inclusive interval from 1 to 3, then
|
||||
// Return the Record { [[Precision]]: fractionalDigitCount, [[Unit]]: millisecond, [[Increment]]: 10**(3 - fractionalDigitCount) }
|
||||
return { Value(fractionalDigitCount.value()), state.context()->staticStrings().lazyMillisecond().string(), pow10Unsigned(3 - fractionalDigitCount.value()) };
|
||||
return { Value(fractionalDigitCount.value()), ISO8601::DateTimeUnit::Millisecond, pow10Unsigned(3 - fractionalDigitCount.value()) };
|
||||
} else if (fractionalDigitCount && fractionalDigitCount.value() >= 4 && fractionalDigitCount.value() <= 6) {
|
||||
// If fractionalDigitCount is in the inclusive interval from 4 to 6, then
|
||||
// Return the Record { [[Precision]]: fractionalDigitCount, [[Unit]]: microsecond, [[Increment]]: 10**(6 - fractionalDigitCount) }.
|
||||
return { Value(fractionalDigitCount.value()), state.context()->staticStrings().lazyMicrosecond().string(), pow10Unsigned(6 - fractionalDigitCount.value()) };
|
||||
return { Value(fractionalDigitCount.value()), ISO8601::DateTimeUnit::Microsecond, pow10Unsigned(6 - fractionalDigitCount.value()) };
|
||||
}
|
||||
// Assert: fractionalDigitCount is in the inclusive interval from 7 to 9.
|
||||
ASSERT(fractionalDigitCount && fractionalDigitCount.value() >= 7 && fractionalDigitCount.value() <= 9);
|
||||
// Return the Record { [[Precision]]: fractionalDigitCount, [[Unit]]: nanosecond, [[Increment]]: 10**(9 - fractionalDigitCount) }.
|
||||
return { Value(fractionalDigitCount.value()), state.context()->staticStrings().lazyNanosecond().string(), pow10Unsigned(9 - fractionalDigitCount.value()) };
|
||||
return { Value(fractionalDigitCount.value()), ISO8601::DateTimeUnit::Nanosecond, pow10Unsigned(9 - fractionalDigitCount.value()) };
|
||||
}
|
||||
|
||||
void Temporal::validateTemporalRoundingIncrement(ExecutionState& state, unsigned increment, Int128 dividend, bool inclusive)
|
||||
|
|
@ -811,10 +849,13 @@ void Temporal::validateTemporalRoundingIncrement(ExecutionState& state, unsigned
|
|||
// Return unused.
|
||||
}
|
||||
|
||||
int32_t Temporal::getRoundingIncrementOption(ExecutionState& state, Object* options)
|
||||
unsigned Temporal::getRoundingIncrementOption(ExecutionState& state, Optional<Object*> options)
|
||||
{
|
||||
if (!options) {
|
||||
return 1;
|
||||
}
|
||||
// Let value be ? Get(options, "roundingIncrement").
|
||||
Value value = options->get(state, ObjectPropertyName(state.context()->staticStrings().lazyRoundingIncrement())).value(state, options);
|
||||
Value value = options.value()->get(state, ObjectPropertyName(state.context()->staticStrings().lazyRoundingIncrement())).value(state, options.value());
|
||||
// If value is undefined, return 1𝔽.
|
||||
if (value.isUndefined()) {
|
||||
return 1;
|
||||
|
|
@ -829,6 +870,153 @@ int32_t Temporal::getRoundingIncrementOption(ExecutionState& state, Object* opti
|
|||
return integerIncrement;
|
||||
}
|
||||
|
||||
Temporal::DifferenceSettingsRecord Temporal::getDifferenceSettings(ExecutionState& state, bool isSinceOperation, Optional<Object*> options, ISO8601::DateTimeUnitCategory unitGroup,
|
||||
Optional<TemporalUnit*> disallowedUnits, size_t disallowedUnitsLength, TemporalUnit fallbackSmallestUnit, TemporalUnit smallestLargestDefaultUnit)
|
||||
{
|
||||
// NOTE: The following steps read options and perform independent validation in alphabetical order.
|
||||
// Let largestUnit be ? GetTemporalUnitValuedOption(options, "largestUnit", unset).
|
||||
Optional<TemporalUnit> largestUnit = Temporal::getTemporalUnitValuedOption(state, options, state.context()->staticStrings().lazyLargestUnit().string(), NullOption);
|
||||
// Let roundingIncrement be ? GetRoundingIncrementOption(options).
|
||||
auto roundingIncrement = Temporal::getRoundingIncrementOption(state, options);
|
||||
// Let roundingMode be ? GetRoundingModeOption(options, trunc).
|
||||
auto roundingMode = Temporal::getRoundingModeOption(state, options, state.context()->staticStrings().trunc.string());
|
||||
// Let smallestUnit be ? GetTemporalUnitValuedOption(options, "smallestUnit", unset).
|
||||
Optional<TemporalUnit> smallestUnit = Temporal::getTemporalUnitValuedOption(state, options, state.context()->staticStrings().lazySmallestUnit().string(), NullOption);
|
||||
// Perform ? ValidateTemporalUnitValue(largestUnit, unitGroup, « auto »).
|
||||
TemporalUnit extraValues[1] = { TemporalUnit::Auto };
|
||||
Temporal::validateTemporalUnitValue(state, largestUnit, unitGroup, extraValues, 1);
|
||||
// If largestUnit is unset, then
|
||||
if (!largestUnit) {
|
||||
// Set largestUnit to auto.
|
||||
largestUnit = TemporalUnit::Auto;
|
||||
}
|
||||
|
||||
// If disallowedUnits contains largestUnit, throw a RangeError exception.
|
||||
if (disallowedUnits) {
|
||||
for (size_t i = 0; i < disallowedUnitsLength; i++) {
|
||||
if (disallowedUnits.value()[i] == largestUnit.value()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, "Invalid largestUnit value");
|
||||
}
|
||||
}
|
||||
}
|
||||
// If operation is since, then
|
||||
if (isSinceOperation) {
|
||||
// Set roundingMode to NegateRoundingMode(roundingMode).
|
||||
roundingMode = Temporal::negateRoundingMode(state, roundingMode);
|
||||
}
|
||||
|
||||
// Perform ? ValidateTemporalUnitValue(smallestUnit, unitGroup).
|
||||
Temporal::validateTemporalUnitValue(state, smallestUnit, unitGroup, nullptr, 0);
|
||||
// If smallestUnit is unset, then
|
||||
if (!smallestUnit) {
|
||||
// Set smallestUnit to fallbackSmallestUnit.
|
||||
smallestUnit = fallbackSmallestUnit;
|
||||
}
|
||||
|
||||
// If disallowedUnits contains smallestUnit, throw a RangeError exception.
|
||||
if (disallowedUnits) {
|
||||
for (size_t i = 0; i < disallowedUnitsLength; i++) {
|
||||
if (disallowedUnits.value()[i] == smallestUnit.value()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, "Invalid largestUnit value");
|
||||
}
|
||||
}
|
||||
}
|
||||
// Let defaultLargestUnit be LargerOfTwoTemporalUnits(smallestLargestDefaultUnit, smallestUnit).
|
||||
auto defaultLargestUnit = Temporal::largerOfTwoTemporalUnits(toDateTimeUnit(smallestLargestDefaultUnit), toDateTimeUnit(smallestUnit.value()));
|
||||
// If largestUnit is auto, set largestUnit to defaultLargestUnit.
|
||||
if (largestUnit == TemporalUnit::Auto) {
|
||||
largestUnit = static_cast<TemporalUnit>(defaultLargestUnit);
|
||||
}
|
||||
// If LargerOfTwoTemporalUnits(largestUnit, smallestUnit) is not largestUnit, throw a RangeError exception.
|
||||
if (!(Temporal::largerOfTwoTemporalUnits(toDateTimeUnit(largestUnit.value()), toDateTimeUnit(smallestUnit.value())) == toDateTimeUnit(largestUnit.value()))) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, "Invalid largestUnit or smallestUnit value");
|
||||
}
|
||||
// Let maximum be MaximumTemporalDurationRoundingIncrement(smallestUnit).
|
||||
auto maximum = Temporal::maximumTemporalDurationRoundingIncrement(toDateTimeUnit(smallestUnit.value()));
|
||||
// If maximum is not unset, perform ? ValidateTemporalRoundingIncrement(roundingIncrement, maximum, false).
|
||||
if (maximum) {
|
||||
Temporal::validateTemporalRoundingIncrement(state, roundingIncrement, maximum.value(), false);
|
||||
}
|
||||
// Return the Record { [[SmallestUnit]]: smallestUnit, [[LargestUnit]]: largestUnit, [[RoundingMode]]: roundingMode, [[RoundingIncrement]]: roundingIncrement, }.
|
||||
return { toDateTimeUnit(smallestUnit.value()), toDateTimeUnit(largestUnit.value()), roundingMode, roundingIncrement };
|
||||
}
|
||||
|
||||
ISO8601::RoundingMode Temporal::negateRoundingMode(ExecutionState& state, ISO8601::RoundingMode roundingMode)
|
||||
{
|
||||
// If roundingMode is ceil, return floor.
|
||||
if (roundingMode == ISO8601::RoundingMode::Ceil) {
|
||||
return ISO8601::RoundingMode::Floor;
|
||||
}
|
||||
// If roundingMode is floor, return ceil.
|
||||
if (roundingMode == ISO8601::RoundingMode::Floor) {
|
||||
return ISO8601::RoundingMode::Ceil;
|
||||
}
|
||||
// If roundingMode is half-ceil, return half-floor.
|
||||
if (roundingMode == ISO8601::RoundingMode::HalfCeil) {
|
||||
return ISO8601::RoundingMode::HalfFloor;
|
||||
}
|
||||
// If roundingMode is half-floor, return half-ceil.
|
||||
if (roundingMode == ISO8601::RoundingMode::HalfFloor) {
|
||||
return ISO8601::RoundingMode::HalfCeil;
|
||||
}
|
||||
// Return roundingMode
|
||||
return roundingMode;
|
||||
}
|
||||
|
||||
ISO8601::DateTimeUnit Temporal::largerOfTwoTemporalUnits(ISO8601::DateTimeUnit u1, ISO8601::DateTimeUnit u2)
|
||||
{
|
||||
// For each row of Table 21, except the header row, in table order, do
|
||||
// Let unit be the value in the "Value" column of the row.
|
||||
// If u1 is unit, return unit.
|
||||
// If u2 is unit, return unit.
|
||||
if (false) {}
|
||||
#define DEFINE_COMPARE(name, Name, names, Names, index, category) \
|
||||
else if (u1 == ISO8601::DateTimeUnit::Name) \
|
||||
{ \
|
||||
return u1; \
|
||||
} \
|
||||
else if (u2 == ISO8601::DateTimeUnit::Name) \
|
||||
{ \
|
||||
return u2; \
|
||||
}
|
||||
PLAIN_DATETIME_UNITS(DEFINE_COMPARE)
|
||||
#undef DEFINE_COMPARE
|
||||
else
|
||||
{
|
||||
ASSERT_NOT_REACHED();
|
||||
return ISO8601::DateTimeUnit::Year;
|
||||
}
|
||||
}
|
||||
|
||||
Optional<unsigned> Temporal::maximumTemporalDurationRoundingIncrement(ISO8601::DateTimeUnit unit)
|
||||
{
|
||||
// Return the value from the "Maximum duration rounding increment" column of the row of Table 21 in which unit is in the "Value" column.
|
||||
if (unit == ISO8601::DateTimeUnit::Hour) {
|
||||
return 24;
|
||||
} else if (unit == ISO8601::DateTimeUnit::Minute) {
|
||||
return 60;
|
||||
} else if (unit == ISO8601::DateTimeUnit::Second) {
|
||||
return 60;
|
||||
} else if (unit == ISO8601::DateTimeUnit::Millisecond) {
|
||||
return 1000;
|
||||
} else if (unit == ISO8601::DateTimeUnit::Microsecond) {
|
||||
return 1000;
|
||||
} else if (unit == ISO8601::DateTimeUnit::Nanosecond) {
|
||||
return 1000;
|
||||
}
|
||||
return NullOption;
|
||||
}
|
||||
|
||||
Int128 Temporal::timeDurationFromEpochNanosecondsDifference(Int128 one, Int128 two)
|
||||
{
|
||||
// Let result be ℝ(one) - ℝ(two).
|
||||
auto result = one - two;
|
||||
// Assert: abs(result) ≤ maxTimeDuration.
|
||||
ASSERT(std::abs(result) < ISO8601::InternalDuration::maxTimeDuration);
|
||||
// Return result.
|
||||
return result;
|
||||
}
|
||||
|
||||
TemporalObject::TemporalObject(ExecutionState& state)
|
||||
: TemporalObject(state, state.context()->globalObject()->objectPrototype())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -215,6 +215,27 @@ struct CalendarDate {
|
|||
bool inLeapYear = false;
|
||||
};
|
||||
|
||||
enum class TemporalUnit : uint8_t {
|
||||
#define DEFINE_TYPE(name, Name, names, Names, index, category) Name,
|
||||
PLAIN_DATETIME_UNITS(DEFINE_TYPE)
|
||||
#undef DEFINE_TYPE
|
||||
Auto
|
||||
};
|
||||
|
||||
inline ISO8601::DateTimeUnit toDateTimeUnit(TemporalUnit u)
|
||||
{
|
||||
ASSERT(u != TemporalUnit::Auto);
|
||||
return static_cast<ISO8601::DateTimeUnit>(u);
|
||||
}
|
||||
|
||||
inline Optional<ISO8601::DateTimeUnit> toDateTimeUnit(Optional<TemporalUnit> u)
|
||||
{
|
||||
if (u) {
|
||||
return toDateTimeUnit(u.value());
|
||||
}
|
||||
return NullOption;
|
||||
}
|
||||
|
||||
class Temporal {
|
||||
public:
|
||||
/* TODO ParseISODateTime
|
||||
|
|
@ -287,6 +308,8 @@ public:
|
|||
static Value createTemporalDate(ExecutionState& state, const ISODate& isoDate, String* calendar, Optional<Object*> newTarget);
|
||||
static Value toTemporalDate(ExecutionState& state, Value item, Value options = Value());
|
||||
|
||||
static void formatSecondsStringFraction(StringBuilder& builder, Int128 fraction, Value precision);
|
||||
|
||||
// https://tc39.es/proposal-temporal/#sec-temporal-systemutcepochnanoseconds
|
||||
static Int128 systemUTCEpochNanoseconds();
|
||||
// https://tc39.es/proposal-temporal/#sec-temporal-isvalidepochnanoseconds
|
||||
|
|
@ -303,13 +326,13 @@ public:
|
|||
static Optional<unsigned> getTemporalFractionalSecondDigitsOption(ExecutionState& state, Optional<Object*> resolvedOptions);
|
||||
|
||||
// https://tc39.es/proposal-temporal/#sec-temporal-getroundingmodeoption
|
||||
static String* getRoundingModeOption(ExecutionState& state, Optional<Object*> resolvedOptions, String* fallback);
|
||||
static ISO8601::RoundingMode getRoundingModeOption(ExecutionState& state, Optional<Object*> resolvedOptions, String* fallback);
|
||||
|
||||
// https://tc39.es/proposal-temporal/#sec-temporal-gettemporalunitvaluedoption
|
||||
static Optional<String*> getTemporalUnitValuedOption(ExecutionState& state, Optional<Object*> resolvedOptions, String* key, Optional<Value> defaultValue /* give DefaultValue to EmptyValue means Required = true*/);
|
||||
static Optional<TemporalUnit> getTemporalUnitValuedOption(ExecutionState& state, Optional<Object*> resolvedOptions, String* key, Optional<Value> defaultValue /* give DefaultValue to EmptyValue means Required = true*/);
|
||||
|
||||
// https://tc39.es/proposal-temporal/#sec-temporal-validatetemporalunitvaluedoption
|
||||
static void validateTemporalUnitValue(ExecutionState& state, Optional<String*> value, ISO8601::DateTimeUnitCategory unitGroup, Optional<String*> extraValues, size_t extraValueSize);
|
||||
static void validateTemporalUnitValue(ExecutionState& state, Optional<TemporalUnit> value, ISO8601::DateTimeUnitCategory unitGroup, Optional<TemporalUnit*> extraValues, size_t extraValueSize);
|
||||
|
||||
// https://tc39.es/proposal-temporal/#sec-temporal-totemporaltimezoneidentifier
|
||||
static TimeZone toTemporalTimezoneIdentifier(ExecutionState& state, const Value& temporalTimeZoneLike);
|
||||
|
|
@ -317,16 +340,38 @@ public:
|
|||
// https://tc39.es/proposal-temporal/#sec-temporal-tosecondsstringprecisionrecord
|
||||
struct StringPrecisionRecord {
|
||||
Value precision;
|
||||
String* unit;
|
||||
ISO8601::DateTimeUnit unit;
|
||||
unsigned increment;
|
||||
};
|
||||
static StringPrecisionRecord toSecondsStringPrecisionRecord(ExecutionState& state, Optional<String*> smallestUnit, Optional<unsigned> fractionalDigitCount);
|
||||
static StringPrecisionRecord toSecondsStringPrecisionRecord(ExecutionState& state, Optional<ISO8601::DateTimeUnit> smallestUnit, Optional<unsigned> fractionalDigitCount);
|
||||
|
||||
// https://tc39.es/proposal-temporal/#sec-validatetemporalroundingincrement
|
||||
static void validateTemporalRoundingIncrement(ExecutionState& state, unsigned increment, Int128 dividend, bool inclusive);
|
||||
|
||||
// https://tc39.es/proposal-temporal/#sec-temporal-getroundingincrementoption
|
||||
static int32_t getRoundingIncrementOption(ExecutionState& state, Object* options);
|
||||
static unsigned getRoundingIncrementOption(ExecutionState& state, Optional<Object*> options);
|
||||
|
||||
// https://tc39.es/proposal-temporal/#sec-temporal-getdifferencesettings
|
||||
struct DifferenceSettingsRecord {
|
||||
ISO8601::DateTimeUnit smallestUnit;
|
||||
ISO8601::DateTimeUnit largestUnit;
|
||||
ISO8601::RoundingMode roundingMode;
|
||||
unsigned roundingIncrement;
|
||||
};
|
||||
static DifferenceSettingsRecord getDifferenceSettings(ExecutionState& state, bool isSinceOperation, Optional<Object*> options, ISO8601::DateTimeUnitCategory unitGroup,
|
||||
Optional<TemporalUnit*> disallowedUnits, size_t disallowedUnitsLength, TemporalUnit fallbackSmallestUnit, TemporalUnit smallestLargestDefaultUnit);
|
||||
|
||||
// https://tc39.es/proposal-temporal/#sec-temporal-negateroundingmode
|
||||
static ISO8601::RoundingMode negateRoundingMode(ExecutionState& state, ISO8601::RoundingMode roundingMode);
|
||||
|
||||
// https://tc39.es/proposal-temporal/#sec-temporal-largeroftwotemporalunits
|
||||
static ISO8601::DateTimeUnit largerOfTwoTemporalUnits(ISO8601::DateTimeUnit u1, ISO8601::DateTimeUnit u2);
|
||||
|
||||
// https://tc39.es/proposal-temporal/#sec-temporal-maximumtemporaldurationroundingincrement
|
||||
static Optional<unsigned> maximumTemporalDurationRoundingIncrement(ISO8601::DateTimeUnit unit);
|
||||
|
||||
// https://tc39.es/proposal-temporal/#sec-temporal-timedurationfromepochnanosecondsdifference
|
||||
static Int128 timeDurationFromEpochNanosecondsDifference(Int128 one, Int128 two);
|
||||
};
|
||||
|
||||
class TemporalObject : public DerivedObject {
|
||||
|
|
|
|||
|
|
@ -68,6 +68,8 @@ constexpr Int128 ExactTime::nsPerDay;
|
|||
constexpr Int128 ExactTime::minValue;
|
||||
constexpr Int128 ExactTime::maxValue;
|
||||
|
||||
constexpr Int128 InternalDuration::maxTimeDuration;
|
||||
|
||||
static constexpr int64_t nsPerHour = 1000LL * 1000 * 1000 * 60 * 60;
|
||||
static constexpr int64_t nsPerMinute = 1000LL * 1000 * 1000 * 60;
|
||||
static constexpr int64_t nsPerSecond = 1000LL * 1000 * 1000;
|
||||
|
|
@ -117,7 +119,7 @@ static int32_t parseDecimalInt32(const std::string& characters)
|
|||
|
||||
// DurationHandleFractions ( fHours, minutes, fMinutes, seconds, fSeconds, milliseconds, fMilliseconds, microseconds, fMicroseconds, nanoseconds, fNanoseconds )
|
||||
// https://tc39.es/proposal-temporal/#sec-temporal-durationhandlefractions
|
||||
static void handleFraction(Duration& duration, int factor, std::string fractionString, Duration::Type fractionType)
|
||||
static void handleFraction(Duration& duration, int factor, std::string fractionString, ISO8601::DateTimeUnit fractionType)
|
||||
{
|
||||
auto fractionLength = fractionString.length();
|
||||
std::string padded("000000000");
|
||||
|
|
@ -131,7 +133,7 @@ static void handleFraction(Duration& duration, int factor, std::string fractionS
|
|||
}
|
||||
|
||||
static constexpr int64_t divisor = 1000000000LL;
|
||||
if (fractionType == Duration::Type::Hours) {
|
||||
if (fractionType == ISO8601::DateTimeUnit::Hour) {
|
||||
fraction *= 60;
|
||||
duration.setMinutes(fraction / divisor);
|
||||
fraction %= divisor;
|
||||
|
|
@ -139,7 +141,7 @@ static void handleFraction(Duration& duration, int factor, std::string fractionS
|
|||
return;
|
||||
}
|
||||
|
||||
if (fractionType != Duration::Type::Seconds) {
|
||||
if (fractionType != ISO8601::DateTimeUnit::Second) {
|
||||
fraction *= 60;
|
||||
duration.setSeconds(fraction / divisor);
|
||||
fraction %= divisor;
|
||||
|
|
@ -157,6 +159,36 @@ static double parseInt(std::string src)
|
|||
return std::stoi(src);
|
||||
}
|
||||
|
||||
DateTimeUnitCategory toDateTimeCategory(DateTimeUnit u)
|
||||
{
|
||||
if (false) {}
|
||||
#define DEFINE_TYPE(name, Name, names, Names, index, category) \
|
||||
else if (u == DateTimeUnit::Name) \
|
||||
{ \
|
||||
return category; \
|
||||
}
|
||||
PLAIN_DATETIME_UNITS(DEFINE_TYPE)
|
||||
#undef DEFINE_TYPE
|
||||
|
||||
ASSERT_NOT_REACHED();
|
||||
return DateTimeUnitCategory::Date;
|
||||
}
|
||||
|
||||
DateTimeUnit toDateTimeUnit(String* unit)
|
||||
{
|
||||
if (false) {}
|
||||
#define DEFINE_TYPE(name, Name, names, Names, index, category) \
|
||||
else if (unit->equals(#name)) \
|
||||
{ \
|
||||
return DateTimeUnit::Name; \
|
||||
}
|
||||
PLAIN_DATETIME_UNITS(DEFINE_TYPE)
|
||||
#undef DEFINE_TYPE
|
||||
|
||||
ASSERT_NOT_REACHED();
|
||||
return DateTimeUnit::Year;
|
||||
}
|
||||
|
||||
Optional<Duration> Duration::parseDurationString(String* input)
|
||||
{
|
||||
// ISO 8601 duration strings are like "-P1Y2M3W4DT5H6M7.123456789S". Notes:
|
||||
|
|
@ -272,7 +304,7 @@ Optional<Duration> Duration::parseDurationString(String* input)
|
|||
return NullOption;
|
||||
result.setHours(integer);
|
||||
if (fractionalPart.size()) {
|
||||
handleFraction(result, factor, fractionalPart, Duration::Type::Hours);
|
||||
handleFraction(result, factor, fractionalPart, ISO8601::DateTimeUnit::Hour);
|
||||
timePartIndex = 3;
|
||||
} else {
|
||||
timePartIndex = 1;
|
||||
|
|
@ -283,7 +315,7 @@ Optional<Duration> Duration::parseDurationString(String* input)
|
|||
return NullOption;
|
||||
result.setMinutes(integer);
|
||||
if (fractionalPart.size()) {
|
||||
handleFraction(result, factor, fractionalPart, Duration::Type::Minutes);
|
||||
handleFraction(result, factor, fractionalPart, ISO8601::DateTimeUnit::Minute);
|
||||
timePartIndex = 3;
|
||||
} else {
|
||||
timePartIndex = 2;
|
||||
|
|
@ -292,7 +324,7 @@ Optional<Duration> Duration::parseDurationString(String* input)
|
|||
case 'S':
|
||||
result.setSeconds(integer);
|
||||
if (fractionalPart.size()) {
|
||||
handleFraction(result, factor, fractionalPart, Duration::Type::Seconds);
|
||||
handleFraction(result, factor, fractionalPart, ISO8601::DateTimeUnit::Second);
|
||||
}
|
||||
timePartIndex = 3;
|
||||
break;
|
||||
|
|
@ -308,11 +340,11 @@ Optional<Duration> Duration::parseDurationString(String* input)
|
|||
return result;
|
||||
}
|
||||
|
||||
String* Duration::typeName(ExecutionState& state, Type t)
|
||||
String* Duration::typeName(ExecutionState& state, ISO8601::DateTimeUnit t)
|
||||
{
|
||||
switch (t) {
|
||||
#define DEFINE_GETTER(name, Name, names, Names, index, category) \
|
||||
case Type::Names: \
|
||||
case ISO8601::DateTimeUnit::Name: \
|
||||
return state.context()->staticStrings().lazy##Names().string();
|
||||
PLAIN_DATETIME_UNITS(DEFINE_GETTER)
|
||||
#undef DEFINE_GETTER
|
||||
|
|
@ -323,11 +355,11 @@ String* Duration::typeName(ExecutionState& state, Type t)
|
|||
return String::emptyString();
|
||||
}
|
||||
|
||||
Int128 Duration::totalNanoseconds(Duration::Type unit) const
|
||||
Int128 Duration::totalNanoseconds(ISO8601::DateTimeUnit unit) const
|
||||
{
|
||||
ASSERT(unit != Duration::Type::Years);
|
||||
ASSERT(unit != Duration::Type::Months);
|
||||
ASSERT(unit != Duration::Type::Weeks);
|
||||
ASSERT(unit != ISO8601::DateTimeUnit::Year);
|
||||
ASSERT(unit != ISO8601::DateTimeUnit::Month);
|
||||
ASSERT(unit != ISO8601::DateTimeUnit::Week);
|
||||
|
||||
Int128 resultNs = 0;
|
||||
|
||||
|
|
@ -335,40 +367,40 @@ Int128 Duration::totalNanoseconds(Duration::Type unit) const
|
|||
constexpr int64_t milliMultiplier = 1000000ULL;
|
||||
constexpr int64_t microMultiplier = 1000ULL;
|
||||
|
||||
if (unit <= Duration::Type::Days) {
|
||||
if (unit <= ISO8601::DateTimeUnit::Day) {
|
||||
Int128 s(days());
|
||||
s *= 86400;
|
||||
s *= nanoMultiplier;
|
||||
resultNs += s;
|
||||
}
|
||||
if (unit <= Duration::Type::Hours) {
|
||||
if (unit <= ISO8601::DateTimeUnit::Hour) {
|
||||
Int128 s(hours());
|
||||
s *= 3600;
|
||||
s *= nanoMultiplier;
|
||||
resultNs += s;
|
||||
}
|
||||
if (unit <= Duration::Type::Minutes) {
|
||||
if (unit <= ISO8601::DateTimeUnit::Minute) {
|
||||
Int128 s(minutes());
|
||||
s *= 60;
|
||||
s *= nanoMultiplier;
|
||||
resultNs += s;
|
||||
}
|
||||
if (unit <= Duration::Type::Seconds) {
|
||||
if (unit <= ISO8601::DateTimeUnit::Second) {
|
||||
Int128 s(seconds());
|
||||
s *= nanoMultiplier;
|
||||
resultNs += s;
|
||||
}
|
||||
if (unit <= Duration::Type::Milliseconds) {
|
||||
if (unit <= ISO8601::DateTimeUnit::Millisecond) {
|
||||
Int128 s(milliseconds());
|
||||
s *= milliMultiplier;
|
||||
resultNs += s;
|
||||
}
|
||||
if (unit <= Duration::Type::Microseconds) {
|
||||
if (unit <= ISO8601::DateTimeUnit::Microsecond) {
|
||||
Int128 s(microseconds());
|
||||
s *= microMultiplier;
|
||||
resultNs += s;
|
||||
}
|
||||
if (unit <= Duration::Type::Nanoseconds) {
|
||||
if (unit <= ISO8601::DateTimeUnit::Nanosecond) {
|
||||
Int128 s(nanoseconds());
|
||||
resultNs += s;
|
||||
}
|
||||
|
|
@ -1303,27 +1335,103 @@ ExactTime ExactTime::fromISOPartsAndOffset(int32_t year, uint8_t month, uint8_t
|
|||
return ExactTime{ utcNanoseconds - offset };
|
||||
}
|
||||
|
||||
static Int128 lengthInNanoseconds(String* unit)
|
||||
Int128 lengthInNanoseconds(DateTimeUnit unit)
|
||||
{
|
||||
if (unit->equals("nanosecond")) {
|
||||
if (unit == DateTimeUnit::Nanosecond) {
|
||||
return 1;
|
||||
} else if (unit->equals("microsecond")) {
|
||||
} else if (unit == DateTimeUnit::Microsecond) {
|
||||
return 1000;
|
||||
} else if (unit->equals("millisecond")) {
|
||||
} else if (unit == DateTimeUnit::Millisecond) {
|
||||
return 1000 * 1000;
|
||||
} else if (unit->equals("second")) {
|
||||
} else if (unit == DateTimeUnit::Second) {
|
||||
return Int128(1000 * 1000) * Int128(1000);
|
||||
} else if (unit->equals("minute")) {
|
||||
} else if (unit == DateTimeUnit::Minute) {
|
||||
return Int128(1000 * 1000) * Int128(1000) * Int128(60);
|
||||
} else if (unit->equals("hour")) {
|
||||
} else if (unit == DateTimeUnit::Hour) {
|
||||
return Int128(1000 * 1000) * Int128(1000) * Int128(60) * Int128(60);
|
||||
} else {
|
||||
ASSERT(unit->equals("day"));
|
||||
ASSERT(unit == DateTimeUnit::Day);
|
||||
return Int128(1000 * 1000) * Int128(1000) * Int128(60) * Int128(60) * Int128(24);
|
||||
}
|
||||
}
|
||||
|
||||
Int128 roundNumberToIncrementAsIfPositive(Int128 x, Int128 increment, String* roundingMode)
|
||||
double roundNumberToIncrement(double x, double increment, RoundingMode roundingMode)
|
||||
{
|
||||
auto quotient = x / increment;
|
||||
auto truncatedQuotient = std::trunc(quotient);
|
||||
if (truncatedQuotient == quotient)
|
||||
return truncatedQuotient * increment;
|
||||
|
||||
auto isNegative = quotient < 0;
|
||||
auto expandedQuotient = isNegative ? truncatedQuotient - 1 : truncatedQuotient + 1;
|
||||
|
||||
if (roundingMode >= RoundingMode::HalfCeil) {
|
||||
auto unsignedFractionalPart = std::abs(quotient - truncatedQuotient);
|
||||
if (unsignedFractionalPart < 0.5)
|
||||
return truncatedQuotient * increment;
|
||||
if (unsignedFractionalPart > 0.5)
|
||||
return expandedQuotient * increment;
|
||||
}
|
||||
|
||||
if (roundingMode == RoundingMode::Ceil) {
|
||||
return (isNegative ? truncatedQuotient : expandedQuotient) * increment;
|
||||
} else if (roundingMode == RoundingMode::Floor) {
|
||||
return (isNegative ? expandedQuotient : truncatedQuotient) * increment;
|
||||
} else if (roundingMode == RoundingMode::Expand) {
|
||||
return expandedQuotient * increment;
|
||||
} else if (roundingMode == RoundingMode::Trunc) {
|
||||
return truncatedQuotient * increment;
|
||||
} else if (roundingMode == RoundingMode::HalfCeil) {
|
||||
return (isNegative ? truncatedQuotient : expandedQuotient) * increment;
|
||||
} else if (roundingMode == RoundingMode::HalfFloor) {
|
||||
return (isNegative ? expandedQuotient : truncatedQuotient) * increment;
|
||||
} else if (roundingMode == RoundingMode::HalfExpand) {
|
||||
return expandedQuotient * increment;
|
||||
} else if (roundingMode == RoundingMode::HalfTrunc) {
|
||||
return truncatedQuotient * increment;
|
||||
} else if (roundingMode == RoundingMode::HalfEven) {
|
||||
return (!std::fmod(truncatedQuotient, 2) ? truncatedQuotient : expandedQuotient) * increment;
|
||||
}
|
||||
|
||||
ASSERT_NOT_REACHED();
|
||||
return 0;
|
||||
}
|
||||
|
||||
Int128 roundNumberToIncrement(Int128 x, Int128 increment, RoundingMode roundingMode)
|
||||
{
|
||||
// This follows the polyfill code rather than the spec, in order to work around
|
||||
// being unable to apply floating-point division in x / increment.
|
||||
// See https://github.com/tc39/proposal-temporal/blob/main/polyfill/lib/ecmascript.mjs#L4043
|
||||
Int128 quotient = x / increment;
|
||||
Int128 remainder = x % increment;
|
||||
bool isNegative = x < 0;
|
||||
Int128 r1 = std::abs(quotient);
|
||||
Int128 r2 = r1 + 1;
|
||||
Int128 even = r1 % 2;
|
||||
auto unsignedRoundingMode = getUnsignedRoundingMode(roundingMode, isNegative);
|
||||
Int128 rounded = 0;
|
||||
if (std::abs(x) == r1 * increment)
|
||||
rounded = r1;
|
||||
else if (unsignedRoundingMode == UnsignedRoundingMode::Zero)
|
||||
rounded = r1;
|
||||
else if (unsignedRoundingMode == UnsignedRoundingMode::Infinity)
|
||||
rounded = r2;
|
||||
else if (std::abs(remainder * 2) < increment)
|
||||
rounded = r1;
|
||||
else if (std::abs(remainder * 2) > increment)
|
||||
rounded = r2;
|
||||
else if (unsignedRoundingMode == UnsignedRoundingMode::HalfZero)
|
||||
rounded = r1;
|
||||
else if (unsignedRoundingMode == UnsignedRoundingMode::HalfInfinity)
|
||||
rounded = r2;
|
||||
else
|
||||
rounded = !even ? r1 : r2;
|
||||
if (isNegative)
|
||||
rounded = -rounded;
|
||||
return rounded * increment;
|
||||
}
|
||||
|
||||
Int128 roundNumberToIncrementAsIfPositive(Int128 x, Int128 increment, RoundingMode roundingMode)
|
||||
{
|
||||
// The following code follows the polyfill rather than the spec, because we don't have float128.
|
||||
// ApplyUnsignedRoundingMode is inlined here to mirror the polyfill's implementation of it,
|
||||
|
|
@ -1357,41 +1465,81 @@ Int128 roundNumberToIncrementAsIfPositive(Int128 x, Int128 increment, String* ro
|
|||
return !even ? r1 * increment : r2 * increment;
|
||||
}
|
||||
|
||||
UnsignedRoundingMode getUnsignedRoundingMode(String* roundingMode, bool isNegative)
|
||||
UnsignedRoundingMode getUnsignedRoundingMode(RoundingMode roundingMode, bool isNegative)
|
||||
{
|
||||
if (roundingMode->equals("ceil")) {
|
||||
if (roundingMode == RoundingMode::Ceil) {
|
||||
return isNegative ? UnsignedRoundingMode::Zero : UnsignedRoundingMode::Infinity;
|
||||
} else if (roundingMode->equals("floor")) {
|
||||
} else if (roundingMode == RoundingMode::Floor) {
|
||||
return isNegative ? UnsignedRoundingMode::Infinity : UnsignedRoundingMode::Zero;
|
||||
} else if (roundingMode->equals("expand")) {
|
||||
} else if (roundingMode == RoundingMode::Expand) {
|
||||
return UnsignedRoundingMode::Infinity;
|
||||
} else if (roundingMode->equals("trunc")) {
|
||||
} else if (roundingMode == RoundingMode::Trunc) {
|
||||
return UnsignedRoundingMode::Zero;
|
||||
} else if (roundingMode->equals("halfCeil")) {
|
||||
} else if (roundingMode == RoundingMode::HalfCeil) {
|
||||
return isNegative ? UnsignedRoundingMode::HalfZero : UnsignedRoundingMode::HalfInfinity;
|
||||
} else if (roundingMode->equals("halfFloor")) {
|
||||
} else if (roundingMode == RoundingMode::HalfFloor) {
|
||||
return isNegative ? UnsignedRoundingMode::HalfInfinity : UnsignedRoundingMode::HalfZero;
|
||||
} else if (roundingMode->equals("halfExpand")) {
|
||||
} else if (roundingMode == RoundingMode::HalfExpand) {
|
||||
return UnsignedRoundingMode::HalfInfinity;
|
||||
} else if (roundingMode->equals("halfTrunc")) {
|
||||
} else if (roundingMode == RoundingMode::HalfTrunc) {
|
||||
return UnsignedRoundingMode::HalfZero;
|
||||
}
|
||||
return UnsignedRoundingMode::HalfEven;
|
||||
}
|
||||
|
||||
static Int128 roundTemporalInstant(Int128 ns, unsigned increment, String* unit, String* roundingMode)
|
||||
static Int128 roundTemporalInstant(Int128 ns, unsigned increment, DateTimeUnit unit, RoundingMode roundingMode)
|
||||
{
|
||||
auto unitLength = lengthInNanoseconds(unit);
|
||||
auto incrementNs = increment * unitLength;
|
||||
return roundNumberToIncrementAsIfPositive(ns, incrementNs, roundingMode);
|
||||
}
|
||||
|
||||
ExactTime ExactTime::round(ExecutionState& state, unsigned increment, String* unit, String* roundingMode)
|
||||
ExactTime ExactTime::round(ExecutionState& state, unsigned increment, DateTimeUnit unit, RoundingMode roundingMode)
|
||||
{
|
||||
auto roundedNs = roundTemporalInstant(m_epochNanoseconds, increment, unit, roundingMode);
|
||||
return ExactTime{ roundedNs };
|
||||
}
|
||||
|
||||
// https://tc39.es/proposal-temporal/#sec-temporal-datedurationsign
|
||||
static int32_t dateDurationSign(const Duration& d)
|
||||
{
|
||||
if (d.years() > 0)
|
||||
return 1;
|
||||
if (d.years() < 0)
|
||||
return -1;
|
||||
if (d.months() > 0)
|
||||
return 1;
|
||||
if (d.months() < 0)
|
||||
return -1;
|
||||
if (d.weeks() > 0)
|
||||
return 1;
|
||||
if (d.weeks() < 0)
|
||||
return -1;
|
||||
if (d.days() > 0)
|
||||
return 1;
|
||||
if (d.days() < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// https://tc39.es/proposal-temporal/#sec-temporal-internaldurationsign
|
||||
int32_t ISO8601::InternalDuration::sign() const
|
||||
{
|
||||
int32_t sign = dateDurationSign(m_dateDuration);
|
||||
if (sign)
|
||||
return sign;
|
||||
return timeDurationSign();
|
||||
}
|
||||
|
||||
// https://tc39.es/proposal-temporal/#sec-temporal-combinedateandtimeduration
|
||||
InternalDuration InternalDuration::combineDateAndTimeDuration(Duration dateDuration, Int128 timeDuration)
|
||||
{
|
||||
int32_t dateSign = dateDurationSign(dateDuration);
|
||||
int32_t timeSign = timeDuration < 0 ? -1 : timeDuration > 0 ? 1
|
||||
: 0;
|
||||
return InternalDuration{ std::move(dateDuration), timeDuration };
|
||||
}
|
||||
|
||||
using CheckedInt128 = Checked<Int128, RecordOverflow>;
|
||||
|
||||
static CheckedInt128 checkedCastDoubleToInt128(double n)
|
||||
|
|
@ -1486,6 +1634,31 @@ Optional<TimeZoneID> parseTimeZoneName(String* string)
|
|||
return ret;
|
||||
}
|
||||
|
||||
Int128 resolveNanosecondsValueByUnit(DateTimeUnit unit)
|
||||
{
|
||||
Int128 maximum = 0;
|
||||
constexpr int64_t hoursPerDay = 24;
|
||||
constexpr int64_t minutesPerHour = 60;
|
||||
constexpr int64_t secondsPerMinute = 60;
|
||||
constexpr int64_t msPerDay = hoursPerDay * minutesPerHour * secondsPerMinute * 1000;
|
||||
|
||||
if (unit == DateTimeUnit::Hour) {
|
||||
maximum = static_cast<Int128>(hoursPerDay);
|
||||
} else if (unit == DateTimeUnit::Minute) {
|
||||
maximum = static_cast<Int128>(minutesPerHour * hoursPerDay);
|
||||
} else if (unit == DateTimeUnit::Second) {
|
||||
maximum = static_cast<Int128>(secondsPerMinute * minutesPerHour * hoursPerDay);
|
||||
} else if (unit == DateTimeUnit::Millisecond) {
|
||||
maximum = static_cast<Int128>(msPerDay);
|
||||
} else if (unit == DateTimeUnit::Microsecond) {
|
||||
maximum = static_cast<Int128>(msPerDay * 1000);
|
||||
} else if (unit == DateTimeUnit::Nanosecond) {
|
||||
maximum = ISO8601::ExactTime::nsPerDay;
|
||||
}
|
||||
|
||||
return maximum;
|
||||
}
|
||||
|
||||
} // namespace ISO8601
|
||||
} // namespace Escargot
|
||||
|
||||
|
|
|
|||
|
|
@ -77,6 +77,23 @@ enum class DateTimeUnitCategory {
|
|||
F(microsecond, Microsecond, microseconds, Microseconds, 8, ISO8601::DateTimeUnitCategory::Time) \
|
||||
F(nanosecond, Nanosecond, nanoseconds, Nanoseconds, 9, ISO8601::DateTimeUnitCategory::Time)
|
||||
|
||||
enum class DateTimeUnit : uint8_t {
|
||||
#define DEFINE_TYPE(name, Name, names, Names, index, category) Name,
|
||||
PLAIN_DATETIME_UNITS(DEFINE_TYPE)
|
||||
#undef DEFINE_TYPE
|
||||
};
|
||||
|
||||
DateTimeUnitCategory toDateTimeCategory(DateTimeUnit u);
|
||||
|
||||
DateTimeUnit toDateTimeUnit(String*);
|
||||
inline Optional<DateTimeUnit> toDateTimeUnit(Optional<String*> s)
|
||||
{
|
||||
if (s) {
|
||||
return toDateTimeUnit(s.value());
|
||||
}
|
||||
return NullOption;
|
||||
}
|
||||
|
||||
#define PLAIN_DATE_UNITS(macro) \
|
||||
macro(year, Year) \
|
||||
macro(month, Month) \
|
||||
|
|
@ -90,6 +107,26 @@ enum class DateTimeUnitCategory {
|
|||
macro(microsecond, Microsecond) \
|
||||
macro(nanosecond, Nanosecond)
|
||||
|
||||
enum class RoundingMode : uint8_t {
|
||||
Ceil,
|
||||
Floor,
|
||||
Expand,
|
||||
Trunc,
|
||||
HalfCeil,
|
||||
HalfFloor,
|
||||
HalfExpand,
|
||||
HalfTrunc,
|
||||
HalfEven,
|
||||
};
|
||||
|
||||
enum class UnsignedRoundingMode : uint8_t {
|
||||
Infinity,
|
||||
Zero,
|
||||
HalfInfinity,
|
||||
HalfZero,
|
||||
HalfEven
|
||||
};
|
||||
|
||||
class ExactTime {
|
||||
public:
|
||||
static constexpr Int128 dayRangeSeconds{ 8640000000000 }; // 1e8 days
|
||||
|
|
@ -144,7 +181,7 @@ public:
|
|||
return m_epochNanoseconds >= ExactTime::minValue && m_epochNanoseconds <= ExactTime::maxValue;
|
||||
}
|
||||
|
||||
ExactTime round(ExecutionState& state, unsigned increment, String* unit, String* roundingMode);
|
||||
ExactTime round(ExecutionState& state, unsigned increment, DateTimeUnit unit, RoundingMode roundingMode);
|
||||
|
||||
private:
|
||||
Int128 m_epochNanoseconds{};
|
||||
|
|
@ -154,12 +191,6 @@ class Duration {
|
|||
std::array<double, 10> m_data;
|
||||
|
||||
public:
|
||||
enum class Type : uint8_t {
|
||||
#define DEFINE_TYPE(name, Name, names, Names, index, category) Names,
|
||||
PLAIN_DATETIME_UNITS(DEFINE_TYPE)
|
||||
#undef DEFINE_TYPE
|
||||
};
|
||||
|
||||
// https://tc39.es/proposal-temporal/#sec-temporal-parsetemporaldurationstring
|
||||
static Optional<Duration> parseDurationString(String* input);
|
||||
|
||||
|
|
@ -173,15 +204,49 @@ public:
|
|||
{
|
||||
}
|
||||
|
||||
static String* typeName(ExecutionState& state, Type t);
|
||||
Int128 totalNanoseconds(Duration::Type type) const;
|
||||
Duration(std::initializer_list<double> list)
|
||||
{
|
||||
size_t idx = 0;
|
||||
for (auto n : list) {
|
||||
m_data[idx++] = n;
|
||||
}
|
||||
}
|
||||
|
||||
int sign() const
|
||||
{
|
||||
for (const auto& v : m_data) {
|
||||
if (v < 0) {
|
||||
return -1;
|
||||
}
|
||||
if (v > 0) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// https://tc39.es/proposal-temporal/#sec-temporal-defaulttemporallargestunit
|
||||
DateTimeUnit defaultTemporalLargestUnit() const
|
||||
{
|
||||
size_t idx = 0;
|
||||
for (auto n : m_data) {
|
||||
if (n) {
|
||||
return static_cast<DateTimeUnit>(idx);
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
return DateTimeUnit::Nanosecond;
|
||||
}
|
||||
|
||||
static String* typeName(ExecutionState& state, ISO8601::DateTimeUnit t);
|
||||
Int128 totalNanoseconds(ISO8601::DateTimeUnit type) const;
|
||||
|
||||
double operator[](size_t idx) const
|
||||
{
|
||||
return m_data[static_cast<unsigned>(idx)];
|
||||
}
|
||||
|
||||
double operator[](Duration::Type idx) const
|
||||
double operator[](ISO8601::DateTimeUnit idx) const
|
||||
{
|
||||
return operator[](static_cast<size_t>(idx));
|
||||
}
|
||||
|
|
@ -191,7 +256,7 @@ public:
|
|||
return m_data[static_cast<unsigned>(idx)];
|
||||
}
|
||||
|
||||
double& operator[](Duration::Type idx)
|
||||
double& operator[](ISO8601::DateTimeUnit idx)
|
||||
{
|
||||
return operator[](static_cast<size_t>(idx));
|
||||
}
|
||||
|
|
@ -222,7 +287,7 @@ public:
|
|||
return m_data[static_cast<unsigned>(idx)];
|
||||
}
|
||||
|
||||
Optional<double> operator[](Duration::Type idx) const
|
||||
Optional<double> operator[](ISO8601::DateTimeUnit idx) const
|
||||
{
|
||||
return operator[](static_cast<size_t>(idx));
|
||||
}
|
||||
|
|
@ -232,7 +297,7 @@ public:
|
|||
return m_data[static_cast<unsigned>(idx)];
|
||||
}
|
||||
|
||||
Optional<double>& operator[](Duration::Type idx)
|
||||
Optional<double>& operator[](ISO8601::DateTimeUnit idx)
|
||||
{
|
||||
return operator[](static_cast<size_t>(idx));
|
||||
}
|
||||
|
|
@ -251,6 +316,50 @@ public:
|
|||
#undef DEFINE_SETTER
|
||||
};
|
||||
|
||||
// https://tc39.es/proposal-temporal/#sec-temporal-internal-duration-records
|
||||
// Represents a duration as an ISO8601::Duration (in which all time fields
|
||||
// are ignored) along with an Int128 time duration that represents the sum
|
||||
// of all time fields. Used to avoid losing precision in intermediate calculations.
|
||||
class InternalDuration final {
|
||||
public:
|
||||
InternalDuration(Duration d, Int128 t)
|
||||
: m_dateDuration(d)
|
||||
, m_time(t)
|
||||
{
|
||||
}
|
||||
InternalDuration()
|
||||
: m_dateDuration(Duration())
|
||||
, m_time(0)
|
||||
{
|
||||
}
|
||||
static constexpr Int128 maxTimeDuration = 9007199254740992 * ExactTime::nsPerSecond - 1;
|
||||
|
||||
int32_t sign() const;
|
||||
|
||||
int32_t timeDurationSign() const
|
||||
{
|
||||
return m_time < 0 ? -1 : m_time > 0 ? 1
|
||||
: 0;
|
||||
}
|
||||
|
||||
Int128 time() const { return m_time; }
|
||||
|
||||
Duration dateDuration() const { return m_dateDuration; }
|
||||
|
||||
static InternalDuration combineDateAndTimeDuration(Duration, Int128);
|
||||
|
||||
private:
|
||||
// Time fields are ignored
|
||||
Duration m_dateDuration;
|
||||
|
||||
// A time duration is an integer in the inclusive interval from -maxTimeDuration
|
||||
// to maxTimeDuration, where
|
||||
// maxTimeDuration = 2**53 × 10**9 - 1 = 9,007,199,254,740,991,999,999,999.
|
||||
// It represents the portion of a Temporal.Duration object that deals with time
|
||||
// units, but as a combined value of total nanoseconds.
|
||||
Int128 m_time;
|
||||
};
|
||||
|
||||
class PlainTime {
|
||||
public:
|
||||
PlainTime()
|
||||
|
|
@ -351,18 +460,18 @@ Optional<int64_t> parseUTCOffset(String* string, bool parseSubMinutePrecision =
|
|||
Optional<TimeZoneID> parseTimeZoneName(String* string);
|
||||
Optional<std::tuple<PlainDate, Optional<PlainTime>, Optional<TimeZoneRecord>, Optional<CalendarID>>> parseCalendarDateTime(String* input, bool parseSubMinutePrecisionForTimeZone = true);
|
||||
|
||||
// https://tc39.es/proposal-temporal/#sec-temporal-roundnumbertoincrement
|
||||
double roundNumberToIncrement(double x, double increment, RoundingMode roundingMode);
|
||||
Int128 roundNumberToIncrement(Int128 x, Int128 increment, RoundingMode roundingMode);
|
||||
|
||||
// https://tc39.es/proposal-temporal/#sec-roundNumbertoincrementasifpositive
|
||||
Int128 roundNumberToIncrementAsIfPositive(Int128 x, Int128 increment, String* roundingMode);
|
||||
Int128 roundNumberToIncrementAsIfPositive(Int128 x, Int128 increment, RoundingMode roundingMode);
|
||||
|
||||
// https://tc39.es/proposal-temporal/#sec-getunsignedroundingmode
|
||||
enum class UnsignedRoundingMode : uint8_t {
|
||||
Infinity,
|
||||
Zero,
|
||||
HalfInfinity,
|
||||
HalfZero,
|
||||
HalfEven
|
||||
};
|
||||
UnsignedRoundingMode getUnsignedRoundingMode(String* roundingMode, bool isNegative);
|
||||
UnsignedRoundingMode getUnsignedRoundingMode(RoundingMode roundingMode, bool isNegative);
|
||||
|
||||
Int128 lengthInNanoseconds(DateTimeUnit unit);
|
||||
Int128 resolveNanosecondsValueByUnit(DateTimeUnit unit);
|
||||
|
||||
class TimeConstants {
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -232,14 +232,6 @@
|
|||
<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/negated/basic"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Duration/prototype/negated/branding"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Duration/prototype/negated/builtin"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Duration/prototype/negated/length"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Duration/prototype/negated/name"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Duration/prototype/negated/not-a-constructor"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Duration/prototype/negated/prop-desc"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Duration/prototype/negated/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>
|
||||
|
|
@ -389,43 +381,9 @@
|
|||
<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/balance"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Duration/prototype/toString/balance-subseconds"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Duration/prototype/toString/blank-duration-precision"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Duration/prototype/toString/branding"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-auto"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-exact-number-of-digits"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-invalid-string"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-nan"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-non-integer"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-number"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-out-of-range"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-undefined"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-wrong-type"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Duration/prototype/toString/max-value"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Duration/prototype/toString/negative-components"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Duration/prototype/toString/no-precision-loss"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Duration/prototype/toString/options-object"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Duration/prototype/toString/options-undefined"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Duration/prototype/toString/options-wrong-type"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Duration/prototype/toString/order-of-operations"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Duration/prototype/toString/precision"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Duration/prototype/toString/prop-desc"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Duration/prototype/toString/round-cross-unit-boundary"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Duration/prototype/toString/roundingmode-ceil"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Duration/prototype/toString/roundingmode-floor"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Duration/prototype/toString/roundingmode-halfExpand"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Duration/prototype/toString/roundingmode-invalid-string"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Duration/prototype/toString/roundingmode-trunc"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Duration/prototype/toString/roundingmode-undefined"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Duration/prototype/toString/roundingmode-wrong-type"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Duration/prototype/toString/smallestunit-fractionalseconddigits"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Duration/prototype/toString/smallestunit-invalid-string"><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/smallestunit-undefined"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Duration/prototype/toString/smallestunit-valid-units"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Duration/prototype/toString/smallestunit-wrong-type"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Duration/prototype/toString/throws-when-rounded-duration-is-invalid"><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>
|
||||
<test id="built-ins/Temporal/Duration/prototype/total/balance-subseconds"><reason>TODO</reason></test>
|
||||
|
|
@ -566,70 +524,8 @@
|
|||
<test id="built-ins/Temporal/Instant/prototype/epochMilliseconds/basic"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/equals/argument-zoneddatetime"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/add-subtract"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/argument-object-tostring"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/argument-string-calendar-annotation"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/argument-string-calendar-annotation-invalid-key"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/argument-string-critical-unknown-annotation"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/argument-string-date-with-utc-offset"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/argument-string-invalid"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/argument-string-limits"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/argument-string-minus-sign"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/argument-string-multiple-calendar"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/argument-string-multiple-time-zone"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/argument-string-time-separators"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/argument-string-time-zone-annotation"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/argument-string-unknown-annotation"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/argument-wrong-type"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/argument-zoneddatetime"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/branding"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/builtin"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/instant-string"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/instant-string-multiple-offsets"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/instant-string-sub-minute-offset"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/invalid-increments"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/largest-unit-default"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/largestunit"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/largestunit-invalid-string"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/largestunit-plurals-accepted"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/largestunit-smallestunit-mismatch"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/largestunit-undefined"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/largestunit-wrong-type"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/leap-second"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/length"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/minutes-and-hours"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/name"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/not-a-constructor"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/options-may-be-function"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/options-object"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/options-undefined"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/order-of-operations"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/prop-desc"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/round-cross-unit-boundary"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/rounding-increments"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/roundingincrement-nan"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/roundingincrement-non-integer"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/roundingincrement-out-of-range"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/roundingincrement-undefined"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/roundingincrement-wrong-type"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/roundingmode-ceil"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/roundingmode-expand"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/roundingmode-floor"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/roundingmode-halfCeil"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/roundingmode-halfEven"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/roundingmode-halfExpand"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/roundingmode-halfFloor"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/roundingmode-halfTrunc"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/roundingmode-invalid-string"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/roundingmode-trunc"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/roundingmode-undefined"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/roundingmode-wrong-type"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/smallestunit-invalid-string"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/smallestunit-plurals-accepted"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/smallestunit-undefined"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/smallestunit-wrong-type"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/subseconds"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/valid-increments"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/since/year-zero"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/subtract/argument-duration-max"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/subtract/argument-duration-out-of-range"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/subtract/argument-mixed-sign"><reason>TODO</reason></test>
|
||||
|
|
@ -670,69 +566,8 @@
|
|||
<test id="built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/timezone-wrong-type"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/to-zoned-date-time-iso"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/add-subtract"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/argument-object-tostring"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/argument-string-calendar-annotation"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/argument-string-calendar-annotation-invalid-key"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/argument-string-critical-unknown-annotation"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/argument-string-date-with-utc-offset"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/argument-string-invalid"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/argument-string-limits"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/argument-string-minus-sign"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/argument-string-multiple-calendar"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/argument-string-multiple-time-zone"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/argument-string-time-separators"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/argument-string-time-zone-annotation"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/argument-string-unknown-annotation"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/argument-wrong-type"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/argument-zoneddatetime"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/branding"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/builtin"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/instant-string"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/instant-string-multiple-offsets"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/instant-string-sub-minute-offset"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/invalid-increments"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/largestunit-default"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/largestunit-invalid-string"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/largestunit-plurals-accepted"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/largestunit-smallestunit-mismatch"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/largestunit-undefined"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/largestunit-wrong-type"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/leap-second"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/length"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/minutes-and-hours"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/name"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/not-a-constructor"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/options-may-be-function"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/options-object"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/options-undefined"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/order-of-operations"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/prop-desc"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/round-cross-unit-boundary"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/rounding-increments"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/roundingincrement-nan"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/roundingincrement-non-integer"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/roundingincrement-out-of-range"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/roundingincrement-undefined"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/roundingincrement-wrong-type"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/roundingmode-ceil"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/roundingmode-expand"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/roundingmode-floor"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/roundingmode-halfCeil"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/roundingmode-halfEven"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/roundingmode-halfExpand"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/roundingmode-halfFloor"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/roundingmode-halfTrunc"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/roundingmode-invalid-string"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/roundingmode-trunc"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/roundingmode-undefined"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/roundingmode-wrong-type"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/smallestunit-invalid-string"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/smallestunit-plurals-accepted"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/smallestunit-undefined"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/smallestunit-wrong-type"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/subseconds"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/valid-increments"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Instant/prototype/until/year-zero"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Now/plainDateISO/length"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Now/plainDateISO/prop-desc"><reason>TODO</reason></test>
|
||||
<test id="built-ins/Temporal/Now/plainDateISO/return-value"><reason>TODO</reason></test>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue