mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-21 15:10:19 +00:00
LibJS: Implement mathematical Temporal.Duration prototypes
Includes: Temporal.Duration.prototype.negated Temporal.Duration.prototype.abs Temporal.Duration.prototype.add Temporal.Duration.prototype.subtract
This commit is contained in:
parent
55c81482b0
commit
a80523be18
Notes:
github-actions[bot]
2024-11-21 00:06:00 +00:00
Author: https://github.com/trflynn89 Commit: https://github.com/LadybirdBrowser/ladybird/commit/a80523be18f Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/2431 Reviewed-by: https://github.com/alimpfard Reviewed-by: https://github.com/shannonbooth ✅
11 changed files with 591 additions and 0 deletions
|
@ -261,6 +261,7 @@
|
|||
M(TemporalInvalidInstantString, "Invalid instant string '{}'") \
|
||||
M(TemporalInvalidISODate, "Invalid ISO date") \
|
||||
M(TemporalInvalidISODateTime, "Invalid ISO date time") \
|
||||
M(TemporalInvalidLargestUnit, "Largest unit must not be {}") \
|
||||
M(TemporalInvalidMonthCode, "Invalid month code") \
|
||||
M(TemporalInvalidMonthDayString, "Invalid month day string '{}'") \
|
||||
M(TemporalInvalidMonthDayStringUTCDesignator, "Invalid month day string '{}': must not contain a UTC designator") \
|
||||
|
|
|
@ -55,6 +55,26 @@ ThrowCompletionOr<RelativeTo> get_temporal_relative_to_option(VM& vm, Object con
|
|||
return RelativeTo { .plain_relative_to = {}, .zoned_relative_to = {} };
|
||||
}
|
||||
|
||||
// 13.19 LargerOfTwoTemporalUnits ( u1, u2 ), https://tc39.es/proposal-temporal/#sec-temporal-largeroftwotemporalunits
|
||||
Unit larger_of_two_temporal_units(Unit unit1, Unit unit2)
|
||||
{
|
||||
// 1. For each row of Table 21, except the header row, in table order, do
|
||||
for (auto const& row : temporal_units) {
|
||||
// a. Let unit be the value in the "Value" column of the row.
|
||||
auto unit = row.value;
|
||||
|
||||
// b. If u1 is unit, return unit.
|
||||
if (unit1 == unit)
|
||||
return unit;
|
||||
|
||||
// c. If u2 is unit, return unit.
|
||||
if (unit2 == unit)
|
||||
return unit;
|
||||
}
|
||||
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
// 13.20 IsCalendarUnit ( unit ), https://tc39.es/proposal-temporal/#sec-temporal-iscalendarunit
|
||||
bool is_calendar_unit(Unit unit)
|
||||
{
|
||||
|
|
|
@ -18,6 +18,11 @@
|
|||
|
||||
namespace JS::Temporal {
|
||||
|
||||
enum class ArithmeticOperation {
|
||||
Add,
|
||||
Subtract,
|
||||
};
|
||||
|
||||
// https://tc39.es/proposal-temporal/#sec-temporal-units
|
||||
enum class Unit {
|
||||
Year,
|
||||
|
@ -56,6 +61,7 @@ struct RelativeTo {
|
|||
};
|
||||
|
||||
ThrowCompletionOr<RelativeTo> get_temporal_relative_to_option(VM&, Object const& options);
|
||||
Unit larger_of_two_temporal_units(Unit, Unit);
|
||||
bool is_calendar_unit(Unit);
|
||||
UnitCategory temporal_unit_category(Unit);
|
||||
ThrowCompletionOr<GC::Ref<Duration>> parse_temporal_duration_string(VM&, StringView iso_string);
|
||||
|
|
|
@ -88,6 +88,200 @@ InternalDuration to_internal_duration_record(VM& vm, Duration const& duration)
|
|||
return MUST(combine_date_and_time_duration(vm, date_duration, move(time_duration)));
|
||||
}
|
||||
|
||||
// 7.5.6 ToInternalDurationRecordWith24HourDays ( duration ), https://tc39.es/proposal-temporal/#sec-temporal-tointernaldurationrecordwith24hourdays
|
||||
InternalDuration to_internal_duration_record_with_24_hour_days(VM& vm, Duration const& duration)
|
||||
{
|
||||
// 1. Let timeDuration be TimeDurationFromComponents(duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]], duration.[[Milliseconds]], duration.[[Microseconds]], duration.[[Nanoseconds]]).
|
||||
auto time_duration = time_duration_from_components(duration.hours(), duration.minutes(), duration.seconds(), duration.milliseconds(), duration.microseconds(), duration.nanoseconds());
|
||||
|
||||
// 2. Set timeDuration to ! Add24HourDaysToTimeDuration(timeDuration, duration.[[Days]]).
|
||||
time_duration = MUST(add_24_hour_days_to_time_duration(vm, time_duration, duration.days()));
|
||||
|
||||
// 3. Let dateDuration be ! CreateDateDurationRecord(duration.[[Years]], duration.[[Months]], duration.[[Weeks]], 0).
|
||||
auto date_duration = MUST(create_date_duration_record(vm, duration.years(), duration.months(), duration.weeks(), 0));
|
||||
|
||||
// 4. Return ! CombineDateAndTimeDuration(dateDuration, timeDuration).
|
||||
return MUST(combine_date_and_time_duration(vm, date_duration, move(time_duration)));
|
||||
}
|
||||
|
||||
// 7.5.8 TemporalDurationFromInternal ( internalDuration, largestUnit ), https://tc39.es/proposal-temporal/#sec-temporal-temporaldurationfrominternal
|
||||
ThrowCompletionOr<GC::Ref<Duration>> temporal_duration_from_internal(VM& vm, InternalDuration const& internal_duration, Unit largest_unit)
|
||||
{
|
||||
// 1. Let days, hours, minutes, seconds, milliseconds, and microseconds be 0.
|
||||
double days = 0;
|
||||
double hours = 0;
|
||||
double minutes = 0;
|
||||
double seconds = 0;
|
||||
double milliseconds = 0;
|
||||
double microseconds = 0;
|
||||
|
||||
// 2. Let sign be TimeDurationSign(internalDuration.[[Time]]).
|
||||
auto sign = time_duration_sign(internal_duration.time);
|
||||
|
||||
// 3. Let nanoseconds be abs(internalDuration.[[Time]]).
|
||||
auto const& absolute_nanoseconds = internal_duration.time.unsigned_value();
|
||||
double nanoseconds = 0;
|
||||
|
||||
// 4. If TemporalUnitCategory(largestUnit) is date, then
|
||||
if (temporal_unit_category(largest_unit) == UnitCategory::Date) {
|
||||
// a. Set microseconds to floor(nanoseconds / 1000).
|
||||
auto nanoseconds_division_result = absolute_nanoseconds.divided_by(NANOSECONDS_PER_MICROSECOND);
|
||||
|
||||
// b. Set nanoseconds to nanoseconds modulo 1000.
|
||||
nanoseconds = nanoseconds_division_result.remainder.to_double();
|
||||
|
||||
// c. Set milliseconds to floor(microseconds / 1000).
|
||||
auto microseconds_division_result = nanoseconds_division_result.quotient.divided_by(MICROSECONDS_PER_MILLISECOND);
|
||||
|
||||
// d. Set microseconds to microseconds modulo 1000.
|
||||
microseconds = microseconds_division_result.remainder.to_double();
|
||||
|
||||
// e. Set seconds to floor(milliseconds / 1000).
|
||||
auto milliseconds_division_result = microseconds_division_result.quotient.divided_by(MILLISECONDS_PER_SECOND);
|
||||
|
||||
// f. Set milliseconds to milliseconds modulo 1000.
|
||||
milliseconds = milliseconds_division_result.remainder.to_double();
|
||||
|
||||
// g. Set minutes to floor(seconds / 60).
|
||||
auto seconds_division_result = milliseconds_division_result.quotient.divided_by(SECONDS_PER_MINUTE);
|
||||
|
||||
// h. Set seconds to seconds modulo 60.
|
||||
seconds = seconds_division_result.remainder.to_double();
|
||||
|
||||
// i. Set hours to floor(minutes / 60).
|
||||
auto minutes_division_result = seconds_division_result.quotient.divided_by(MINUTES_PER_HOUR);
|
||||
|
||||
// j. Set minutes to minutes modulo 60.
|
||||
minutes = minutes_division_result.remainder.to_double();
|
||||
|
||||
// k. Set days to floor(hours / 24).
|
||||
auto hours_division_result = minutes_division_result.quotient.divided_by(HOURS_PER_DAY);
|
||||
days = hours_division_result.quotient.to_double();
|
||||
|
||||
// l. Set hours to hours modulo 24.
|
||||
hours = hours_division_result.remainder.to_double();
|
||||
}
|
||||
// 5. Else if largestUnit is hour, then
|
||||
else if (largest_unit == Unit::Hour) {
|
||||
// a. Set microseconds to floor(nanoseconds / 1000).
|
||||
auto nanoseconds_division_result = absolute_nanoseconds.divided_by(NANOSECONDS_PER_MICROSECOND);
|
||||
|
||||
// b. Set nanoseconds to nanoseconds modulo 1000.
|
||||
nanoseconds = nanoseconds_division_result.remainder.to_double();
|
||||
|
||||
// c. Set milliseconds to floor(microseconds / 1000).
|
||||
auto microseconds_division_result = nanoseconds_division_result.quotient.divided_by(MICROSECONDS_PER_MILLISECOND);
|
||||
|
||||
// d. Set microseconds to microseconds modulo 1000.
|
||||
microseconds = microseconds_division_result.remainder.to_double();
|
||||
|
||||
// e. Set seconds to floor(milliseconds / 1000).
|
||||
auto milliseconds_division_result = microseconds_division_result.quotient.divided_by(MILLISECONDS_PER_SECOND);
|
||||
|
||||
// f. Set milliseconds to milliseconds modulo 1000.
|
||||
milliseconds = milliseconds_division_result.remainder.to_double();
|
||||
|
||||
// g. Set minutes to floor(seconds / 60).
|
||||
auto seconds_division_result = milliseconds_division_result.quotient.divided_by(SECONDS_PER_MINUTE);
|
||||
|
||||
// h. Set seconds to seconds modulo 60.
|
||||
seconds = seconds_division_result.remainder.to_double();
|
||||
|
||||
// i. Set hours to floor(minutes / 60).
|
||||
auto minutes_division_result = seconds_division_result.quotient.divided_by(MINUTES_PER_HOUR);
|
||||
hours = minutes_division_result.quotient.to_double();
|
||||
|
||||
// j. Set minutes to minutes modulo 60.
|
||||
minutes = minutes_division_result.remainder.to_double();
|
||||
}
|
||||
// 6. Else if largestUnit is minute, then
|
||||
else if (largest_unit == Unit::Minute) {
|
||||
// a. Set microseconds to floor(nanoseconds / 1000).
|
||||
auto nanoseconds_division_result = absolute_nanoseconds.divided_by(Crypto::UnsignedBigInteger(NANOSECONDS_PER_MICROSECOND));
|
||||
|
||||
// b. Set nanoseconds to nanoseconds modulo 1000.
|
||||
nanoseconds = nanoseconds_division_result.remainder.to_double();
|
||||
|
||||
// c. Set milliseconds to floor(microseconds / 1000).
|
||||
auto microseconds_division_result = nanoseconds_division_result.quotient.divided_by(MICROSECONDS_PER_MILLISECOND);
|
||||
|
||||
// d. Set microseconds to microseconds modulo 1000.
|
||||
microseconds = microseconds_division_result.remainder.to_double();
|
||||
|
||||
// e. Set seconds to floor(milliseconds / 1000).
|
||||
auto milliseconds_division_result = microseconds_division_result.quotient.divided_by(MILLISECONDS_PER_SECOND);
|
||||
|
||||
// f. Set milliseconds to milliseconds modulo 1000.
|
||||
milliseconds = milliseconds_division_result.remainder.to_double();
|
||||
|
||||
// g. Set minutes to floor(seconds / 60).
|
||||
auto seconds_division_result = milliseconds_division_result.quotient.divided_by(SECONDS_PER_MINUTE);
|
||||
minutes = seconds_division_result.quotient.to_double();
|
||||
|
||||
// h. Set seconds to seconds modulo 60.
|
||||
seconds = seconds_division_result.remainder.to_double();
|
||||
}
|
||||
// 7. Else if largestUnit is second, then
|
||||
else if (largest_unit == Unit::Second) {
|
||||
// a. Set microseconds to floor(nanoseconds / 1000).
|
||||
auto nanoseconds_division_result = absolute_nanoseconds.divided_by(NANOSECONDS_PER_MICROSECOND);
|
||||
|
||||
// b. Set nanoseconds to nanoseconds modulo 1000.
|
||||
nanoseconds = nanoseconds_division_result.remainder.to_double();
|
||||
|
||||
// c. Set milliseconds to floor(microseconds / 1000).
|
||||
auto microseconds_division_result = nanoseconds_division_result.quotient.divided_by(MICROSECONDS_PER_MILLISECOND);
|
||||
|
||||
// d. Set microseconds to microseconds modulo 1000.
|
||||
microseconds = microseconds_division_result.remainder.to_double();
|
||||
|
||||
// e. Set seconds to floor(milliseconds / 1000).
|
||||
auto milliseconds_division_result = microseconds_division_result.quotient.divided_by(MILLISECONDS_PER_SECOND);
|
||||
seconds = milliseconds_division_result.quotient.to_double();
|
||||
|
||||
// f. Set milliseconds to milliseconds modulo 1000.
|
||||
milliseconds = milliseconds_division_result.remainder.to_double();
|
||||
}
|
||||
// 8. Else if largestUnit is millisecond, then
|
||||
else if (largest_unit == Unit::Millisecond) {
|
||||
// a. Set microseconds to floor(nanoseconds / 1000).
|
||||
auto nanoseconds_division_result = absolute_nanoseconds.divided_by(NANOSECONDS_PER_MICROSECOND);
|
||||
|
||||
// b. Set nanoseconds to nanoseconds modulo 1000.
|
||||
nanoseconds = nanoseconds_division_result.remainder.to_double();
|
||||
|
||||
// c. Set milliseconds to floor(microseconds / 1000).
|
||||
auto microseconds_division_result = nanoseconds_division_result.quotient.divided_by(MICROSECONDS_PER_MILLISECOND);
|
||||
milliseconds = microseconds_division_result.quotient.to_double();
|
||||
|
||||
// d. Set microseconds to microseconds modulo 1000.
|
||||
microseconds = microseconds_division_result.remainder.to_double();
|
||||
}
|
||||
// 9. Else if largestUnit is microsecond, then
|
||||
else if (largest_unit == Unit::Microsecond) {
|
||||
// a. Set microseconds to floor(nanoseconds / 1000).
|
||||
auto nanoseconds_division_result = absolute_nanoseconds.divided_by(NANOSECONDS_PER_MICROSECOND);
|
||||
microseconds = nanoseconds_division_result.quotient.to_double();
|
||||
|
||||
// b. Set nanoseconds to nanoseconds modulo 1000.
|
||||
nanoseconds = nanoseconds_division_result.remainder.to_double();
|
||||
}
|
||||
// 10. Else,
|
||||
else {
|
||||
// a. Assert: largestUnit is nanosecond.
|
||||
VERIFY(largest_unit == Unit::Nanosecond);
|
||||
nanoseconds = absolute_nanoseconds.to_double();
|
||||
}
|
||||
|
||||
// 11. NOTE: When largestUnit is millisecond, microsecond, or nanosecond, milliseconds, microseconds, or nanoseconds
|
||||
// may be an unsafe integer. In this case, care must be taken when implementing the calculation using floating
|
||||
// point arithmetic. It can be implemented in C++ using std::fma(). String manipulation will also give an exact
|
||||
// result, since the multiplication is by a power of 10.
|
||||
|
||||
// 12. 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).
|
||||
return TRY(create_temporal_duration(vm, internal_duration.date.years, internal_duration.date.months, internal_duration.date.weeks, internal_duration.date.days + (days * sign), hours * sign, minutes * sign, seconds * sign, milliseconds * sign, microseconds * sign, nanoseconds * sign));
|
||||
}
|
||||
|
||||
// 7.5.9 CreateDateDurationRecord ( years, months, weeks, days ), https://tc39.es/proposal-temporal/#sec-temporal-createdatedurationrecord
|
||||
ThrowCompletionOr<DateDuration> create_date_duration_record(VM& vm, double years, double months, double weeks, double days)
|
||||
{
|
||||
|
@ -432,6 +626,13 @@ ThrowCompletionOr<GC::Ref<Duration>> create_temporal_duration(VM& vm, double yea
|
|||
return object;
|
||||
}
|
||||
|
||||
// 7.5.20 CreateNegatedTemporalDuration ( duration ), https://tc39.es/proposal-temporal/#sec-temporal-createnegatedtemporalduration
|
||||
GC::Ref<Duration> create_negated_temporal_duration(VM& vm, Duration const& duration)
|
||||
{
|
||||
// 1. Return ! CreateTemporalDuration(-duration.[[Years]], -duration.[[Months]], -duration.[[Weeks]], -duration.[[Days]], -duration.[[Hours]], -duration.[[Minutes]], -duration.[[Seconds]], -duration.[[Milliseconds]], -duration.[[Microseconds]], -duration.[[Nanoseconds]]).
|
||||
return MUST(create_temporal_duration(vm, -duration.years(), -duration.months(), -duration.weeks(), -duration.days(), -duration.hours(), -duration.minutes(), -duration.seconds(), -duration.milliseconds(), -duration.microseconds(), -duration.nanoseconds()));
|
||||
}
|
||||
|
||||
// 7.5.21 TimeDurationFromComponents ( hours, minutes, seconds, milliseconds, microseconds, nanoseconds ), https://tc39.es/proposal-temporal/#sec-temporal-timedurationfromcomponents
|
||||
TimeDuration time_duration_from_components(double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds)
|
||||
{
|
||||
|
@ -457,6 +658,20 @@ TimeDuration time_duration_from_components(double hours, double minutes, double
|
|||
return total_nanoseconds;
|
||||
}
|
||||
|
||||
// 7.5.22 AddTimeDuration ( one, two ), https://tc39.es/proposal-temporal/#sec-temporal-addtimeduration
|
||||
ThrowCompletionOr<TimeDuration> add_time_duration(VM& vm, TimeDuration const& one, TimeDuration const& two)
|
||||
{
|
||||
// 1. Let result be one + two.
|
||||
auto result = one.plus(two);
|
||||
|
||||
// 2. If abs(result) > maxTimeDuration, throw a RangeError exception.
|
||||
if (result.unsigned_value() > MAX_TIME_DURATION.unsigned_value())
|
||||
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidDuration);
|
||||
|
||||
// 3. Return result.
|
||||
return result;
|
||||
}
|
||||
|
||||
// 7.5.23 Add24HourDaysToTimeDuration ( d, days ), https://tc39.es/proposal-temporal/#sec-temporal-add24hourdaystonormalizedtimeduration
|
||||
ThrowCompletionOr<TimeDuration> add_24_hour_days_to_time_duration(VM& vm, TimeDuration const& time_duration, double days)
|
||||
{
|
||||
|
@ -501,4 +716,43 @@ i8 time_duration_sign(TimeDuration const& time_duration)
|
|||
return 0;
|
||||
}
|
||||
|
||||
// 7.5.40 AddDurations ( operation, duration, other ), https://tc39.es/proposal-temporal/#sec-temporal-adddurations
|
||||
ThrowCompletionOr<GC::Ref<Duration>> add_durations(VM& vm, ArithmeticOperation operation, Duration const& duration, Value other_value)
|
||||
{
|
||||
// 1. Set other to ? ToTemporalDuration(other).
|
||||
auto other = TRY(to_temporal_duration(vm, other_value));
|
||||
|
||||
// 2. If operation is subtract, set other to CreateNegatedTemporalDuration(other).
|
||||
if (operation == ArithmeticOperation::Subtract)
|
||||
other = create_negated_temporal_duration(vm, other);
|
||||
|
||||
// 3. Let largestUnit1 be DefaultTemporalLargestUnit(duration).
|
||||
auto largest_unit1 = default_temporal_largest_unit(duration);
|
||||
|
||||
// 4. Let largestUnit2 be DefaultTemporalLargestUnit(other).
|
||||
auto largest_unit2 = default_temporal_largest_unit(other);
|
||||
|
||||
// 5. Let largestUnit be LargerOfTwoTemporalUnits(largestUnit1, largestUnit2).
|
||||
auto largest_unit = larger_of_two_temporal_units(largest_unit1, largest_unit2);
|
||||
|
||||
// 6. If IsCalendarUnit(largestUnit) is true, throw a RangeError exception.
|
||||
if (is_calendar_unit(largest_unit))
|
||||
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidLargestUnit, "a calendar unit"sv);
|
||||
|
||||
// 7. Let d1 be ToInternalDurationRecordWith24HourDays(duration).
|
||||
auto duration1 = to_internal_duration_record_with_24_hour_days(vm, duration);
|
||||
|
||||
// 8. Let d2 be ToInternalDurationRecordWith24HourDays(other).
|
||||
auto duration2 = to_internal_duration_record_with_24_hour_days(vm, other);
|
||||
|
||||
// 9. Let timeResult be ? AddTimeDuration(d1.[[Time]], d2.[[Time]]).
|
||||
auto time_result = TRY(add_time_duration(vm, duration1.time, duration2.time));
|
||||
|
||||
// 10. Let result be ! CombineDateAndTimeDuration(ZeroDateDuration(), timeResult).
|
||||
auto result = MUST(combine_date_and_time_duration(vm, zero_date_duration(vm), move(time_result)));
|
||||
|
||||
// 11. Return ? TemporalDurationFromInternal(result, largestUnit).
|
||||
return TRY(temporal_duration_from_internal(vm, result, largest_unit));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -102,6 +102,8 @@ struct InternalDuration {
|
|||
|
||||
DateDuration zero_date_duration(VM&);
|
||||
InternalDuration to_internal_duration_record(VM&, Duration const&);
|
||||
InternalDuration to_internal_duration_record_with_24_hour_days(VM&, Duration const&);
|
||||
ThrowCompletionOr<GC::Ref<Duration>> temporal_duration_from_internal(VM&, InternalDuration const&, Unit largest_unit);
|
||||
ThrowCompletionOr<DateDuration> create_date_duration_record(VM&, double years, double months, double weeks, double days);
|
||||
ThrowCompletionOr<InternalDuration> combine_date_and_time_duration(VM&, DateDuration, TimeDuration);
|
||||
ThrowCompletionOr<GC::Ref<Duration>> to_temporal_duration(VM&, Value);
|
||||
|
@ -111,9 +113,12 @@ bool is_valid_duration(double years, double months, double weeks, double days, d
|
|||
Unit default_temporal_largest_unit(Duration const&);
|
||||
ThrowCompletionOr<PartialDuration> to_temporal_partial_duration_record(VM&, Value temporal_duration_like);
|
||||
ThrowCompletionOr<GC::Ref<Duration>> create_temporal_duration(VM&, double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds, GC::Ptr<FunctionObject> new_target = {});
|
||||
GC::Ref<Duration> create_negated_temporal_duration(VM&, Duration const&);
|
||||
TimeDuration time_duration_from_components(double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds);
|
||||
ThrowCompletionOr<TimeDuration> add_time_duration(VM&, TimeDuration const&, TimeDuration const&);
|
||||
ThrowCompletionOr<TimeDuration> add_24_hour_days_to_time_duration(VM&, TimeDuration const&, double days);
|
||||
i8 compare_time_duration(TimeDuration const&, TimeDuration const&);
|
||||
i8 time_duration_sign(TimeDuration const&);
|
||||
ThrowCompletionOr<GC::Ref<Duration>> add_durations(VM&, ArithmeticOperation, Duration const&, Value);
|
||||
|
||||
}
|
||||
|
|
|
@ -37,6 +37,10 @@ void DurationPrototype::initialize(Realm& realm)
|
|||
|
||||
u8 attr = Attribute::Writable | Attribute::Configurable;
|
||||
define_native_function(realm, vm.names.with, with, 1, attr);
|
||||
define_native_function(realm, vm.names.negated, negated, 0, attr);
|
||||
define_native_function(realm, vm.names.abs, abs, 0, attr);
|
||||
define_native_function(realm, vm.names.add, add, 1, attr);
|
||||
define_native_function(realm, vm.names.subtract, subtract, 1, attr);
|
||||
}
|
||||
|
||||
// 7.3.3 get Temporal.Duration.prototype.years, https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.years
|
||||
|
@ -162,4 +166,52 @@ JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::with)
|
|||
return TRY(create_temporal_duration(vm, years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds));
|
||||
}
|
||||
|
||||
// 7.3.16 Temporal.Duration.prototype.negated ( ), https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.negated
|
||||
JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::negated)
|
||||
{
|
||||
// 1. Let duration be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
|
||||
auto duration = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Return CreateNegatedTemporalDuration(duration).
|
||||
return create_negated_temporal_duration(vm, duration);
|
||||
}
|
||||
|
||||
// 7.3.17 Temporal.Duration.prototype.abs ( ), https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.abs
|
||||
JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::abs)
|
||||
{
|
||||
// 1. Let duration be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
|
||||
auto duration = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Return ! CreateTemporalDuration(abs(duration.[[Years]]), abs(duration.[[Months]]), abs(duration.[[Weeks]]), abs(duration.[[Days]]), abs(duration.[[Hours]]), abs(duration.[[Minutes]]), abs(duration.[[Seconds]]), abs(duration.[[Milliseconds]]), abs(duration.[[Microseconds]]), abs(duration.[[Nanoseconds]])).
|
||||
return MUST(create_temporal_duration(vm, fabs(duration->years()), fabs(duration->months()), fabs(duration->weeks()), fabs(duration->days()), fabs(duration->hours()), fabs(duration->minutes()), fabs(duration->seconds()), fabs(duration->milliseconds()), fabs(duration->microseconds()), fabs(duration->nanoseconds())));
|
||||
}
|
||||
|
||||
// 7.3.18 Temporal.Duration.prototype.add ( other ), https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.add
|
||||
JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::add)
|
||||
{
|
||||
auto other = vm.argument(0);
|
||||
|
||||
// 1. Let duration be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
|
||||
auto duration = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Return ? AddDurations(ADD, duration, other).
|
||||
return TRY(add_durations(vm, ArithmeticOperation::Add, duration, other));
|
||||
}
|
||||
|
||||
// 7.3.19 Temporal.Duration.prototype.subtract ( other ), https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.subtract
|
||||
JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::subtract)
|
||||
{
|
||||
auto other = vm.argument(0);
|
||||
|
||||
// 1. Let duration be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
|
||||
auto duration = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Return ? AddDurations(SUBTRACT, duration, other).
|
||||
return TRY(add_durations(vm, ArithmeticOperation::Subtract, duration, other));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -31,6 +31,10 @@ private:
|
|||
JS_DECLARE_NATIVE_FUNCTION(sign_getter);
|
||||
JS_DECLARE_NATIVE_FUNCTION(blank_getter);
|
||||
JS_DECLARE_NATIVE_FUNCTION(with);
|
||||
JS_DECLARE_NATIVE_FUNCTION(negated);
|
||||
JS_DECLARE_NATIVE_FUNCTION(abs);
|
||||
JS_DECLARE_NATIVE_FUNCTION(add);
|
||||
JS_DECLARE_NATIVE_FUNCTION(subtract);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
const DURATION_PROPERTIES = [
|
||||
"years",
|
||||
"months",
|
||||
"weeks",
|
||||
"days",
|
||||
"hours",
|
||||
"minutes",
|
||||
"seconds",
|
||||
"milliseconds",
|
||||
"microseconds",
|
||||
"nanoseconds",
|
||||
];
|
||||
|
||||
describe("correct behavior", () => {
|
||||
test("length is 0", () => {
|
||||
expect(Temporal.Duration.prototype.abs).toHaveLength(0);
|
||||
});
|
||||
|
||||
test("basic functionality", () => {
|
||||
let absoluteDuration;
|
||||
|
||||
absoluteDuration = new Temporal.Duration(123).abs();
|
||||
expect(absoluteDuration.years).toBe(123);
|
||||
|
||||
absoluteDuration = new Temporal.Duration(-123).abs();
|
||||
expect(absoluteDuration.years).toBe(123);
|
||||
});
|
||||
|
||||
test("each property is made absolute", () => {
|
||||
let values;
|
||||
let duration;
|
||||
|
||||
values = Array(DURATION_PROPERTIES.length).fill(-1);
|
||||
duration = new Temporal.Duration(...values).abs();
|
||||
for (const property of DURATION_PROPERTIES) {
|
||||
expect(duration[property]).toBe(1);
|
||||
}
|
||||
|
||||
values = Array(DURATION_PROPERTIES.length).fill(1);
|
||||
duration = new Temporal.Duration(...values).abs();
|
||||
for (const property of DURATION_PROPERTIES) {
|
||||
expect(duration[property]).toBe(1);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("this value must be a Temporal.Duration object", () => {
|
||||
expect(() => {
|
||||
Temporal.Duration.prototype.abs.call("foo");
|
||||
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.Duration");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,77 @@
|
|||
describe("correct behavior", () => {
|
||||
test("length is 1", () => {
|
||||
expect(Temporal.Duration.prototype.add).toHaveLength(1);
|
||||
});
|
||||
|
||||
function checkCommonResults(durationResult) {
|
||||
expect(durationResult.years).toBe(0);
|
||||
expect(durationResult.months).toBe(0);
|
||||
expect(durationResult.weeks).toBe(0);
|
||||
expect(durationResult.days).toBe(3);
|
||||
expect(durationResult.hours).toBe(3);
|
||||
expect(durationResult.minutes).toBe(3);
|
||||
expect(durationResult.seconds).toBe(3);
|
||||
expect(durationResult.milliseconds).toBe(3);
|
||||
expect(durationResult.microseconds).toBe(3);
|
||||
expect(durationResult.nanoseconds).toBe(3);
|
||||
}
|
||||
|
||||
test("basic functionality", () => {
|
||||
const duration = new Temporal.Duration(0, 0, 0, 2, 2, 2, 2, 2, 2, 2);
|
||||
const oneDuration = new Temporal.Duration(0, 0, 0, 1, 1, 1, 1, 1, 1, 1);
|
||||
const durationResult = duration.add(oneDuration);
|
||||
|
||||
checkCommonResults(durationResult);
|
||||
});
|
||||
|
||||
test("from duration-like", () => {
|
||||
const duration = new Temporal.Duration(0, 0, 0, 2, 2, 2, 2, 2, 2, 2);
|
||||
const oneDuration = {
|
||||
days: 1,
|
||||
hours: 1,
|
||||
minutes: 1,
|
||||
seconds: 1,
|
||||
milliseconds: 1,
|
||||
microseconds: 1,
|
||||
nanoseconds: 1,
|
||||
};
|
||||
const durationResult = duration.add(oneDuration);
|
||||
|
||||
checkCommonResults(durationResult);
|
||||
});
|
||||
|
||||
test("from string", () => {
|
||||
const duration = new Temporal.Duration(0, 0, 0, 2, 2, 2, 2, 2, 2, 2);
|
||||
const oneDuration = "P1DT1H1M1.001001001S";
|
||||
const durationResult = duration.add(oneDuration);
|
||||
|
||||
checkCommonResults(durationResult);
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("this value must be a Temporal.Duration object", () => {
|
||||
expect(() => {
|
||||
Temporal.Duration.prototype.add.call("foo");
|
||||
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.Duration");
|
||||
});
|
||||
|
||||
test("relativeTo is required when duration has calendar units", () => {
|
||||
const yearDuration = new Temporal.Duration(1);
|
||||
const monthDuration = new Temporal.Duration(0, 1);
|
||||
const weekDuration = new Temporal.Duration(0, 0, 1);
|
||||
const durationToAdd = { seconds: 1 };
|
||||
|
||||
expect(() => {
|
||||
yearDuration.add(durationToAdd);
|
||||
}).toThrowWithMessage(RangeError, "Largest unit must not be a calendar unit");
|
||||
|
||||
expect(() => {
|
||||
monthDuration.add(durationToAdd);
|
||||
}).toThrowWithMessage(RangeError, "Largest unit must not be a calendar unit");
|
||||
|
||||
expect(() => {
|
||||
weekDuration.add(durationToAdd);
|
||||
}).toThrowWithMessage(RangeError, "Largest unit must not be a calendar unit");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,42 @@
|
|||
const DURATION_PROPERTIES = [
|
||||
"years",
|
||||
"months",
|
||||
"weeks",
|
||||
"days",
|
||||
"hours",
|
||||
"minutes",
|
||||
"seconds",
|
||||
"milliseconds",
|
||||
"microseconds",
|
||||
"nanoseconds",
|
||||
];
|
||||
|
||||
describe("correct behavior", () => {
|
||||
test("length is 0", () => {
|
||||
expect(Temporal.Duration.prototype.negated).toHaveLength(0);
|
||||
});
|
||||
|
||||
test("basic functionality", () => {
|
||||
const negativeDuration = new Temporal.Duration(123).negated();
|
||||
expect(negativeDuration.years).toBe(-123);
|
||||
|
||||
const positiveDuration = new Temporal.Duration(-123).negated();
|
||||
expect(positiveDuration.years).toBe(123);
|
||||
});
|
||||
|
||||
test("each property is negated", () => {
|
||||
const values = Array(DURATION_PROPERTIES.length).fill(1);
|
||||
const duration = new Temporal.Duration(...values).negated();
|
||||
for (const property of DURATION_PROPERTIES) {
|
||||
expect(duration[property]).toBe(-1);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("this value must be a Temporal.Duration object", () => {
|
||||
expect(() => {
|
||||
Temporal.Duration.prototype.negated.call("foo");
|
||||
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.Duration");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,77 @@
|
|||
describe("correct behavior", () => {
|
||||
test("length is 1", () => {
|
||||
expect(Temporal.Duration.prototype.subtract).toHaveLength(1);
|
||||
});
|
||||
|
||||
function checkCommonResults(durationResult) {
|
||||
expect(durationResult.years).toBe(0);
|
||||
expect(durationResult.months).toBe(0);
|
||||
expect(durationResult.weeks).toBe(0);
|
||||
expect(durationResult.days).toBe(1);
|
||||
expect(durationResult.hours).toBe(1);
|
||||
expect(durationResult.minutes).toBe(1);
|
||||
expect(durationResult.seconds).toBe(1);
|
||||
expect(durationResult.milliseconds).toBe(1);
|
||||
expect(durationResult.microseconds).toBe(1);
|
||||
expect(durationResult.nanoseconds).toBe(1);
|
||||
}
|
||||
|
||||
test("basic functionality", () => {
|
||||
const duration = new Temporal.Duration(0, 0, 0, 2, 2, 2, 2, 2, 2, 2);
|
||||
const oneDuration = new Temporal.Duration(0, 0, 0, 1, 1, 1, 1, 1, 1, 1);
|
||||
const durationResult = duration.subtract(oneDuration);
|
||||
|
||||
checkCommonResults(durationResult);
|
||||
});
|
||||
|
||||
test("from duration-like", () => {
|
||||
const duration = new Temporal.Duration(0, 0, 0, 2, 2, 2, 2, 2, 2, 2);
|
||||
const oneDuration = {
|
||||
days: 1,
|
||||
hours: 1,
|
||||
minutes: 1,
|
||||
seconds: 1,
|
||||
milliseconds: 1,
|
||||
microseconds: 1,
|
||||
nanoseconds: 1,
|
||||
};
|
||||
const durationResult = duration.subtract(oneDuration);
|
||||
|
||||
checkCommonResults(durationResult);
|
||||
});
|
||||
|
||||
test("from string", () => {
|
||||
const duration = new Temporal.Duration(0, 0, 0, 2, 2, 2, 2, 2, 2, 2);
|
||||
const oneDuration = "P1DT1H1M1.001001001S";
|
||||
const durationResult = duration.subtract(oneDuration);
|
||||
|
||||
checkCommonResults(durationResult);
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("this value must be a Temporal.Duration object", () => {
|
||||
expect(() => {
|
||||
Temporal.Duration.prototype.subtract.call("foo");
|
||||
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.Duration");
|
||||
});
|
||||
|
||||
test("relativeTo is required when duration has calendar units", () => {
|
||||
const yearDuration = new Temporal.Duration(1);
|
||||
const monthDuration = new Temporal.Duration(0, 1);
|
||||
const weekDuration = new Temporal.Duration(0, 0, 1);
|
||||
const durationToSubtract = { seconds: 1 };
|
||||
|
||||
expect(() => {
|
||||
yearDuration.subtract(durationToSubtract);
|
||||
}).toThrowWithMessage(RangeError, "Largest unit must not be a calendar unit");
|
||||
|
||||
expect(() => {
|
||||
monthDuration.subtract(durationToSubtract);
|
||||
}).toThrowWithMessage(RangeError, "Largest unit must not be a calendar unit");
|
||||
|
||||
expect(() => {
|
||||
weekDuration.subtract(durationToSubtract);
|
||||
}).toThrowWithMessage(RangeError, "Largest unit must not be a calendar unit");
|
||||
});
|
||||
});
|
Loading…
Reference in a new issue