mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 16:27:35 +00:00
LibJS: Refactor Temporal since/until to common AOs
This is an editorial change in the Temporal spec.
See: 85a9f57
This commit is contained in:
parent
fc6cf3cb9d
commit
cc8f5151d7
19 changed files with 493 additions and 612 deletions
|
@ -22,6 +22,11 @@ enum class ArithmeticOperation {
|
||||||
Subtract,
|
Subtract,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class DifferenceOperation {
|
||||||
|
Since,
|
||||||
|
Until,
|
||||||
|
};
|
||||||
|
|
||||||
enum class OptionType {
|
enum class OptionType {
|
||||||
Boolean,
|
Boolean,
|
||||||
String,
|
String,
|
||||||
|
|
|
@ -281,7 +281,69 @@ ThrowCompletionOr<String> temporal_instant_to_string(GlobalObject& global_object
|
||||||
return String::formatted("{}{}", date_time_string, time_zone_string);
|
return String::formatted("{}{}", date_time_string, time_zone_string);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 8.5.10 AddDurationToOrSubtractDurationFromInstant ( operation, instant, temporalDurationLike ), https://tc39.es/proposal-temporal/#sec-temporal-adddurationtoorsubtractdurationfrominstant
|
// 8.5.10 DifferenceTemporalInstant ( operation, instant, other, options ), https://tc39.es/proposal-temporal/#sec-temporal-differencetemporalinstant
|
||||||
|
ThrowCompletionOr<Duration*> difference_temporal_instant(GlobalObject& global_object, DifferenceOperation operation, Instant const& instant, Value other_value, Value options_value)
|
||||||
|
{
|
||||||
|
// 1. Set other to ? ToTemporalInstant(other).
|
||||||
|
auto* other = TRY(to_temporal_instant(global_object, other_value));
|
||||||
|
|
||||||
|
Instant const* first;
|
||||||
|
Instant const* second;
|
||||||
|
|
||||||
|
// 2. If operation is until, then
|
||||||
|
if (operation == DifferenceOperation::Until) {
|
||||||
|
// a. Let first be instant.
|
||||||
|
first = &instant;
|
||||||
|
|
||||||
|
// b. Let second be other.
|
||||||
|
second = other;
|
||||||
|
}
|
||||||
|
// 3. Else,
|
||||||
|
else {
|
||||||
|
// a. Let first be other.
|
||||||
|
first = other;
|
||||||
|
|
||||||
|
// b. Let second be instant.
|
||||||
|
second = &instant;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Set options to ? GetOptionsObject(options).
|
||||||
|
auto const* options = TRY(get_options_object(global_object, options_value));
|
||||||
|
|
||||||
|
// 5. Let smallestUnit be ? ToSmallestTemporalUnit(options, « "year", "month", "week", "day" », "nanosecond").
|
||||||
|
auto smallest_unit = TRY(to_smallest_temporal_unit(global_object, *options, { "year"sv, "month"sv, "week"sv, "day"sv }, "nanosecond"sv));
|
||||||
|
|
||||||
|
// 6. Let defaultLargestUnit be ! LargerOfTwoTemporalUnits("second", smallestUnit).
|
||||||
|
auto default_largest_unit = larger_of_two_temporal_units("second"sv, *smallest_unit);
|
||||||
|
|
||||||
|
// 7. Let largestUnit be ? ToLargestTemporalUnit(options, « "year", "month", "week", "day" », "auto", defaultLargestUnit).
|
||||||
|
auto largest_unit = TRY(to_largest_temporal_unit(global_object, *options, { "year"sv, "month"sv, "week"sv, "day"sv }, "auto"sv, default_largest_unit));
|
||||||
|
|
||||||
|
// 8. Perform ? ValidateTemporalUnitRange(largestUnit, smallestUnit).
|
||||||
|
TRY(validate_temporal_unit_range(global_object, *largest_unit, *smallest_unit));
|
||||||
|
|
||||||
|
// 9. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc").
|
||||||
|
auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *options, "trunc"sv));
|
||||||
|
|
||||||
|
// 10. Let maximum be ! MaximumTemporalDurationRoundingIncrement(smallestUnit).
|
||||||
|
auto maximum = maximum_temporal_duration_rounding_increment(*smallest_unit);
|
||||||
|
|
||||||
|
// 11. Let roundingIncrement be ? ToTemporalRoundingIncrement(options, maximum, false).
|
||||||
|
auto rounding_increment = TRY(to_temporal_rounding_increment(global_object, *options, *maximum, false));
|
||||||
|
|
||||||
|
// 12. Let roundedNs be ! DifferenceInstant(first.[[Nanoseconds]], second.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode).
|
||||||
|
auto* rounded_ns = difference_instant(global_object, first->nanoseconds(), second->nanoseconds(), rounding_increment, *smallest_unit, rounding_mode);
|
||||||
|
|
||||||
|
// 13. Assert: The following steps cannot fail due to overflow in the Number domain because abs(roundedNs) ≤ 1.728 × 10^22.
|
||||||
|
|
||||||
|
// 14. Let result be ! BalanceDuration(0, 0, 0, 0, 0, 0, roundedNs, largestUnit).
|
||||||
|
auto result = MUST(balance_duration(global_object, 0, 0, 0, 0, 0, 0, rounded_ns->big_integer(), *largest_unit));
|
||||||
|
|
||||||
|
// 15. Return ! CreateTemporalDuration(0, 0, 0, 0, result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]]).
|
||||||
|
return MUST(create_temporal_duration(global_object, 0, 0, 0, 0, result.hours, result.minutes, result.seconds, result.milliseconds, result.microseconds, result.nanoseconds));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8.5.11 AddDurationToOrSubtractDurationFromInstant ( operation, instant, temporalDurationLike ), https://tc39.es/proposal-temporal/#sec-temporal-adddurationtoorsubtractdurationfrominstant
|
||||||
ThrowCompletionOr<Instant*> add_duration_to_or_subtract_duration_from_instant(GlobalObject& global_object, ArithmeticOperation operation, Instant const& instant, Value temporal_duration_like)
|
ThrowCompletionOr<Instant*> add_duration_to_or_subtract_duration_from_instant(GlobalObject& global_object, ArithmeticOperation operation, Instant const& instant, Value temporal_duration_like)
|
||||||
{
|
{
|
||||||
// 1. If operation is subtract, let sign be -1. Otherwise, let sign be 1.
|
// 1. If operation is subtract, let sign be -1. Otherwise, let sign be 1.
|
||||||
|
|
|
@ -49,6 +49,7 @@ ThrowCompletionOr<BigInt*> add_instant(GlobalObject&, BigInt const& epoch_nanose
|
||||||
BigInt* difference_instant(GlobalObject&, BigInt const& nanoseconds1, BigInt const& nanoseconds2, u64 rounding_increment, StringView smallest_unit, StringView rounding_mode);
|
BigInt* difference_instant(GlobalObject&, BigInt const& nanoseconds1, BigInt const& nanoseconds2, u64 rounding_increment, StringView smallest_unit, StringView rounding_mode);
|
||||||
BigInt* round_temporal_instant(GlobalObject&, BigInt const& nanoseconds, u64 increment, StringView unit, StringView rounding_mode);
|
BigInt* round_temporal_instant(GlobalObject&, BigInt const& nanoseconds, u64 increment, StringView unit, StringView rounding_mode);
|
||||||
ThrowCompletionOr<String> temporal_instant_to_string(GlobalObject&, Instant&, Value time_zone, Variant<StringView, u8> const& precision);
|
ThrowCompletionOr<String> temporal_instant_to_string(GlobalObject&, Instant&, Value time_zone, Variant<StringView, u8> const& precision);
|
||||||
|
ThrowCompletionOr<Duration*> difference_temporal_instant(GlobalObject&, DifferenceOperation, Instant const&, Value other, Value options);
|
||||||
ThrowCompletionOr<Instant*> add_duration_to_or_subtract_duration_from_instant(GlobalObject&, ArithmeticOperation, Instant const&, Value temporal_duration_like);
|
ThrowCompletionOr<Instant*> add_duration_to_or_subtract_duration_from_instant(GlobalObject&, ArithmeticOperation, Instant const&, Value temporal_duration_like);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -147,89 +147,29 @@ JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::subtract)
|
||||||
// 8.3.9 Temporal.Instant.prototype.until ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.until
|
// 8.3.9 Temporal.Instant.prototype.until ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.until
|
||||||
JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::until)
|
JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::until)
|
||||||
{
|
{
|
||||||
|
auto other = vm.argument(0);
|
||||||
|
auto options = vm.argument(1);
|
||||||
|
|
||||||
// 1. Let instant be the this value.
|
// 1. Let instant be the this value.
|
||||||
// 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).
|
// 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).
|
||||||
auto* instant = TRY(typed_this_object(global_object));
|
auto* instant = TRY(typed_this_object(global_object));
|
||||||
|
|
||||||
// 3. Set other to ? ToTemporalInstant(other).
|
// 3. Return ? DifferenceTemporalInstant(until, instant, other, options).
|
||||||
auto* other = TRY(to_temporal_instant(global_object, vm.argument(0)));
|
return TRY(difference_temporal_instant(global_object, DifferenceOperation::Until, *instant, other, options));
|
||||||
|
|
||||||
// 4. Set options to ? GetOptionsObject(options).
|
|
||||||
auto const* options = TRY(get_options_object(global_object, vm.argument(1)));
|
|
||||||
|
|
||||||
// 5. Let smallestUnit be ? ToSmallestTemporalUnit(options, « "year", "month", "week", "day" », "nanosecond").
|
|
||||||
auto smallest_unit = TRY(to_smallest_temporal_unit(global_object, *options, { "year"sv, "month"sv, "week"sv, "day"sv }, "nanosecond"sv));
|
|
||||||
|
|
||||||
// 6. Let defaultLargestUnit be ! LargerOfTwoTemporalUnits("second", smallestUnit).
|
|
||||||
auto default_largest_unit = larger_of_two_temporal_units("second"sv, *smallest_unit);
|
|
||||||
|
|
||||||
// 7. Let largestUnit be ? ToLargestTemporalUnit(options, « "year", "month", "week", "day" », "auto", defaultLargestUnit).
|
|
||||||
auto largest_unit = TRY(to_largest_temporal_unit(global_object, *options, { "year"sv, "month"sv, "week"sv, "day"sv }, "auto"sv, default_largest_unit));
|
|
||||||
|
|
||||||
// 8. Perform ? ValidateTemporalUnitRange(largestUnit, smallestUnit).
|
|
||||||
TRY(validate_temporal_unit_range(global_object, *largest_unit, *smallest_unit));
|
|
||||||
|
|
||||||
// 9. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc").
|
|
||||||
auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *options, "trunc"sv));
|
|
||||||
|
|
||||||
// 10. Let maximum be ! MaximumTemporalDurationRoundingIncrement(smallestUnit).
|
|
||||||
auto maximum = maximum_temporal_duration_rounding_increment(*smallest_unit);
|
|
||||||
|
|
||||||
// 11. Let roundingIncrement be ? ToTemporalRoundingIncrement(options, maximum, false).
|
|
||||||
auto rounding_increment = TRY(to_temporal_rounding_increment(global_object, *options, *maximum, false));
|
|
||||||
|
|
||||||
// 12. Let roundedNs be ! DifferenceInstant(instant.[[Nanoseconds]], other.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode).
|
|
||||||
auto* rounded_ns = difference_instant(global_object, instant->nanoseconds(), other->nanoseconds(), rounding_increment, *smallest_unit, rounding_mode);
|
|
||||||
|
|
||||||
// 13. Let result be ! BalanceDuration(0, 0, 0, 0, 0, 0, roundedNs, largestUnit).
|
|
||||||
auto result = MUST(balance_duration(global_object, 0, 0, 0, 0, 0, 0, rounded_ns->big_integer(), *largest_unit));
|
|
||||||
|
|
||||||
// 14. Return ? CreateTemporalDuration(0, 0, 0, 0, result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]]).
|
|
||||||
return TRY(create_temporal_duration(global_object, 0, 0, 0, 0, result.hours, result.minutes, result.seconds, result.milliseconds, result.microseconds, result.nanoseconds));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 8.3.10 Temporal.Instant.prototype.since ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.since
|
// 8.3.10 Temporal.Instant.prototype.since ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.since
|
||||||
JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::since)
|
JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::since)
|
||||||
{
|
{
|
||||||
|
auto other = vm.argument(0);
|
||||||
|
auto options = vm.argument(1);
|
||||||
|
|
||||||
// 1. Let instant be the this value.
|
// 1. Let instant be the this value.
|
||||||
// 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).
|
// 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).
|
||||||
auto* instant = TRY(typed_this_object(global_object));
|
auto* instant = TRY(typed_this_object(global_object));
|
||||||
|
|
||||||
// 3. Set other to ? ToTemporalInstant(other).
|
// 3. Return ? DifferenceTemporalInstant(since, instant, other, options).
|
||||||
auto* other = TRY(to_temporal_instant(global_object, vm.argument(0)));
|
return TRY(difference_temporal_instant(global_object, DifferenceOperation::Since, *instant, other, options));
|
||||||
|
|
||||||
// 4. Set options to ? GetOptionsObject(options).
|
|
||||||
auto const* options = TRY(get_options_object(global_object, vm.argument(1)));
|
|
||||||
|
|
||||||
// 5. Let smallestUnit be ? ToSmallestTemporalUnit(options, « "year", "month", "week", "day" », "nanosecond").
|
|
||||||
auto smallest_unit = TRY(to_smallest_temporal_unit(global_object, *options, { "year"sv, "month"sv, "week"sv, "day"sv }, "nanosecond"sv));
|
|
||||||
|
|
||||||
// 6. Let defaultLargestUnit be ! LargerOfTwoTemporalUnits("second", smallestUnit).
|
|
||||||
auto default_largest_unit = larger_of_two_temporal_units("second"sv, *smallest_unit);
|
|
||||||
|
|
||||||
// 7. Let largestUnit be ? ToLargestTemporalUnit(options, « "year", "month", "week", "day" », "auto", defaultLargestUnit).
|
|
||||||
auto largest_unit = TRY(to_largest_temporal_unit(global_object, *options, { "year"sv, "month"sv, "week"sv, "day"sv }, "auto"sv, move(default_largest_unit)));
|
|
||||||
|
|
||||||
// 8. Perform ? ValidateTemporalUnitRange(largestUnit, smallestUnit).
|
|
||||||
TRY(validate_temporal_unit_range(global_object, *largest_unit, *smallest_unit));
|
|
||||||
|
|
||||||
// 9. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc").
|
|
||||||
auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *options, "trunc"sv));
|
|
||||||
|
|
||||||
// 10. Let maximum be ! MaximumTemporalDurationRoundingIncrement(smallestUnit).
|
|
||||||
auto maximum = maximum_temporal_duration_rounding_increment(*smallest_unit);
|
|
||||||
|
|
||||||
// 11. Let roundingIncrement be ? ToTemporalRoundingIncrement(options, maximum, false).
|
|
||||||
auto rounding_increment = TRY(to_temporal_rounding_increment(global_object, *options, *maximum, false));
|
|
||||||
|
|
||||||
// 12. Let roundedNs be ! DifferenceInstant(other.[[Nanoseconds]], instant.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode).
|
|
||||||
auto* rounded_ns = difference_instant(global_object, other->nanoseconds(), instant->nanoseconds(), rounding_increment, *smallest_unit, rounding_mode);
|
|
||||||
|
|
||||||
// 13. Let result be ! BalanceDuration(0, 0, 0, 0, 0, 0, roundedNs, largestUnit).
|
|
||||||
auto result = MUST(balance_duration(global_object, 0, 0, 0, 0, 0, 0, rounded_ns->big_integer(), *largest_unit));
|
|
||||||
|
|
||||||
// 14. Return ? CreateTemporalDuration(0, 0, 0, 0, result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]]).
|
|
||||||
return TRY(create_temporal_duration(global_object, 0, 0, 0, 0, result.hours, result.minutes, result.seconds, result.milliseconds, result.microseconds, result.nanoseconds));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 8.3.11 Temporal.Instant.prototype.round ( roundTo ), https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.round
|
// 8.3.11 Temporal.Instant.prototype.round ( roundTo ), https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.round
|
||||||
|
|
|
@ -486,4 +486,67 @@ i8 compare_iso_date(i32 year1, u8 month1, u8 day1, i32 year2, u8 month2, u8 day2
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 3.5.11 DifferenceTemporalPlainDate ( operation, temporalDate, other, options ), https://tc39.es/proposal-temporal/#sec-temporal-differencetemporalplaindate
|
||||||
|
ThrowCompletionOr<Duration*> difference_temporal_plain_date(GlobalObject& global_object, DifferenceOperation operation, PlainDate& temporal_date, Value other_value, Value options_value)
|
||||||
|
{
|
||||||
|
auto& vm = global_object.vm();
|
||||||
|
|
||||||
|
// 1. If operation is since, let sign be -1. Otherwise, let sign be 1.
|
||||||
|
i8 sign = operation == DifferenceOperation::Since ? -1 : 1;
|
||||||
|
|
||||||
|
// 2. Set other to ? ToTemporalDate(other).
|
||||||
|
auto* other = TRY(to_temporal_date(global_object, other_value));
|
||||||
|
|
||||||
|
// 3. If ? CalendarEquals(temporalDate.[[Calendar]], other.[[Calendar]]) is false, throw a RangeError exception.
|
||||||
|
if (!TRY(calendar_equals(global_object, temporal_date.calendar(), other->calendar())))
|
||||||
|
return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalDifferentCalendars);
|
||||||
|
|
||||||
|
// 4. Set options to ? GetOptionsObject(options).
|
||||||
|
auto const* options = TRY(get_options_object(global_object, options_value));
|
||||||
|
|
||||||
|
// 5. Let disallowedUnits be « "hour", "minute", "second", "millisecond", "microsecond", "nanosecond" ».
|
||||||
|
auto disallowed_units = Vector<StringView> { "hour"sv, "minute"sv, "second"sv, "millisecond"sv, "microsecond"sv, "nanosecond"sv };
|
||||||
|
|
||||||
|
// 6. Let smallestUnit be ? ToSmallestTemporalUnit(options, disallowedUnits, "day").
|
||||||
|
auto smallest_unit = TRY(to_smallest_temporal_unit(global_object, *options, disallowed_units, "day"sv));
|
||||||
|
|
||||||
|
// 7. Let defaultLargestUnit be ! LargerOfTwoTemporalUnits("day", smallestUnit).
|
||||||
|
auto default_largest_unit = larger_of_two_temporal_units("day"sv, *smallest_unit);
|
||||||
|
|
||||||
|
// 8. Let largestUnit be ? ToLargestTemporalUnit(options, disallowedUnits, "auto", defaultLargestUnit).
|
||||||
|
auto largest_unit = TRY(to_largest_temporal_unit(global_object, *options, disallowed_units, "auto"sv, default_largest_unit));
|
||||||
|
|
||||||
|
// 9. Perform ? ValidateTemporalUnitRange(largestUnit, smallestUnit).
|
||||||
|
TRY(validate_temporal_unit_range(global_object, *largest_unit, *smallest_unit));
|
||||||
|
|
||||||
|
// 10. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc").
|
||||||
|
auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *options, "trunc"sv));
|
||||||
|
|
||||||
|
// 11. If operation is since, then
|
||||||
|
if (operation == DifferenceOperation::Since) {
|
||||||
|
// a. Set roundingMode to ! NegateTemporalRoundingMode(roundingMode).
|
||||||
|
rounding_mode = negate_temporal_rounding_mode(rounding_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 12. Let roundingIncrement be ? ToTemporalRoundingIncrement(options, undefined, false).
|
||||||
|
auto rounding_increment = TRY(to_temporal_rounding_increment(global_object, *options, {}, false));
|
||||||
|
|
||||||
|
// 13. Let untilOptions be ? MergeLargestUnitOption(options, largestUnit).
|
||||||
|
auto* until_options = TRY(merge_largest_unit_option(global_object, options, largest_unit.release_value()));
|
||||||
|
|
||||||
|
// 14. Let result be ? CalendarDateUntil(temporalDate.[[Calendar]], temporalDate, other, untilOptions).
|
||||||
|
auto* duration = TRY(calendar_date_until(global_object, temporal_date.calendar(), &temporal_date, other, *until_options));
|
||||||
|
|
||||||
|
auto result = DurationRecord { duration->years(), duration->months(), duration->weeks(), duration->days(), 0, 0, 0, 0, 0, 0 };
|
||||||
|
|
||||||
|
// 15. If smallestUnit is not "day" or roundingIncrement ≠ 1, then
|
||||||
|
if (*smallest_unit != "day"sv || rounding_increment != 1) {
|
||||||
|
// a. Set result to (? RoundDuration(result.[[Years]], result.[[Months]], result.[[Weeks]], result.[[Days]], 0, 0, 0, 0, 0, 0, roundingIncrement, smallestUnit, roundingMode, temporalDate)).[[DurationRecord]].
|
||||||
|
result = TRY(round_duration(global_object, result.years, result.months, result.weeks, result.days, 0, 0, 0, 0, 0, 0, rounding_increment, *smallest_unit, rounding_mode, &temporal_date)).duration_record;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 16. Return ! CreateTemporalDuration(sign × result.[[Years]], sign × result.[[Months]], sign × result.[[Weeks]], sign × result.[[Days]], 0, 0, 0, 0, 0, 0).
|
||||||
|
return TRY(create_temporal_duration(global_object, sign * result.years, sign * result.months, sign * result.weeks, sign * result.days, 0, 0, 0, 0, 0, 0));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include <LibJS/Forward.h>
|
#include <LibJS/Forward.h>
|
||||||
#include <LibJS/Runtime/Completion.h>
|
#include <LibJS/Runtime/Completion.h>
|
||||||
#include <LibJS/Runtime/Object.h>
|
#include <LibJS/Runtime/Object.h>
|
||||||
|
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
|
||||||
|
|
||||||
namespace JS::Temporal {
|
namespace JS::Temporal {
|
||||||
|
|
||||||
|
@ -52,5 +53,6 @@ String pad_iso_year(i32 y);
|
||||||
ThrowCompletionOr<String> temporal_date_to_string(GlobalObject&, PlainDate&, StringView show_calendar);
|
ThrowCompletionOr<String> temporal_date_to_string(GlobalObject&, PlainDate&, StringView show_calendar);
|
||||||
ThrowCompletionOr<ISODate> add_iso_date(GlobalObject&, i32 year, u8 month, u8 day, double years, double months, double weeks, double days, StringView overflow);
|
ThrowCompletionOr<ISODate> add_iso_date(GlobalObject&, i32 year, u8 month, u8 day, double years, double months, double weeks, double days, StringView overflow);
|
||||||
i8 compare_iso_date(i32 year1, u8 month1, u8 day1, i32 year2, u8 month2, u8 day2);
|
i8 compare_iso_date(i32 year1, u8 month1, u8 day1, i32 year2, u8 month2, u8 day2);
|
||||||
|
ThrowCompletionOr<Duration*> difference_temporal_plain_date(GlobalObject&, DifferenceOperation, PlainDate&, Value other, Value options);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -441,116 +441,29 @@ JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::with_calendar)
|
||||||
// 3.3.23 Temporal.PlainDate.prototype.until ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.until
|
// 3.3.23 Temporal.PlainDate.prototype.until ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.until
|
||||||
JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::until)
|
JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::until)
|
||||||
{
|
{
|
||||||
|
auto other = vm.argument(0);
|
||||||
|
auto options = vm.argument(1);
|
||||||
|
|
||||||
// 1. Let temporalDate be the this value.
|
// 1. Let temporalDate be the this value.
|
||||||
// 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).
|
// 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).
|
||||||
auto* temporal_date = TRY(typed_this_object(global_object));
|
auto* temporal_date = TRY(typed_this_object(global_object));
|
||||||
|
|
||||||
// 3. Set other to ? ToTemporalDate(other).
|
// 3. Return ? DifferenceTemporalPlainDate(until, temporalDate, other, options).
|
||||||
auto* other = TRY(to_temporal_date(global_object, vm.argument(0)));
|
return TRY(difference_temporal_plain_date(global_object, DifferenceOperation::Until, *temporal_date, other, options));
|
||||||
|
|
||||||
// 4. If ? CalendarEquals(temporalDate.[[Calendar]], other.[[Calendar]]) is false, throw a RangeError exception.
|
|
||||||
if (!TRY(calendar_equals(global_object, temporal_date->calendar(), other->calendar())))
|
|
||||||
return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalDifferentCalendars);
|
|
||||||
|
|
||||||
// 5. Set options to ? GetOptionsObject(options).
|
|
||||||
auto const* options = TRY(get_options_object(global_object, vm.argument(1)));
|
|
||||||
|
|
||||||
// 6. Let disallowedUnits be « "hour", "minute", "second", "millisecond", "microsecond", "nanosecond" ».
|
|
||||||
Vector<StringView> disallowed_units { "hour"sv, "minute"sv, "second"sv, "millisecond"sv, "microsecond"sv, "nanosecond"sv };
|
|
||||||
|
|
||||||
// 7. Let smallestUnit be ? ToSmallestTemporalUnit(options, disallowedUnits, "day").
|
|
||||||
auto smallest_unit = TRY(to_smallest_temporal_unit(global_object, *options, disallowed_units, "day"sv));
|
|
||||||
|
|
||||||
// 8. Let defaultLargestUnit be ! LargerOfTwoTemporalUnits("day", smallestUnit).
|
|
||||||
auto default_largest_unit = larger_of_two_temporal_units("day"sv, *smallest_unit);
|
|
||||||
|
|
||||||
// 9. Let largestUnit be ? ToLargestTemporalUnit(options, disallowedUnits, "auto", defaultLargestUnit).
|
|
||||||
auto largest_unit = TRY(to_largest_temporal_unit(global_object, *options, disallowed_units, "auto"sv, default_largest_unit));
|
|
||||||
|
|
||||||
// 10. Perform ? ValidateTemporalUnitRange(largestUnit, smallestUnit).
|
|
||||||
TRY(validate_temporal_unit_range(global_object, *largest_unit, *smallest_unit));
|
|
||||||
|
|
||||||
// 11. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc").
|
|
||||||
auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *options, "trunc"sv));
|
|
||||||
|
|
||||||
// 12. Let roundingIncrement be ? ToTemporalRoundingIncrement(options, undefined, false).
|
|
||||||
auto rounding_increment = TRY(to_temporal_rounding_increment(global_object, *options, {}, false));
|
|
||||||
|
|
||||||
// 13. Let untilOptions be ? MergeLargestUnitOption(options, largestUnit).
|
|
||||||
auto* until_options = TRY(merge_largest_unit_option(global_object, options, move(*largest_unit)));
|
|
||||||
|
|
||||||
// 14. Let result be ? CalendarDateUntil(temporalDate.[[Calendar]], temporalDate, other, untilOptions).
|
|
||||||
auto* duration = TRY(calendar_date_until(global_object, temporal_date->calendar(), temporal_date, other, *until_options));
|
|
||||||
|
|
||||||
DurationRecord result { duration->years(), duration->months(), duration->weeks(), duration->days(), 0, 0, 0, 0, 0, 0 };
|
|
||||||
|
|
||||||
// 15. If smallestUnit is not "day" or roundingIncrement ≠ 1, then
|
|
||||||
if (*smallest_unit != "day"sv || rounding_increment != 1) {
|
|
||||||
// a. Set result to (? RoundDuration(result.[[Years]], result.[[Months]], result.[[Weeks]], result.[[Days]], 0, 0, 0, 0, 0, 0, roundingIncrement, smallestUnit, roundingMode, temporalDate)).[[DurationRecord]].
|
|
||||||
result = TRY(round_duration(global_object, result.years, result.months, result.weeks, result.days, 0, 0, 0, 0, 0, 0, rounding_increment, *smallest_unit, rounding_mode, temporal_date)).duration_record;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 16. Return ! CreateTemporalDuration(result.[[Years]], result.[[Months]], result.[[Weeks]], result.[[Days]], 0, 0, 0, 0, 0, 0).
|
|
||||||
return MUST(create_temporal_duration(global_object, result.years, result.months, result.weeks, result.days, 0, 0, 0, 0, 0, 0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3.3.24 Temporal.PlainDate.prototype.since ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.since
|
// 3.3.24 Temporal.PlainDate.prototype.since ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.since
|
||||||
JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::since)
|
JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::since)
|
||||||
{
|
{
|
||||||
|
auto other = vm.argument(0);
|
||||||
|
auto options = vm.argument(1);
|
||||||
|
|
||||||
// 1. Let temporalDate be the this value.
|
// 1. Let temporalDate be the this value.
|
||||||
// 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).
|
// 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).
|
||||||
auto* temporal_date = TRY(typed_this_object(global_object));
|
auto* temporal_date = TRY(typed_this_object(global_object));
|
||||||
|
|
||||||
// 3. Set other to ? ToTemporalDate(other).
|
// 3. Return ? DifferenceTemporalPlainDate(since, temporalDate, other, options).
|
||||||
auto* other = TRY(to_temporal_date(global_object, vm.argument(0)));
|
return TRY(difference_temporal_plain_date(global_object, DifferenceOperation::Since, *temporal_date, other, options));
|
||||||
|
|
||||||
// 4. If ? CalendarEquals(temporalDate.[[Calendar]], other.[[Calendar]]) is false, throw a RangeError exception.
|
|
||||||
if (!TRY(calendar_equals(global_object, temporal_date->calendar(), other->calendar())))
|
|
||||||
return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalDifferentCalendars);
|
|
||||||
|
|
||||||
// 5. Set options to ? GetOptionsObject(options).
|
|
||||||
auto* options = TRY(get_options_object(global_object, vm.argument(1)));
|
|
||||||
|
|
||||||
// 6. Let disallowedUnits be « "hour", "minute", "second", "millisecond", "microsecond", "nanosecond" ».
|
|
||||||
Vector<StringView> disallowed_units { "hour"sv, "minute"sv, "second"sv, "millisecond"sv, "microsecond"sv, "nanosecond"sv };
|
|
||||||
|
|
||||||
// 7. Let smallestUnit be ? ToSmallestTemporalUnit(options, disallowedUnits, "day").
|
|
||||||
auto smallest_unit = TRY(to_smallest_temporal_unit(global_object, *options, disallowed_units, "day"sv));
|
|
||||||
|
|
||||||
// 8. Let defaultLargestUnit be ! LargerOfTwoTemporalUnits("day", smallestUnit).
|
|
||||||
auto default_largest_unit = larger_of_two_temporal_units("day"sv, *smallest_unit);
|
|
||||||
|
|
||||||
// 9. Let largestUnit be ? ToLargestTemporalUnit(options, disallowedUnits, "auto", defaultLargestUnit).
|
|
||||||
auto largest_unit = TRY(to_largest_temporal_unit(global_object, *options, disallowed_units, "auto"sv, default_largest_unit));
|
|
||||||
|
|
||||||
// 10. Perform ? ValidateTemporalUnitRange(largestUnit, smallestUnit).
|
|
||||||
TRY(validate_temporal_unit_range(global_object, *largest_unit, *smallest_unit));
|
|
||||||
|
|
||||||
// 11. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc").
|
|
||||||
auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *options, "trunc"sv));
|
|
||||||
|
|
||||||
// 12. Set roundingMode to ! NegateTemporalRoundingMode(roundingMode).
|
|
||||||
rounding_mode = negate_temporal_rounding_mode(rounding_mode);
|
|
||||||
|
|
||||||
// 13. Let roundingIncrement be ? ToTemporalRoundingIncrement(options, undefined, false).
|
|
||||||
auto rounding_increment = TRY(to_temporal_rounding_increment(global_object, *options, {}, false));
|
|
||||||
|
|
||||||
// 14. Let untilOptions be ? MergeLargestUnitOption(options, largestUnit).
|
|
||||||
auto* until_options = TRY(merge_largest_unit_option(global_object, options, move(*largest_unit)));
|
|
||||||
|
|
||||||
// 15. Let result be ? CalendarDateUntil(temporalDate.[[Calendar]], temporalDate, other, untilOptions).
|
|
||||||
auto* duration = TRY(calendar_date_until(global_object, temporal_date->calendar(), temporal_date, other, *until_options));
|
|
||||||
|
|
||||||
DurationRecord result { duration->years(), duration->months(), duration->weeks(), duration->days(), 0, 0, 0, 0, 0, 0 };
|
|
||||||
|
|
||||||
// 16. If smallestUnit is not "day" or roundingIncrement ≠ 1, then
|
|
||||||
if (*smallest_unit != "day"sv || rounding_increment != 1) {
|
|
||||||
// a. Set result to (? RoundDuration(result.[[Years]], result.[[Months]], result.[[Weeks]], result.[[Days]], 0, 0, 0, 0, 0, 0, roundingIncrement, smallestUnit, roundingMode, temporalDate)).[[DurationRecord]].
|
|
||||||
result = TRY(round_duration(global_object, result.years, result.months, result.weeks, result.days, 0, 0, 0, 0, 0, 0, rounding_increment, *smallest_unit, rounding_mode, temporal_date)).duration_record;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 17. Return ! CreateTemporalDuration(-result.[[Years]], -result.[[Months]], -result.[[Weeks]], -result.[[Days]], 0, 0, 0, 0, 0, 0).
|
|
||||||
return TRY(create_temporal_duration(global_object, -result.years, -result.months, -result.weeks, -result.days, 0, 0, 0, 0, 0, 0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3.3.25 Temporal.PlainDate.prototype.equals ( other ), https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.equals
|
// 3.3.25 Temporal.PlainDate.prototype.equals ( other ), https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.equals
|
||||||
|
|
|
@ -391,7 +391,68 @@ ThrowCompletionOr<DurationRecord> difference_iso_date_time(GlobalObject& global_
|
||||||
return create_duration_record(date_difference->years(), date_difference->months(), date_difference->weeks(), balance_result.days, balance_result.hours, balance_result.minutes, balance_result.seconds, balance_result.milliseconds, balance_result.microseconds, balance_result.nanoseconds);
|
return create_duration_record(date_difference->years(), date_difference->months(), date_difference->weeks(), balance_result.days, balance_result.hours, balance_result.minutes, balance_result.seconds, balance_result.milliseconds, balance_result.microseconds, balance_result.nanoseconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5.5.12 AddDurationToOrSubtractDurationFromPlainDateTime ( operation, dateTime, temporalDurationLike, options ), https://tc39.es/proposal-temporal/#sec-temporal-adddurationtoorsubtractdurationfromplaindatetime
|
// 5.5.12 DifferenceTemporalPlainDateTime ( operation, dateTime, other, options ), https://tc39.es/proposal-temporal/#sec-temporal-differencetemporalplaindatetime
|
||||||
|
ThrowCompletionOr<Duration*> difference_temporal_plain_date_time(GlobalObject& global_object, DifferenceOperation operation, PlainDateTime& date_time, Value other_value, Value options_value)
|
||||||
|
{
|
||||||
|
auto& vm = global_object.vm();
|
||||||
|
|
||||||
|
// 1. If operation is since, let sign be -1. Otherwise, let sign be 1.
|
||||||
|
i8 sign = operation == DifferenceOperation::Since ? -1 : 1;
|
||||||
|
|
||||||
|
// 2. Set other to ? ToTemporalDateTime(other).
|
||||||
|
auto* other = TRY(to_temporal_date_time(global_object, other_value));
|
||||||
|
|
||||||
|
// 3. If ? CalendarEquals(dateTime.[[Calendar]], other.[[Calendar]]) is false, throw a RangeError exception.
|
||||||
|
if (!TRY(calendar_equals(global_object, date_time.calendar(), other->calendar())))
|
||||||
|
return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalDifferentCalendars);
|
||||||
|
|
||||||
|
// 4. Set options to ? GetOptionsObject(options).
|
||||||
|
auto const* options = TRY(get_options_object(global_object, options_value));
|
||||||
|
|
||||||
|
// 5. Let smallestUnit be ? ToSmallestTemporalUnit(options, « », "nanosecond").
|
||||||
|
auto smallest_unit = TRY(to_smallest_temporal_unit(global_object, *options, {}, "nanosecond"sv));
|
||||||
|
|
||||||
|
// 6. Let defaultLargestUnit be ! LargerOfTwoTemporalUnits("day", smallestUnit).
|
||||||
|
auto default_largest_unit = larger_of_two_temporal_units("day"sv, *smallest_unit);
|
||||||
|
|
||||||
|
// 7. Let largestUnit be ? ToLargestTemporalUnit(options, « », "auto", defaultLargestUnit).
|
||||||
|
auto largest_unit = TRY(to_largest_temporal_unit(global_object, *options, {}, "auto"sv, default_largest_unit));
|
||||||
|
|
||||||
|
// 8. Perform ? ValidateTemporalUnitRange(largestUnit, smallestUnit).
|
||||||
|
TRY(validate_temporal_unit_range(global_object, *largest_unit, *smallest_unit));
|
||||||
|
|
||||||
|
// 9. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc").
|
||||||
|
auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *options, "trunc"sv));
|
||||||
|
|
||||||
|
// 10. If operation is since, then
|
||||||
|
if (operation == DifferenceOperation::Since) {
|
||||||
|
// a. Set roundingMode to ! NegateTemporalRoundingMode(roundingMode).
|
||||||
|
rounding_mode = negate_temporal_rounding_mode(rounding_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 11. Let maximum be ! MaximumTemporalDurationRoundingIncrement(smallestUnit).
|
||||||
|
auto maximum = maximum_temporal_duration_rounding_increment(*smallest_unit);
|
||||||
|
|
||||||
|
// 12. Let roundingIncrement be ? ToTemporalRoundingIncrement(options, maximum, false).
|
||||||
|
auto rounding_increment = TRY(to_temporal_rounding_increment(global_object, *options, Optional<double>(maximum), false));
|
||||||
|
|
||||||
|
// 13. Let diff be ? DifferenceISODateTime(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[ISOHour]], dateTime.[[ISOMinute]], dateTime.[[ISOSecond]], dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]], dateTime.[[ISONanosecond]], other.[[ISOYear]], other.[[ISOMonth]], other.[[ISODay]], other.[[ISOHour]], other.[[ISOMinute]], other.[[ISOSecond]], other.[[ISOMillisecond]], other.[[ISOMicrosecond]], other.[[ISONanosecond]], dateTime.[[Calendar]], largestUnit, options).
|
||||||
|
auto diff = TRY(difference_iso_date_time(global_object, date_time.iso_year(), date_time.iso_month(), date_time.iso_day(), date_time.iso_hour(), date_time.iso_minute(), date_time.iso_second(), date_time.iso_millisecond(), date_time.iso_microsecond(), date_time.iso_nanosecond(), other->iso_year(), other->iso_month(), other->iso_day(), other->iso_hour(), other->iso_minute(), other->iso_second(), other->iso_millisecond(), other->iso_microsecond(), other->iso_nanosecond(), date_time.calendar(), *largest_unit, options));
|
||||||
|
|
||||||
|
// 14. Let relativeTo be ! CreateTemporalDate(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[Calendar]]).
|
||||||
|
auto* relative_to = MUST(create_temporal_date(global_object, date_time.iso_year(), date_time.iso_month(), date_time.iso_day(), date_time.calendar()));
|
||||||
|
|
||||||
|
// 15. Let roundResult be (? RoundDuration(diff.[[Years]], diff.[[Months]], diff.[[Weeks]], diff.[[Days]], diff.[[Hours]], diff.[[Minutes]], diff.[[Seconds]], diff.[[Milliseconds]], diff.[[Microseconds]], diff.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode, relativeTo)).[[DurationRecord]].
|
||||||
|
auto round_result = TRY(round_duration(global_object, diff.years, diff.months, diff.weeks, diff.days, diff.hours, diff.minutes, diff.seconds, diff.milliseconds, diff.microseconds, diff.nanoseconds, rounding_increment, *smallest_unit, rounding_mode, relative_to)).duration_record;
|
||||||
|
|
||||||
|
// 16. Let result be ? BalanceDuration(roundResult.[[Days]], roundResult.[[Hours]], roundResult.[[Minutes]], roundResult.[[Seconds]], roundResult.[[Milliseconds]], roundResult.[[Microseconds]], roundResult.[[Nanoseconds]], largestUnit).
|
||||||
|
auto result = MUST(balance_duration(global_object, round_result.days, round_result.hours, round_result.minutes, round_result.seconds, round_result.milliseconds, round_result.microseconds, Crypto::SignedBigInteger::create_from((i64)round_result.nanoseconds), *largest_unit));
|
||||||
|
|
||||||
|
// 17. Return ! CreateTemporalDuration(sign × roundResult.[[Years]], sign × roundResult.[[Months]], sign × roundResult.[[Weeks]], sign × result.[[Days]], sign × result.[[Hours]], sign × result.[[Minutes]], sign × result.[[Seconds]], sign × result.[[Milliseconds]], sign × result.[[Microseconds]], sign × result.[[Nanoseconds]]).
|
||||||
|
return MUST(create_temporal_duration(global_object, sign * round_result.years, sign * round_result.months, sign * round_result.weeks, sign * result.days, sign * result.hours, sign * result.minutes, sign * result.seconds, sign * result.milliseconds, sign * result.microseconds, sign * result.nanoseconds));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5.5.13 AddDurationToOrSubtractDurationFromPlainDateTime ( operation, dateTime, temporalDurationLike, options ), https://tc39.es/proposal-temporal/#sec-temporal-adddurationtoorsubtractdurationfromplaindatetime
|
||||||
ThrowCompletionOr<PlainDateTime*> add_duration_to_or_subtract_duration_from_plain_date_time(GlobalObject& global_object, ArithmeticOperation operation, PlainDateTime& date_time, Value temporal_duration_like, Value options_value)
|
ThrowCompletionOr<PlainDateTime*> add_duration_to_or_subtract_duration_from_plain_date_time(GlobalObject& global_object, ArithmeticOperation operation, PlainDateTime& date_time, Value temporal_duration_like, Value options_value)
|
||||||
{
|
{
|
||||||
// 1. If operation is subtract, let sign be -1. Otherwise, let sign be 1.
|
// 1. If operation is subtract, let sign be -1. Otherwise, let sign be 1.
|
||||||
|
|
|
@ -74,6 +74,7 @@ i8 compare_iso_date_time(i32 year1, u8 month1, u8 day1, u8 hour1, u8 minute1, u8
|
||||||
ThrowCompletionOr<TemporalPlainDateTime> add_date_time(GlobalObject&, i32 year, u8 month, u8 day, u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, Object& calendar, double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds, Object* options);
|
ThrowCompletionOr<TemporalPlainDateTime> add_date_time(GlobalObject&, i32 year, u8 month, u8 day, u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, Object& calendar, double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds, Object* options);
|
||||||
ISODateTime round_iso_date_time(i32 year, u8 month, u8 day, u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, u64 increment, StringView unit, StringView rounding_mode, Optional<double> day_length = {});
|
ISODateTime round_iso_date_time(i32 year, u8 month, u8 day, u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, u64 increment, StringView unit, StringView rounding_mode, Optional<double> day_length = {});
|
||||||
ThrowCompletionOr<DurationRecord> difference_iso_date_time(GlobalObject&, i32 year1, u8 month1, u8 day1, u8 hour1, u8 minute1, u8 second1, u16 millisecond1, u16 microsecond1, u16 nanosecond1, i32 year2, u8 month2, u8 day2, u8 hour2, u8 minute2, u8 second2, u16 millisecond2, u16 microsecond2, u16 nanosecond2, Object& calendar, StringView largest_unit, Object const* options = nullptr);
|
ThrowCompletionOr<DurationRecord> difference_iso_date_time(GlobalObject&, i32 year1, u8 month1, u8 day1, u8 hour1, u8 minute1, u8 second1, u16 millisecond1, u16 microsecond1, u16 nanosecond1, i32 year2, u8 month2, u8 day2, u8 hour2, u8 minute2, u8 second2, u16 millisecond2, u16 microsecond2, u16 nanosecond2, Object& calendar, StringView largest_unit, Object const* options = nullptr);
|
||||||
|
ThrowCompletionOr<Duration*> difference_temporal_plain_date_time(GlobalObject&, DifferenceOperation, PlainDateTime&, Value other, Value options);
|
||||||
ThrowCompletionOr<PlainDateTime*> add_duration_to_or_subtract_duration_from_plain_date_time(GlobalObject&, ArithmeticOperation, PlainDateTime&, Value temporal_duration_like, Value options_value);
|
ThrowCompletionOr<PlainDateTime*> add_duration_to_or_subtract_duration_from_plain_date_time(GlobalObject&, ArithmeticOperation, PlainDateTime&, Value temporal_duration_like, Value options_value);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -489,112 +489,29 @@ JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::subtract)
|
||||||
// 5.3.28 Temporal.PlainDateTime.prototype.until ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.since
|
// 5.3.28 Temporal.PlainDateTime.prototype.until ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.since
|
||||||
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::until)
|
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::until)
|
||||||
{
|
{
|
||||||
|
auto other = vm.argument(0);
|
||||||
|
auto options = vm.argument(1);
|
||||||
|
|
||||||
// 1. Let dateTime be the this value.
|
// 1. Let dateTime be the this value.
|
||||||
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
|
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
|
||||||
auto* date_time = TRY(typed_this_object(global_object));
|
auto* date_time = TRY(typed_this_object(global_object));
|
||||||
|
|
||||||
// 3. Set other to ? ToTemporalDateTime(other).
|
// 3. Return ? DifferenceTemporalPlainDateTime(until, dateTime, other, options).
|
||||||
auto* other = TRY(to_temporal_date_time(global_object, vm.argument(0)));
|
return TRY(difference_temporal_plain_date_time(global_object, DifferenceOperation::Until, *date_time, other, options));
|
||||||
|
|
||||||
// 4. If ? CalendarEquals(dateTime.[[Calendar]], other.[[Calendar]]) is false, throw a RangeError exception.
|
|
||||||
if (!TRY(calendar_equals(global_object, date_time->calendar(), other->calendar())))
|
|
||||||
return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalDifferentCalendars);
|
|
||||||
|
|
||||||
// 5. Set options to ? GetOptionsObject(options).
|
|
||||||
auto const* options = TRY(get_options_object(global_object, vm.argument(1)));
|
|
||||||
|
|
||||||
// 6. Let smallestUnit be ? ToSmallestTemporalUnit(options, « », "nanosecond").
|
|
||||||
auto smallest_unit = TRY(to_smallest_temporal_unit(global_object, *options, {}, "nanosecond"sv));
|
|
||||||
|
|
||||||
// 7. Let defaultLargestUnit be ! LargerOfTwoTemporalUnits("day", smallestUnit).
|
|
||||||
auto default_largest_unit = larger_of_two_temporal_units("day"sv, *smallest_unit);
|
|
||||||
|
|
||||||
// 8. Let largestUnit be ? ToLargestTemporalUnit(options, « », "auto", defaultLargestUnit).
|
|
||||||
auto largest_unit = TRY(to_largest_temporal_unit(global_object, *options, {}, "auto"sv, default_largest_unit));
|
|
||||||
|
|
||||||
// 9. Perform ? ValidateTemporalUnitRange(largestUnit, smallestUnit).
|
|
||||||
TRY(validate_temporal_unit_range(global_object, *largest_unit, *smallest_unit));
|
|
||||||
|
|
||||||
// 10. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc").
|
|
||||||
auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *options, "trunc"sv));
|
|
||||||
|
|
||||||
// 11. Let maximum be ! MaximumTemporalDurationRoundingIncrement(smallestUnit).
|
|
||||||
auto maximum = maximum_temporal_duration_rounding_increment(*smallest_unit);
|
|
||||||
|
|
||||||
// 12. Let roundingIncrement be ? ToTemporalRoundingIncrement(options, maximum, false).
|
|
||||||
auto rounding_increment = TRY(to_temporal_rounding_increment(global_object, *options, Optional<double>(maximum), false));
|
|
||||||
|
|
||||||
// 13. Let diff be ? DifferenceISODateTime(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[ISOHour]], dateTime.[[ISOMinute]], dateTime.[[ISOSecond]], dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]], dateTime.[[ISONanosecond]], other.[[ISOYear]], other.[[ISOMonth]], other.[[ISODay]], other.[[ISOHour]], other.[[ISOMinute]], other.[[ISOSecond]], other.[[ISOMillisecond]], other.[[ISOMicrosecond]], other.[[ISONanosecond]], dateTime.[[Calendar]], largestUnit, options).
|
|
||||||
auto diff = TRY(difference_iso_date_time(global_object, date_time->iso_year(), date_time->iso_month(), date_time->iso_day(), date_time->iso_hour(), date_time->iso_minute(), date_time->iso_second(), date_time->iso_millisecond(), date_time->iso_microsecond(), date_time->iso_nanosecond(), other->iso_year(), other->iso_month(), other->iso_day(), other->iso_hour(), other->iso_minute(), other->iso_second(), other->iso_millisecond(), other->iso_microsecond(), other->iso_nanosecond(), date_time->calendar(), *largest_unit, options));
|
|
||||||
|
|
||||||
// 14. Let relativeTo be ! CreateTemporalDate(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[Calendar]]).
|
|
||||||
auto* relative_to = MUST(create_temporal_date(global_object, date_time->iso_year(), date_time->iso_month(), date_time->iso_day(), date_time->calendar()));
|
|
||||||
|
|
||||||
// 15. Let roundResult be (? RoundDuration(diff.[[Years]], diff.[[Months]], diff.[[Weeks]], diff.[[Days]], diff.[[Hours]], diff.[[Minutes]], diff.[[Seconds]], diff.[[Milliseconds]], diff.[[Microseconds]], diff.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode, relativeTo)).[[DurationRecord]].
|
|
||||||
auto round_result = TRY(round_duration(global_object, diff.years, diff.months, diff.weeks, diff.days, diff.hours, diff.minutes, diff.seconds, diff.milliseconds, diff.microseconds, diff.nanoseconds, rounding_increment, *smallest_unit, rounding_mode, relative_to)).duration_record;
|
|
||||||
|
|
||||||
// 16. Let result be ! BalanceDuration(roundResult.[[Days]], roundResult.[[Hours]], roundResult.[[Minutes]], roundResult.[[Seconds]], roundResult.[[Milliseconds]], roundResult.[[Microseconds]], roundResult.[[Nanoseconds]], largestUnit).
|
|
||||||
auto result = MUST(balance_duration(global_object, round_result.days, round_result.hours, round_result.minutes, round_result.seconds, round_result.milliseconds, round_result.microseconds, Crypto::SignedBigInteger::create_from((i64)round_result.nanoseconds), *largest_unit));
|
|
||||||
|
|
||||||
// 17. Return ? CreateTemporalDuration(roundResult.[[Years]], roundResult.[[Months]], roundResult.[[Weeks]], result.[[Days]], result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]]).
|
|
||||||
return TRY(create_temporal_duration(global_object, round_result.years, round_result.months, round_result.weeks, result.days, result.hours, result.minutes, result.seconds, result.milliseconds, result.microseconds, result.nanoseconds));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5.3.29 Temporal.PlainDateTime.prototype.since ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.since
|
// 5.3.29 Temporal.PlainDateTime.prototype.since ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.since
|
||||||
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::since)
|
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::since)
|
||||||
{
|
{
|
||||||
|
auto other = vm.argument(0);
|
||||||
|
auto options = vm.argument(1);
|
||||||
|
|
||||||
// 1. Let dateTime be the this value.
|
// 1. Let dateTime be the this value.
|
||||||
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
|
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
|
||||||
auto* date_time = TRY(typed_this_object(global_object));
|
auto* date_time = TRY(typed_this_object(global_object));
|
||||||
|
|
||||||
// 3. Set other to ? ToTemporalDateTime(other).
|
// 3. Return ? DifferenceTemporalPlainDateTime(since, dateTime, other, options).
|
||||||
auto* other = TRY(to_temporal_date_time(global_object, vm.argument(0)));
|
return TRY(difference_temporal_plain_date_time(global_object, DifferenceOperation::Since, *date_time, other, options));
|
||||||
|
|
||||||
// 4. If ? CalendarEquals(dateTime.[[Calendar]], other.[[Calendar]]) is false, throw a RangeError exception.
|
|
||||||
if (!TRY(calendar_equals(global_object, date_time->calendar(), other->calendar())))
|
|
||||||
return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalDifferentCalendars);
|
|
||||||
|
|
||||||
// 5. Set options to ? GetOptionsObject(options).
|
|
||||||
auto const* options = TRY(get_options_object(global_object, vm.argument(1)));
|
|
||||||
|
|
||||||
// 6. Let smallestUnit be ? ToSmallestTemporalUnit(options, « », "nanosecond").
|
|
||||||
auto smallest_unit = TRY(to_smallest_temporal_unit(global_object, *options, {}, "nanosecond"sv));
|
|
||||||
|
|
||||||
// 7. Let defaultLargestUnit be ! LargerOfTwoTemporalUnits("day", smallestUnit).
|
|
||||||
auto default_largest_unit = larger_of_two_temporal_units("day"sv, *smallest_unit);
|
|
||||||
|
|
||||||
// 8. Let largestUnit be ? ToLargestTemporalUnit(options, « », "auto", defaultLargestUnit).
|
|
||||||
auto largest_unit = TRY(to_largest_temporal_unit(global_object, *options, {}, "auto"sv, default_largest_unit));
|
|
||||||
|
|
||||||
// 9. Perform ? ValidateTemporalUnitRange(largestUnit, smallestUnit).
|
|
||||||
TRY(validate_temporal_unit_range(global_object, *largest_unit, *smallest_unit));
|
|
||||||
|
|
||||||
// 10. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc").
|
|
||||||
auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *options, "trunc"sv));
|
|
||||||
|
|
||||||
// 11. Set roundingMode to ! NegateTemporalRoundingMode(roundingMode).
|
|
||||||
rounding_mode = negate_temporal_rounding_mode(rounding_mode);
|
|
||||||
|
|
||||||
// 12. Let maximum be ! MaximumTemporalDurationRoundingIncrement(smallestUnit).
|
|
||||||
auto maximum = maximum_temporal_duration_rounding_increment(*smallest_unit);
|
|
||||||
|
|
||||||
// 13. Let roundingIncrement be ? ToTemporalRoundingIncrement(options, maximum, false).
|
|
||||||
auto rounding_increment = TRY(to_temporal_rounding_increment(global_object, *options, Optional<double>(maximum), false));
|
|
||||||
|
|
||||||
// 14. Let diff be ? DifferenceISODateTime(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[ISOHour]], dateTime.[[ISOMinute]], dateTime.[[ISOSecond]], dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]], dateTime.[[ISONanosecond]], other.[[ISOYear]], other.[[ISOMonth]], other.[[ISODay]], other.[[ISOHour]], other.[[ISOMinute]], other.[[ISOSecond]], other.[[ISOMillisecond]], other.[[ISOMicrosecond]], other.[[ISONanosecond]], dateTime.[[Calendar]], largestUnit, options).
|
|
||||||
auto diff = TRY(difference_iso_date_time(global_object, date_time->iso_year(), date_time->iso_month(), date_time->iso_day(), date_time->iso_hour(), date_time->iso_minute(), date_time->iso_second(), date_time->iso_millisecond(), date_time->iso_microsecond(), date_time->iso_nanosecond(), other->iso_year(), other->iso_month(), other->iso_day(), other->iso_hour(), other->iso_minute(), other->iso_second(), other->iso_millisecond(), other->iso_microsecond(), other->iso_nanosecond(), date_time->calendar(), *largest_unit, options));
|
|
||||||
|
|
||||||
// 15. Let relativeTo be ! CreateTemporalDate(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[Calendar]]).
|
|
||||||
auto* relative_to = MUST(create_temporal_date(global_object, date_time->iso_year(), date_time->iso_month(), date_time->iso_day(), date_time->calendar()));
|
|
||||||
|
|
||||||
// 16. Let roundResult be (? RoundDuration(diff.[[Years]], diff.[[Months]], diff.[[Weeks]], diff.[[Days]], diff.[[Hours]], diff.[[Minutes]], diff.[[Seconds]], diff.[[Milliseconds]], diff.[[Microseconds]], diff.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode, relativeTo)).[[DurationRecord]].
|
|
||||||
auto round_result = TRY(round_duration(global_object, diff.years, diff.months, diff.weeks, diff.days, diff.hours, diff.minutes, diff.seconds, diff.milliseconds, diff.microseconds, diff.nanoseconds, rounding_increment, *smallest_unit, rounding_mode, relative_to)).duration_record;
|
|
||||||
|
|
||||||
// 17. Let result be ! BalanceDuration(roundResult.[[Days]], roundResult.[[Hours]], roundResult.[[Minutes]], roundResult.[[Seconds]], roundResult.[[Milliseconds]], roundResult.[[Microseconds]], roundResult.[[Nanoseconds]], largestUnit).
|
|
||||||
auto result = MUST(balance_duration(global_object, round_result.days, round_result.hours, round_result.minutes, round_result.seconds, round_result.milliseconds, round_result.microseconds, Crypto::SignedBigInteger::create_from((i64)round_result.nanoseconds), *largest_unit));
|
|
||||||
|
|
||||||
// 18. Return ? CreateTemporalDuration(-roundResult.[[Years]], -roundResult.[[Months]], -roundResult.[[Weeks]], -result.[[Days]], -result.[[Hours]], -result.[[Minutes]], -result.[[Seconds]], -result.[[Milliseconds]], -result.[[Microseconds]], -result.[[Nanoseconds]]).
|
|
||||||
return TRY(create_temporal_duration(global_object, -round_result.years, -round_result.months, -round_result.weeks, -result.days, -result.hours, -result.minutes, -result.seconds, -result.milliseconds, -result.microseconds, -result.nanoseconds));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5.3.30 Temporal.PlainDateTime.prototype.round ( roundTo ), https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.round
|
// 5.3.30 Temporal.PlainDateTime.prototype.round ( roundTo ), https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.round
|
||||||
|
|
|
@ -619,7 +619,68 @@ DaysAndTime round_time(u8 hour, u8 minute, u8 second, u16 millisecond, u16 micro
|
||||||
return balance_time(hour, minute, second, millisecond, microsecond, result);
|
return balance_time(hour, minute, second, millisecond, microsecond, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4.5.14 AddDurationToOrSubtractDurationFromPlainTime ( operation, temporalTime, temporalDurationLike ), https://tc39.es/proposal-temporal/#sec-temporal-adddurationtoorsubtractdurationfromplaintime
|
// 4.5.14 DifferenceTemporalPlainTime ( operation, temporalTime, other, options ), https://tc39.es/proposal-temporal/#sec-temporal-differencetemporalplaintime
|
||||||
|
ThrowCompletionOr<Duration*> difference_temporal_plain_time(GlobalObject& global_object, DifferenceOperation operation, PlainTime const& temporal_time, Value other_value, Value options_value)
|
||||||
|
{
|
||||||
|
// 1. If operation is since, let sign be -1. Otherwise, let sign be 1.
|
||||||
|
i8 sign = operation == DifferenceOperation::Since ? -1 : 1;
|
||||||
|
|
||||||
|
PlainTime const* first;
|
||||||
|
PlainTime const* second;
|
||||||
|
|
||||||
|
// 2. Set other to ? ToTemporalTime(other).
|
||||||
|
auto* other = TRY(to_temporal_time(global_object, other_value));
|
||||||
|
|
||||||
|
// FIXME: Copied from DifferenceTemporalInstant, also needed here (spec issue, see https://github.com/tc39/proposal-temporal/pull/2184)
|
||||||
|
if (operation == DifferenceOperation::Until) {
|
||||||
|
first = &temporal_time;
|
||||||
|
second = other;
|
||||||
|
} else {
|
||||||
|
first = other;
|
||||||
|
second = &temporal_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Set options to ? GetOptionsObject(options).
|
||||||
|
auto const* options = TRY(get_options_object(global_object, options_value));
|
||||||
|
|
||||||
|
// 4. Let smallestUnit be ? ToSmallestTemporalUnit(options, « "year", "month", "week", "day" », "nanosecond").
|
||||||
|
auto smallest_unit = TRY(to_smallest_temporal_unit(global_object, *options, { "year"sv, "month"sv, "week"sv, "day"sv }, "nanosecond"sv));
|
||||||
|
|
||||||
|
// 5. Let largestUnit be ? ToLargestTemporalUnit(options, « "year", "month", "week", "day" », "auto", "hour").
|
||||||
|
auto largest_unit = TRY(to_largest_temporal_unit(global_object, *options, { "year"sv, "month"sv, "week"sv, "day"sv }, "auto"sv, "hour"sv));
|
||||||
|
|
||||||
|
// 6. Perform ? ValidateTemporalUnitRange(largestUnit, smallestUnit).
|
||||||
|
TRY(validate_temporal_unit_range(global_object, *largest_unit, *smallest_unit));
|
||||||
|
|
||||||
|
// 7. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc").
|
||||||
|
auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *options, "trunc"sv));
|
||||||
|
|
||||||
|
// 8. If operation is since, then
|
||||||
|
if (operation == DifferenceOperation::Since) {
|
||||||
|
// a. Set roundingMode to ! NegateTemporalRoundingMode(roundingMode).
|
||||||
|
rounding_mode = negate_temporal_rounding_mode(rounding_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 9. Let maximum be ! MaximumTemporalDurationRoundingIncrement(smallestUnit).
|
||||||
|
auto maximum = maximum_temporal_duration_rounding_increment(*smallest_unit);
|
||||||
|
|
||||||
|
// 10. Let roundingIncrement be ? ToTemporalRoundingIncrement(options, maximum, false).
|
||||||
|
auto rounding_increment = TRY(to_temporal_rounding_increment(global_object, *options, Optional<double>(maximum), false));
|
||||||
|
|
||||||
|
// 11. Let result be ! DifferenceTime(other.[[ISOHour]], other.[[ISOMinute]], other.[[ISOSecond]], other.[[ISOMillisecond]], other.[[ISOMicrosecond]], other.[[ISONanosecond]], temporalTime.[[ISOHour]], temporalTime.[[ISOMinute]], temporalTime.[[ISOSecond]], temporalTime.[[ISOMillisecond]], temporalTime.[[ISOMicrosecond]], temporalTime.[[ISONanosecond]]).
|
||||||
|
auto result = difference_time(first->iso_hour(), first->iso_minute(), first->iso_second(), first->iso_millisecond(), first->iso_microsecond(), first->iso_nanosecond(), second->iso_hour(), second->iso_minute(), second->iso_second(), second->iso_millisecond(), second->iso_microsecond(), second->iso_nanosecond());
|
||||||
|
|
||||||
|
// 12. Set result to (? RoundDuration(0, 0, 0, 0, sign × result.[[Hours]], sign × result.[[Minutes]], sign × result.[[Seconds]], sign × result.[[Milliseconds]], sign × result.[[Microseconds]], sign × result.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode)).[[DurationRecord]].
|
||||||
|
auto rounded_result = TRY(round_duration(global_object, 0, 0, 0, 0, sign * result.hours, sign * result.minutes, sign * result.seconds, sign * result.milliseconds, sign * result.microseconds, sign * result.nanoseconds, rounding_increment, *smallest_unit, rounding_mode)).duration_record;
|
||||||
|
|
||||||
|
// 13. Set result to ? BalanceDuration(0, sign × result.[[Hours]], sign × result.[[Minutes]], sign × result.[[Seconds]], sign × result.[[Milliseconds]], sign × result.[[Microseconds]], sign × result.[[Nanoseconds]], largestUnit).
|
||||||
|
result = MUST(balance_duration(global_object, 0, sign * rounded_result.hours, sign * rounded_result.minutes, sign * rounded_result.seconds, sign * rounded_result.milliseconds, sign * rounded_result.microseconds, Crypto::SignedBigInteger { (i32)(sign * rounded_result.nanoseconds) }, *largest_unit));
|
||||||
|
|
||||||
|
// 14. Return ! CreateTemporalDuration(0, 0, 0, 0, result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]]).
|
||||||
|
return MUST(create_temporal_duration(global_object, 0, 0, 0, 0, result.hours, result.minutes, result.seconds, result.milliseconds, result.microseconds, result.nanoseconds));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4.5.15 AddDurationToOrSubtractDurationFromPlainTime ( operation, temporalTime, temporalDurationLike ), https://tc39.es/proposal-temporal/#sec-temporal-adddurationtoorsubtractdurationfromplaintime
|
||||||
ThrowCompletionOr<PlainTime*> add_duration_to_or_subtract_duration_from_plain_time(GlobalObject& global_object, ArithmeticOperation operation, PlainTime const& temporal_time, Value temporal_duration_like)
|
ThrowCompletionOr<PlainTime*> add_duration_to_or_subtract_duration_from_plain_time(GlobalObject& global_object, ArithmeticOperation operation, PlainTime const& temporal_time, Value temporal_duration_like)
|
||||||
{
|
{
|
||||||
// 1. If operation is subtract, let sign be -1. Otherwise, let sign be 1.
|
// 1. If operation is subtract, let sign be -1. Otherwise, let sign be 1.
|
||||||
|
|
|
@ -106,6 +106,7 @@ String temporal_time_to_string(u8 hour, u8 minute, u8 second, u16 millisecond, u
|
||||||
i8 compare_temporal_time(u8 hour1, u8 minute1, u8 second1, u16 millisecond1, u16 microsecond1, u16 nanosecond1, u8 hour2, u8 minute2, u8 second2, u16 millisecond2, u16 microsecond2, u16 nanosecond2);
|
i8 compare_temporal_time(u8 hour1, u8 minute1, u8 second1, u16 millisecond1, u16 microsecond1, u16 nanosecond1, u8 hour2, u8 minute2, u8 second2, u16 millisecond2, u16 microsecond2, u16 nanosecond2);
|
||||||
DaysAndTime add_time(u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds);
|
DaysAndTime add_time(u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds);
|
||||||
DaysAndTime round_time(u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, u64 increment, StringView unit, StringView rounding_mode, Optional<double> day_length_ns = {});
|
DaysAndTime round_time(u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, u64 increment, StringView unit, StringView rounding_mode, Optional<double> day_length_ns = {});
|
||||||
|
ThrowCompletionOr<Duration*> difference_temporal_plain_time(GlobalObject&, DifferenceOperation, PlainTime const&, Value other, Value options);
|
||||||
ThrowCompletionOr<PlainTime*> add_duration_to_or_subtract_duration_from_plain_time(GlobalObject&, ArithmeticOperation, PlainTime const&, Value temporal_duration_like);
|
ThrowCompletionOr<PlainTime*> add_duration_to_or_subtract_duration_from_plain_time(GlobalObject&, ArithmeticOperation, PlainTime const&, Value temporal_duration_like);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -237,92 +237,31 @@ JS_DEFINE_NATIVE_FUNCTION(PlainTimePrototype::with)
|
||||||
// 4.3.13 Temporal.PlainTime.prototype.until ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.until
|
// 4.3.13 Temporal.PlainTime.prototype.until ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.until
|
||||||
JS_DEFINE_NATIVE_FUNCTION(PlainTimePrototype::until)
|
JS_DEFINE_NATIVE_FUNCTION(PlainTimePrototype::until)
|
||||||
{
|
{
|
||||||
|
auto other = vm.argument(0);
|
||||||
|
auto options = vm.argument(1);
|
||||||
|
|
||||||
// 1. Let temporalTime be the this value.
|
// 1. Let temporalTime be the this value.
|
||||||
// 2. Perform ? RequireInternalSlot(temporalTime, [[InitializedTemporalTime]]).
|
// 2. Perform ? RequireInternalSlot(temporalTime, [[InitializedTemporalTime]]).
|
||||||
auto* temporal_time = TRY(typed_this_object(global_object));
|
auto* temporal_time = TRY(typed_this_object(global_object));
|
||||||
|
|
||||||
// 3. Set other to ? ToTemporalTime(other).
|
// 3. Return ? DifferenceTemporalPlainTime(since, temporalTime, other, options).
|
||||||
auto* other = TRY(to_temporal_time(global_object, vm.argument(0)));
|
// FIXME: until, not since (spec issue, see https://github.com/tc39/proposal-temporal/pull/2183)
|
||||||
|
return TRY(difference_temporal_plain_time(global_object, DifferenceOperation::Until, *temporal_time, other, options));
|
||||||
// 4. Set options to ? GetOptionsObject(options).
|
|
||||||
auto* options = TRY(get_options_object(global_object, vm.argument(1)));
|
|
||||||
|
|
||||||
// 5. Let smallestUnit be ? ToSmallestTemporalUnit(options, « "year", "month", "week", "day" », "nanosecond").
|
|
||||||
auto smallest_unit = TRY(to_smallest_temporal_unit(global_object, *options, { "year"sv, "month"sv, "week"sv, "day"sv }, "nanosecond"sv));
|
|
||||||
|
|
||||||
// 6. Let largestUnit be ? ToLargestTemporalUnit(options, « "year", "month", "week", "day" », "auto", "hour").
|
|
||||||
auto largest_unit = TRY(to_largest_temporal_unit(global_object, *options, { "year"sv, "month"sv, "week"sv, "day"sv }, "auto"sv, "hour"sv));
|
|
||||||
|
|
||||||
// 7. Perform ? ValidateTemporalUnitRange(largestUnit, smallestUnit).
|
|
||||||
TRY(validate_temporal_unit_range(global_object, *largest_unit, *smallest_unit));
|
|
||||||
|
|
||||||
// 8. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc").
|
|
||||||
auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *options, "trunc"sv));
|
|
||||||
|
|
||||||
// 9. Let maximum be ! MaximumTemporalDurationRoundingIncrement(smallestUnit).
|
|
||||||
auto maximum = maximum_temporal_duration_rounding_increment(*smallest_unit);
|
|
||||||
|
|
||||||
// 10. Let roundingIncrement be ? ToTemporalRoundingIncrement(options, maximum, false).
|
|
||||||
auto rounding_increment = TRY(to_temporal_rounding_increment(global_object, *options, Optional<double>(maximum), false));
|
|
||||||
|
|
||||||
// 11. Let result be ! DifferenceTime(temporalTime.[[ISOHour]], temporalTime.[[ISOMinute]], temporalTime.[[ISOSecond]], temporalTime.[[ISOMillisecond]], temporalTime.[[ISOMicrosecond]], temporalTime.[[ISONanosecond]], other.[[ISOHour]], other.[[ISOMinute]], other.[[ISOSecond]], other.[[ISOMillisecond]], other.[[ISOMicrosecond]], other.[[ISONanosecond]]).
|
|
||||||
auto result = difference_time(temporal_time->iso_hour(), temporal_time->iso_minute(), temporal_time->iso_second(), temporal_time->iso_millisecond(), temporal_time->iso_microsecond(), temporal_time->iso_nanosecond(), other->iso_hour(), other->iso_minute(), other->iso_second(), other->iso_millisecond(), other->iso_microsecond(), other->iso_nanosecond());
|
|
||||||
|
|
||||||
// 12. Set result to (? RoundDuration(0, 0, 0, 0, result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode)).[[DurationRecord]].
|
|
||||||
auto rounded_result = TRY(round_duration(global_object, 0, 0, 0, 0, result.hours, result.minutes, result.seconds, result.milliseconds, result.microseconds, result.nanoseconds, rounding_increment, *smallest_unit, rounding_mode)).duration_record;
|
|
||||||
|
|
||||||
// 13. Set result to ! BalanceDuration(0, result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]], largestUnit).
|
|
||||||
result = MUST(balance_duration(global_object, 0, rounded_result.hours, rounded_result.minutes, rounded_result.seconds, rounded_result.milliseconds, rounded_result.microseconds, Crypto::SignedBigInteger { (i32)rounded_result.nanoseconds }, *largest_unit));
|
|
||||||
|
|
||||||
// 14. Return ? CreateTemporalDuration(0, 0, 0, 0, result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]]).
|
|
||||||
return TRY(create_temporal_duration(global_object, 0, 0, 0, 0, result.hours, result.minutes, result.seconds, result.milliseconds, result.microseconds, result.nanoseconds));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4.3.14 Temporal.PlainTime.prototype.since ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.since
|
// 4.3.14 Temporal.PlainTime.prototype.since ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.since
|
||||||
JS_DEFINE_NATIVE_FUNCTION(PlainTimePrototype::since)
|
JS_DEFINE_NATIVE_FUNCTION(PlainTimePrototype::since)
|
||||||
{
|
{
|
||||||
|
auto other = vm.argument(0);
|
||||||
|
auto options = vm.argument(1);
|
||||||
|
|
||||||
// 1. Let temporalTime be the this value.
|
// 1. Let temporalTime be the this value.
|
||||||
// 2. Perform ? RequireInternalSlot(temporalTime, [[InitializedTemporalTime]]).
|
// 2. Perform ? RequireInternalSlot(temporalTime, [[InitializedTemporalTime]]).
|
||||||
auto* temporal_time = TRY(typed_this_object(global_object));
|
auto* temporal_time = TRY(typed_this_object(global_object));
|
||||||
|
|
||||||
// 3. Set other to ? ToTemporalTime(other).
|
// 3. Return ? DifferenceTemporalPlainTime(until, temporalTime, other, options).
|
||||||
auto* other = TRY(to_temporal_time(global_object, vm.argument(0)));
|
// FIXME: since, not until (spec issue, see https://github.com/tc39/proposal-temporal/pull/2183)
|
||||||
|
return TRY(difference_temporal_plain_time(global_object, DifferenceOperation::Since, *temporal_time, other, options));
|
||||||
// 4. Set options to ? GetOptionsObject(options).
|
|
||||||
auto* options = TRY(get_options_object(global_object, vm.argument(1)));
|
|
||||||
|
|
||||||
// 5. Let smallestUnit be ? ToSmallestTemporalUnit(options, « "year", "month", "week", "day" », "nanosecond").
|
|
||||||
auto smallest_unit = TRY(to_smallest_temporal_unit(global_object, *options, { "year"sv, "month"sv, "week"sv, "day"sv }, "nanosecond"sv));
|
|
||||||
|
|
||||||
// 6. Let largestUnit be ? ToLargestTemporalUnit(options, « "year", "month", "week", "day" », "auto", "hour").
|
|
||||||
auto largest_unit = TRY(to_largest_temporal_unit(global_object, *options, { "year"sv, "month"sv, "week"sv, "day"sv }, "auto"sv, "hour"sv));
|
|
||||||
|
|
||||||
// 7. Perform ? ValidateTemporalUnitRange(largestUnit, smallestUnit).
|
|
||||||
TRY(validate_temporal_unit_range(global_object, *largest_unit, *smallest_unit));
|
|
||||||
|
|
||||||
// 8. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc").
|
|
||||||
auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *options, "trunc"sv));
|
|
||||||
|
|
||||||
// 9. Set roundingMode to ! NegateTemporalRoundingMode(roundingMode).
|
|
||||||
rounding_mode = negate_temporal_rounding_mode(rounding_mode);
|
|
||||||
|
|
||||||
// 10. Let maximum be ! MaximumTemporalDurationRoundingIncrement(smallestUnit).
|
|
||||||
auto maximum = maximum_temporal_duration_rounding_increment(*smallest_unit);
|
|
||||||
|
|
||||||
// 11. Let roundingIncrement be ? ToTemporalRoundingIncrement(options, maximum, false).
|
|
||||||
auto rounding_increment = TRY(to_temporal_rounding_increment(global_object, *options, Optional<double>(maximum), false));
|
|
||||||
|
|
||||||
// 12. Let result be ! DifferenceTime(other.[[ISOHour]], other.[[ISOMinute]], other.[[ISOSecond]], other.[[ISOMillisecond]], other.[[ISOMicrosecond]], other.[[ISONanosecond]], temporalTime.[[ISOHour]], temporalTime.[[ISOMinute]], temporalTime.[[ISOSecond]], temporalTime.[[ISOMillisecond]], temporalTime.[[ISOMicrosecond]], temporalTime.[[ISONanosecond]]).
|
|
||||||
auto result = difference_time(other->iso_hour(), other->iso_minute(), other->iso_second(), other->iso_millisecond(), other->iso_microsecond(), other->iso_nanosecond(), temporal_time->iso_hour(), temporal_time->iso_minute(), temporal_time->iso_second(), temporal_time->iso_millisecond(), temporal_time->iso_microsecond(), temporal_time->iso_nanosecond());
|
|
||||||
|
|
||||||
// 13. Set result to (? RoundDuration(0, 0, 0, 0, -result.[[Hours]], -result.[[Minutes]], -result.[[Seconds]], -result.[[Milliseconds]], -result.[[Microseconds]], -result.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode)).[[DurationRecord]].
|
|
||||||
auto rounded_result = TRY(round_duration(global_object, 0, 0, 0, 0, -result.hours, -result.minutes, -result.seconds, -result.milliseconds, -result.microseconds, -result.nanoseconds, rounding_increment, *smallest_unit, rounding_mode)).duration_record;
|
|
||||||
|
|
||||||
// 14. Set result to ! BalanceDuration(0, -result.[[Hours]], -result.[[Minutes]], -result.[[Seconds]], -result.[[Milliseconds]], -result.[[Microseconds]], -result.[[Nanoseconds]], largestUnit).
|
|
||||||
result = MUST(balance_duration(global_object, 0, -rounded_result.hours, -rounded_result.minutes, -rounded_result.seconds, -rounded_result.milliseconds, -rounded_result.microseconds, Crypto::SignedBigInteger { (i32)-rounded_result.nanoseconds }, *largest_unit));
|
|
||||||
|
|
||||||
// 15. Return ? CreateTemporalDuration(0, 0, 0, 0, result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]]).
|
|
||||||
return TRY(create_temporal_duration(global_object, 0, 0, 0, 0, result.hours, result.minutes, result.seconds, result.milliseconds, result.microseconds, result.nanoseconds));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4.3.15 Temporal.PlainTime.prototype.round ( roundTo ), https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.round
|
// 4.3.15 Temporal.PlainTime.prototype.round ( roundTo ), https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.round
|
||||||
|
|
|
@ -257,7 +257,91 @@ ThrowCompletionOr<String> temporal_year_month_to_string(GlobalObject& global_obj
|
||||||
return String::formatted("{}{}", result, calendar_string);
|
return String::formatted("{}{}", result, calendar_string);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 9.5.9 AddDurationToOrSubtractDurationFromPlainYearMonth ( operation, yearMonth, temporalDurationLike, options ), https://tc39.es/proposal-temporal/#sec-temporal-addtemporalplainyearmonth
|
// 9.5.9 DifferenceTemporalPlainYearMonth ( operation, yearMonth, other, options ), https://tc39.es/proposal-temporal/#sec-temporal-differencetemporalplainyearmonth
|
||||||
|
ThrowCompletionOr<Duration*> difference_temporal_plain_year_month(GlobalObject& global_object, DifferenceOperation operation, PlainYearMonth& year_month, Value other_value, Value options_value)
|
||||||
|
{
|
||||||
|
auto& vm = global_object.vm();
|
||||||
|
|
||||||
|
// 1. If operation is since, let sign be -1. Otherwise, let sign be 1.
|
||||||
|
i8 sign = operation == DifferenceOperation::Since ? -1 : 1;
|
||||||
|
|
||||||
|
// 2. Set other to ? ToTemporalYearMonth(other).
|
||||||
|
auto* other = TRY(to_temporal_year_month(global_object, other_value));
|
||||||
|
|
||||||
|
// 3. Let calendar be yearMonth.[[Calendar]].
|
||||||
|
auto& calendar = year_month.calendar();
|
||||||
|
|
||||||
|
// 4. If ? CalendarEquals(calendar, other.[[Calendar]]) is false, throw a RangeError exception.
|
||||||
|
if (!TRY(calendar_equals(global_object, calendar, other->calendar())))
|
||||||
|
return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalDifferentCalendars);
|
||||||
|
|
||||||
|
// 5. Set options to ? GetOptionsObject(options).
|
||||||
|
auto const* options = TRY(get_options_object(global_object, options_value));
|
||||||
|
|
||||||
|
// 6. Let disallowedUnits be « "week", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond" ».
|
||||||
|
auto disallowed_units = Vector<StringView> { "week"sv, "day"sv, "hour"sv, "minute"sv, "second"sv, "millisecond"sv, "microsecond"sv, "nanosecond"sv };
|
||||||
|
|
||||||
|
// 7. Let smallestUnit be ? ToSmallestTemporalUnit(options, disallowedUnits, "month").
|
||||||
|
auto smallest_unit = TRY(to_smallest_temporal_unit(global_object, *options, disallowed_units, "month"sv));
|
||||||
|
|
||||||
|
// 8. Let largestUnit be ? ToLargestTemporalUnit(options, disallowedUnits, "auto", "year").
|
||||||
|
auto largest_unit = TRY(to_largest_temporal_unit(global_object, *options, disallowed_units, "auto"sv, "year"sv));
|
||||||
|
|
||||||
|
// 9. Perform ? ValidateTemporalUnitRange(largestUnit, smallestUnit).
|
||||||
|
TRY(validate_temporal_unit_range(global_object, *largest_unit, *smallest_unit));
|
||||||
|
|
||||||
|
// 10. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc").
|
||||||
|
auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *options, "trunc"sv));
|
||||||
|
|
||||||
|
// 11. If operation is since, then
|
||||||
|
if (operation == DifferenceOperation::Since) {
|
||||||
|
// a. Set roundingMode to ! NegateTemporalRoundingMode(roundingMode).
|
||||||
|
rounding_mode = negate_temporal_rounding_mode(rounding_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 12. Let roundingIncrement be ? ToTemporalRoundingIncrement(options, undefined, false).
|
||||||
|
auto rounding_increment = TRY(to_temporal_rounding_increment(global_object, *options, {}, false));
|
||||||
|
|
||||||
|
// 13. Let fieldNames be ? CalendarFields(calendar, « "monthCode", "year" »).
|
||||||
|
auto field_names = TRY(calendar_fields(global_object, calendar, { "monthCode"sv, "year"sv }));
|
||||||
|
|
||||||
|
// 14. Let otherFields be ? PrepareTemporalFields(other, fieldNames, «»).
|
||||||
|
auto* other_fields = TRY(prepare_temporal_fields(global_object, *other, field_names, {}));
|
||||||
|
|
||||||
|
// 15. Perform ! CreateDataPropertyOrThrow(otherFields, "day", 1𝔽).
|
||||||
|
MUST(other_fields->create_data_property_or_throw(vm.names.day, Value(1)));
|
||||||
|
|
||||||
|
// 16. Let otherDate be ? CalendarDateFromFields(calendar, otherFields).
|
||||||
|
auto* other_date = TRY(calendar_date_from_fields(global_object, calendar, *other_fields));
|
||||||
|
|
||||||
|
// 17. Let thisFields be ? PrepareTemporalFields(yearMonth, fieldNames, «»).
|
||||||
|
auto* this_fields = TRY(prepare_temporal_fields(global_object, year_month, field_names, {}));
|
||||||
|
|
||||||
|
// 18. Perform ! CreateDataPropertyOrThrow(thisFields, "day", 1𝔽).
|
||||||
|
MUST(this_fields->create_data_property_or_throw(vm.names.day, Value(1)));
|
||||||
|
|
||||||
|
// 19. Let thisDate be ? CalendarDateFromFields(calendar, thisFields).
|
||||||
|
auto* this_date = TRY(calendar_date_from_fields(global_object, calendar, *this_fields));
|
||||||
|
|
||||||
|
// 20. Let untilOptions be ? MergeLargestUnitOption(options, largestUnit).
|
||||||
|
auto* until_options = TRY(merge_largest_unit_option(global_object, options, *largest_unit));
|
||||||
|
|
||||||
|
// 21. Let result be ? CalendarDateUntil(calendar, thisDate, otherDate, untilOptions).
|
||||||
|
auto* duration = TRY(calendar_date_until(global_object, calendar, this_date, other_date, *until_options));
|
||||||
|
|
||||||
|
auto result = DurationRecord { duration->years(), duration->months(), 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||||
|
|
||||||
|
// 22. If smallestUnit is not "month" or roundingIncrement ≠ 1, then
|
||||||
|
if (smallest_unit != "month"sv || rounding_increment != 1) {
|
||||||
|
// a. Set result to (? RoundDuration(result.[[Years]], result.[[Months]], 0, 0, 0, 0, 0, 0, 0, 0, roundingIncrement, smallestUnit, roundingMode, thisDate)).[[DurationRecord]].
|
||||||
|
result = TRY(round_duration(global_object, result.years, result.months, 0, 0, 0, 0, 0, 0, 0, 0, rounding_increment, *smallest_unit, rounding_mode, this_date)).duration_record;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 23. Return ! CreateTemporalDuration(sign × result.[[Years]], sign × result.[[Months]], 0, 0, 0, 0, 0, 0, 0, 0).
|
||||||
|
return MUST(create_temporal_duration(global_object, sign * result.years, sign * result.months, 0, 0, 0, 0, 0, 0, 0, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 9.5.10 AddDurationToOrSubtractDurationFromPlainYearMonth ( operation, yearMonth, temporalDurationLike, options ), https://tc39.es/proposal-temporal/#sec-temporal-addtemporalplainyearmonth
|
||||||
ThrowCompletionOr<PlainYearMonth*> add_duration_to_or_subtract_duration_from_plain_year_month(GlobalObject& global_object, ArithmeticOperation operation, PlainYearMonth& year_month, Value temporal_duration_like, Value options_value)
|
ThrowCompletionOr<PlainYearMonth*> add_duration_to_or_subtract_duration_from_plain_year_month(GlobalObject& global_object, ArithmeticOperation operation, PlainYearMonth& year_month, Value temporal_duration_like, Value options_value)
|
||||||
{
|
{
|
||||||
auto& vm = global_object.vm();
|
auto& vm = global_object.vm();
|
||||||
|
|
|
@ -48,6 +48,7 @@ ISOYearMonth balance_iso_year_month(double year, double month);
|
||||||
ISOYearMonth constrain_iso_year_month(double year, double month);
|
ISOYearMonth constrain_iso_year_month(double year, double month);
|
||||||
ThrowCompletionOr<PlainYearMonth*> create_temporal_year_month(GlobalObject&, i32 iso_year, u8 iso_month, Object& calendar, u8 reference_iso_day, FunctionObject const* new_target = nullptr);
|
ThrowCompletionOr<PlainYearMonth*> create_temporal_year_month(GlobalObject&, i32 iso_year, u8 iso_month, Object& calendar, u8 reference_iso_day, FunctionObject const* new_target = nullptr);
|
||||||
ThrowCompletionOr<String> temporal_year_month_to_string(GlobalObject&, PlainYearMonth&, StringView show_calendar);
|
ThrowCompletionOr<String> temporal_year_month_to_string(GlobalObject&, PlainYearMonth&, StringView show_calendar);
|
||||||
|
ThrowCompletionOr<Duration*> difference_temporal_plain_year_month(GlobalObject&, DifferenceOperation, PlainYearMonth&, Value other, Value options);
|
||||||
ThrowCompletionOr<PlainYearMonth*> add_duration_to_or_subtract_duration_from_plain_year_month(GlobalObject&, ArithmeticOperation, PlainYearMonth&, Value temporal_duration_like, Value options_value);
|
ThrowCompletionOr<PlainYearMonth*> add_duration_to_or_subtract_duration_from_plain_year_month(GlobalObject&, ArithmeticOperation, PlainYearMonth&, Value temporal_duration_like, Value options_value);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -267,160 +267,29 @@ JS_DEFINE_NATIVE_FUNCTION(PlainYearMonthPrototype::subtract)
|
||||||
// 9.3.14 Temporal.PlainYearMonth.prototype.until ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.prototype.until
|
// 9.3.14 Temporal.PlainYearMonth.prototype.until ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.prototype.until
|
||||||
JS_DEFINE_NATIVE_FUNCTION(PlainYearMonthPrototype::until)
|
JS_DEFINE_NATIVE_FUNCTION(PlainYearMonthPrototype::until)
|
||||||
{
|
{
|
||||||
|
auto other = vm.argument(0);
|
||||||
|
auto options = vm.argument(1);
|
||||||
|
|
||||||
// 1. Let yearMonth be the this value.
|
// 1. Let yearMonth be the this value.
|
||||||
// 2. Perform ? RequireInternalSlot(yearMonth, [[InitializedTemporalYearMonth]]).
|
// 2. Perform ? RequireInternalSlot(yearMonth, [[InitializedTemporalYearMonth]]).
|
||||||
auto* year_month = TRY(typed_this_object(global_object));
|
auto* year_month = TRY(typed_this_object(global_object));
|
||||||
|
|
||||||
// 3. Set other to ? ToTemporalYearMonth(other).
|
// 3. Return ? DifferenceTemporalPlainYearMonth(until, yearMonth, other, options).
|
||||||
auto* other = TRY(to_temporal_year_month(global_object, vm.argument(0)));
|
return TRY(difference_temporal_plain_year_month(global_object, DifferenceOperation::Until, *year_month, other, options));
|
||||||
|
|
||||||
// 4. Let calendar be yearMonth.[[Calendar]].
|
|
||||||
auto& calendar = year_month->calendar();
|
|
||||||
|
|
||||||
// 5. If ? CalendarEquals(calendar, other.[[Calendar]]) is false, throw a RangeError exception.
|
|
||||||
if (!TRY(calendar_equals(global_object, calendar, other->calendar())))
|
|
||||||
return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalDifferentCalendars);
|
|
||||||
|
|
||||||
// 6. Set options to ? GetOptionsObject(options).
|
|
||||||
auto* options = TRY(get_options_object(global_object, vm.argument(1)));
|
|
||||||
|
|
||||||
// 7. Let disallowedUnits be « "week", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond" ».
|
|
||||||
auto disallowed_units = Vector<StringView> { "week"sv, "day"sv, "hour"sv, "minute"sv, "second"sv, "millisecond"sv, "microsecond"sv, "nanosecond"sv };
|
|
||||||
|
|
||||||
// 8. Let smallestUnit be ? ToSmallestTemporalUnit(options, disallowedUnits, "month").
|
|
||||||
auto smallest_unit = TRY(to_smallest_temporal_unit(global_object, *options, disallowed_units, "month"sv));
|
|
||||||
|
|
||||||
// 9. Let largestUnit be ? ToLargestTemporalUnit(options, disallowedUnits, "auto", "year").
|
|
||||||
auto largest_unit = TRY(to_largest_temporal_unit(global_object, *options, disallowed_units, "auto"sv, "year"));
|
|
||||||
|
|
||||||
// 10. Perform ? ValidateTemporalUnitRange(largestUnit, smallestUnit).
|
|
||||||
TRY(validate_temporal_unit_range(global_object, *largest_unit, *smallest_unit));
|
|
||||||
|
|
||||||
// 11. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc").
|
|
||||||
auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *options, "trunc"sv));
|
|
||||||
|
|
||||||
// 12. Let roundingIncrement be ? ToTemporalRoundingIncrement(options, undefined, false).
|
|
||||||
auto rounding_increment = TRY(to_temporal_rounding_increment(global_object, *options, {}, false));
|
|
||||||
|
|
||||||
// 13. Let fieldNames be ? CalendarFields(calendar, « "monthCode", "year" »).
|
|
||||||
auto field_names = TRY(calendar_fields(global_object, calendar, { "monthCode"sv, "year"sv }));
|
|
||||||
|
|
||||||
// 14. Let otherFields be ? PrepareTemporalFields(other, fieldNames, «»).
|
|
||||||
auto* other_fields = TRY(prepare_temporal_fields(global_object, *other, field_names, {}));
|
|
||||||
|
|
||||||
// 15. Perform ! CreateDataPropertyOrThrow(otherFields, "day", 1𝔽).
|
|
||||||
MUST(other_fields->create_data_property_or_throw(vm.names.day, Value(1)));
|
|
||||||
|
|
||||||
// 16. Let otherDate be ? CalendarDateFromFields(calendar, otherFields).
|
|
||||||
auto* other_date = TRY(calendar_date_from_fields(global_object, calendar, *other_fields));
|
|
||||||
|
|
||||||
// 17. Let thisFields be ? PrepareTemporalFields(yearMonth, fieldNames, «»).
|
|
||||||
auto* this_fields = TRY(prepare_temporal_fields(global_object, *year_month, field_names, {}));
|
|
||||||
|
|
||||||
// 18. Perform ! CreateDataPropertyOrThrow(thisFields, "day", 1𝔽).
|
|
||||||
MUST(this_fields->create_data_property_or_throw(vm.names.day, Value(1)));
|
|
||||||
|
|
||||||
// 19. Let thisDate be ? CalendarDateFromFields(calendar, thisFields).
|
|
||||||
auto* this_date = TRY(calendar_date_from_fields(global_object, calendar, *this_fields));
|
|
||||||
|
|
||||||
// 20. Let untilOptions be ? MergeLargestUnitOption(options, largestUnit).
|
|
||||||
auto* until_options = TRY(merge_largest_unit_option(global_object, options, *largest_unit));
|
|
||||||
|
|
||||||
// 21. Let result be ? CalendarDateUntil(calendar, thisDate, otherDate, untilOptions).
|
|
||||||
auto* result = TRY(calendar_date_until(global_object, calendar, this_date, other_date, *until_options));
|
|
||||||
|
|
||||||
// 22. If smallestUnit is "month" and roundingIncrement = 1, then
|
|
||||||
if (smallest_unit == "month"sv && rounding_increment == 1) {
|
|
||||||
// a. Return ! CreateTemporalDuration(result.[[Years]], result.[[Months]], 0, 0, 0, 0, 0, 0, 0, 0).
|
|
||||||
return MUST(create_temporal_duration(global_object, result->years(), result->months(), 0, 0, 0, 0, 0, 0, 0, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 23. Let result be (? RoundDuration(result.[[Years]], result.[[Months]], 0, 0, 0, 0, 0, 0, 0, 0, roundingIncrement, smallestUnit, roundingMode, thisDate)).[[DurationRecord]].
|
|
||||||
auto round_result = TRY(round_duration(global_object, result->years(), result->months(), 0, 0, 0, 0, 0, 0, 0, 0, rounding_increment, *smallest_unit, rounding_mode, this_date)).duration_record;
|
|
||||||
|
|
||||||
// 24. Return ! CreateTemporalDuration(result.[[Years]], result.[[Months]], 0, 0, 0, 0, 0, 0, 0, 0).
|
|
||||||
return MUST(create_temporal_duration(global_object, round_result.years, round_result.months, 0, 0, 0, 0, 0, 0, 0, 0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 9.3.15 Temporal.PlainYearMonth.prototype.since ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.prototype.since
|
// 9.3.15 Temporal.PlainYearMonth.prototype.since ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.prototype.since
|
||||||
JS_DEFINE_NATIVE_FUNCTION(PlainYearMonthPrototype::since)
|
JS_DEFINE_NATIVE_FUNCTION(PlainYearMonthPrototype::since)
|
||||||
{
|
{
|
||||||
|
auto other = vm.argument(0);
|
||||||
|
auto options = vm.argument(1);
|
||||||
|
|
||||||
// 1. Let yearMonth be the this value.
|
// 1. Let yearMonth be the this value.
|
||||||
// 2. Perform ? RequireInternalSlot(yearMonth, [[InitializedTemporalYearMonth]]).
|
// 2. Perform ? RequireInternalSlot(yearMonth, [[InitializedTemporalYearMonth]]).
|
||||||
auto* year_month = TRY(typed_this_object(global_object));
|
auto* year_month = TRY(typed_this_object(global_object));
|
||||||
|
|
||||||
// 3. Set other to ? ToTemporalYearMonth(other).
|
// 3. Return ? DifferenceTemporalPlainYearMonth(since, yearMonth, other, options).
|
||||||
auto* other = TRY(to_temporal_year_month(global_object, vm.argument(0)));
|
return TRY(difference_temporal_plain_year_month(global_object, DifferenceOperation::Since, *year_month, other, options));
|
||||||
|
|
||||||
// 4. Let calendar be yearMonth.[[Calendar]].
|
|
||||||
auto& calendar = year_month->calendar();
|
|
||||||
|
|
||||||
// 5. If ? CalendarEquals(calendar, other.[[Calendar]]) is false, throw a RangeError exception.
|
|
||||||
if (!TRY(calendar_equals(global_object, calendar, other->calendar())))
|
|
||||||
return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalDifferentCalendars);
|
|
||||||
|
|
||||||
// 6. Set options to ? GetOptionsObject(options).
|
|
||||||
auto* options = TRY(get_options_object(global_object, vm.argument(1)));
|
|
||||||
|
|
||||||
// 7. Let disallowedUnits be « "week", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond" ».
|
|
||||||
auto disallowed_units = Vector<StringView> { "week"sv, "day"sv, "hour"sv, "minute"sv, "second"sv, "millisecond"sv, "microsecond"sv, "nanosecond"sv };
|
|
||||||
|
|
||||||
// 8. Let smallestUnit be ? ToSmallestTemporalUnit(options, disallowedUnits, "month").
|
|
||||||
auto smallest_unit = TRY(to_smallest_temporal_unit(global_object, *options, disallowed_units, "month"sv));
|
|
||||||
|
|
||||||
// 9. Let largestUnit be ? ToLargestTemporalUnit(options, disallowedUnits, "auto", "year").
|
|
||||||
auto largest_unit = TRY(to_largest_temporal_unit(global_object, *options, disallowed_units, "auto"sv, "year"));
|
|
||||||
|
|
||||||
// 10. Perform ? ValidateTemporalUnitRange(largestUnit, smallestUnit).
|
|
||||||
TRY(validate_temporal_unit_range(global_object, *largest_unit, *smallest_unit));
|
|
||||||
|
|
||||||
// 11. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc").
|
|
||||||
auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *options, "trunc"sv));
|
|
||||||
|
|
||||||
// 12. Set roundingMode to ! NegateTemporalRoundingMode(roundingMode).
|
|
||||||
rounding_mode = negate_temporal_rounding_mode(rounding_mode);
|
|
||||||
|
|
||||||
// 13. Let roundingIncrement be ? ToTemporalRoundingIncrement(options, undefined, false).
|
|
||||||
auto rounding_increment = TRY(to_temporal_rounding_increment(global_object, *options, {}, false));
|
|
||||||
|
|
||||||
// 14. Let fieldNames be ? CalendarFields(calendar, « "monthCode", "year" »).
|
|
||||||
auto field_names = TRY(calendar_fields(global_object, calendar, { "monthCode"sv, "year"sv }));
|
|
||||||
|
|
||||||
// 15. Let otherFields be ? PrepareTemporalFields(other, fieldNames, «»).
|
|
||||||
auto* other_fields = TRY(prepare_temporal_fields(global_object, *other, field_names, {}));
|
|
||||||
|
|
||||||
// 16. Perform ! CreateDataPropertyOrThrow(otherFields, "day", 1𝔽).
|
|
||||||
MUST(other_fields->create_data_property_or_throw(vm.names.day, Value(1)));
|
|
||||||
|
|
||||||
// 17. Let otherDate be ? CalendarDateFromFields(calendar, otherFields).
|
|
||||||
auto* other_date = TRY(calendar_date_from_fields(global_object, calendar, *other_fields));
|
|
||||||
|
|
||||||
// 18. Let thisFields be ? PrepareTemporalFields(yearMonth, fieldNames, «»).
|
|
||||||
auto* this_fields = TRY(prepare_temporal_fields(global_object, *year_month, field_names, {}));
|
|
||||||
|
|
||||||
// 19. Perform ! CreateDataPropertyOrThrow(thisFields, "day", 1𝔽).
|
|
||||||
MUST(this_fields->create_data_property_or_throw(vm.names.day, Value(1)));
|
|
||||||
|
|
||||||
// 20. Let thisDate be ? CalendarDateFromFields(calendar, thisFields).
|
|
||||||
auto* this_date = TRY(calendar_date_from_fields(global_object, calendar, *this_fields));
|
|
||||||
|
|
||||||
// 21. Let untilOptions be ? MergeLargestUnitOption(options, largestUnit).
|
|
||||||
auto* until_options = TRY(merge_largest_unit_option(global_object, options, *largest_unit));
|
|
||||||
|
|
||||||
// 22. Let result be ? CalendarDateUntil(calendar, thisDate, otherDate, untilOptions).
|
|
||||||
auto* result = TRY(calendar_date_until(global_object, calendar, this_date, other_date, *until_options));
|
|
||||||
|
|
||||||
// 23. If smallestUnit is "month" and roundingIncrement = 1, then
|
|
||||||
if (smallest_unit == "month"sv && rounding_increment == 1) {
|
|
||||||
// a. Return ! CreateTemporalDuration(-result.[[Years]], -result.[[Months]], 0, 0, 0, 0, 0, 0, 0, 0).
|
|
||||||
return MUST(create_temporal_duration(global_object, -result->years(), -result->months(), 0, 0, 0, 0, 0, 0, 0, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 24. Let result be (? RoundDuration(result.[[Years]], result.[[Months]], 0, 0, 0, 0, 0, 0, 0, 0, roundingIncrement, smallestUnit, roundingMode, thisDate)).[[DurationRecord]].
|
|
||||||
auto round_result = TRY(round_duration(global_object, result->years(), result->months(), 0, 0, 0, 0, 0, 0, 0, 0, rounding_increment, *smallest_unit, rounding_mode, this_date)).duration_record;
|
|
||||||
|
|
||||||
// 25. Return ! CreateTemporalDuration(-result.[[Years]], -result.[[Months]], 0, 0, 0, 0, 0, 0, 0, 0).
|
|
||||||
return MUST(create_temporal_duration(global_object, -round_result.years, -round_result.months, 0, 0, 0, 0, 0, 0, 0, 0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 9.3.16 Temporal.PlainYearMonth.prototype.equals ( other ), https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.prototype.equals
|
// 9.3.16 Temporal.PlainYearMonth.prototype.equals ( other ), https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.prototype.equals
|
||||||
|
|
|
@ -537,7 +537,90 @@ ThrowCompletionOr<NanosecondsToDaysResult> nanoseconds_to_days(GlobalObject& glo
|
||||||
return NanosecondsToDaysResult { .days = days, .nanoseconds = move(nanoseconds), .day_length = fabs(day_length_ns.to_double()) };
|
return NanosecondsToDaysResult { .days = days, .nanoseconds = move(nanoseconds), .day_length = fabs(day_length_ns.to_double()) };
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6.5.8 AddDurationToOrSubtractDurationFromZonedDateTime ( operation, zonedDateTime, temporalDurationLike, options ), https://tc39.es/proposal-temporal/#sec-temporal-adddurationtoOrsubtractdurationfromzoneddatetime
|
// 6.5.8 DifferenceTemporalZonedDateTime ( operation, zonedDateTime, other, options ), https://tc39.es/proposal-temporal/#sec-temporal-differencetemporalzoneddatetime
|
||||||
|
ThrowCompletionOr<Duration*> difference_temporal_zoned_date_time(GlobalObject& global_object, DifferenceOperation operation, ZonedDateTime& zoned_date_time, Value other_value, Value options_value)
|
||||||
|
{
|
||||||
|
auto& vm = global_object.vm();
|
||||||
|
|
||||||
|
// 1. If operation is since, let sign be -1. Otherwise, let sign be 1.
|
||||||
|
i8 sign = operation == DifferenceOperation::Since ? -1 : 1;
|
||||||
|
|
||||||
|
// 2. Set other to ? ToTemporalZonedDateTime(other).
|
||||||
|
auto* other = TRY(to_temporal_zoned_date_time(global_object, other_value));
|
||||||
|
|
||||||
|
// 3. If ? CalendarEquals(zonedDateTime.[[Calendar]], other.[[Calendar]]) is false, then
|
||||||
|
if (!TRY(calendar_equals(global_object, zoned_date_time.calendar(), other->calendar()))) {
|
||||||
|
// a. Throw a RangeError exception.
|
||||||
|
return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalDifferentCalendars);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Set options to ? GetOptionsObject(options).
|
||||||
|
auto const* options = TRY(get_options_object(global_object, options_value));
|
||||||
|
|
||||||
|
// 5. Let smallestUnit be ? ToSmallestTemporalUnit(options, « », "nanosecond").
|
||||||
|
auto smallest_unit = TRY(to_smallest_temporal_unit(global_object, *options, {}, "nanosecond"sv));
|
||||||
|
|
||||||
|
// 6. Let defaultLargestUnit be ! LargerOfTwoTemporalUnits("hour", smallestUnit).
|
||||||
|
auto default_largest_unit = larger_of_two_temporal_units("hour"sv, *smallest_unit);
|
||||||
|
|
||||||
|
// 7. Let largestUnit be ? ToLargestTemporalUnit(options, « », "auto", defaultLargestUnit).
|
||||||
|
auto largest_unit = TRY(to_largest_temporal_unit(global_object, *options, {}, "auto"sv, default_largest_unit));
|
||||||
|
|
||||||
|
// 8. Perform ? ValidateTemporalUnitRange(largestUnit, smallestUnit).
|
||||||
|
TRY(validate_temporal_unit_range(global_object, *largest_unit, *smallest_unit));
|
||||||
|
|
||||||
|
// 9. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc").
|
||||||
|
auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *options, "trunc"sv));
|
||||||
|
|
||||||
|
// 10. If operation is since, then
|
||||||
|
if (operation == DifferenceOperation::Since) {
|
||||||
|
// a. Set roundingMode to ! NegateTemporalRoundingMode(roundingMode).
|
||||||
|
rounding_mode = negate_temporal_rounding_mode(rounding_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 11. Let maximum be ! MaximumTemporalDurationRoundingIncrement(smallestUnit).
|
||||||
|
auto maximum = maximum_temporal_duration_rounding_increment(*smallest_unit);
|
||||||
|
|
||||||
|
// 12. Let roundingIncrement be ? ToTemporalRoundingIncrement(options, maximum, false).
|
||||||
|
auto rounding_increment = TRY(to_temporal_rounding_increment(global_object, *options, Optional<double>(maximum), false));
|
||||||
|
|
||||||
|
// 13. If largestUnit is not one of "year", "month", "week", or "day", then
|
||||||
|
if (!largest_unit->is_one_of("year"sv, "month"sv, "week"sv, "day"sv)) {
|
||||||
|
// a. Let differenceNs be ! DifferenceInstant(zonedDateTime.[[Nanoseconds]], other.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode).
|
||||||
|
auto* difference_ns = difference_instant(global_object, zoned_date_time.nanoseconds(), other->nanoseconds(), rounding_increment, *smallest_unit, rounding_mode);
|
||||||
|
|
||||||
|
// b. Assert: The following steps cannot fail due to overflow in the Number domain because abs(differenceNs) ≤ 1.728 × 10^22.
|
||||||
|
|
||||||
|
// c. Let balanceResult be ! BalanceDuration(0, 0, 0, 0, 0, 0, differenceNs, largestUnit).
|
||||||
|
auto balance_result = MUST(balance_duration(global_object, 0, 0, 0, 0, 0, 0, difference_ns->big_integer(), *largest_unit));
|
||||||
|
|
||||||
|
// d. Return ! CreateTemporalDuration(0, 0, 0, 0, sign × balanceResult.[[Hours]], sign × balanceResult.[[Minutes]], sign × balanceResult.[[Seconds]], sign × balanceResult.[[Milliseconds]], sign × balanceResult.[[Microseconds]], sign × balanceResult.[[Nanoseconds]]).
|
||||||
|
return MUST(create_temporal_duration(global_object, 0, 0, 0, 0, sign * balance_result.hours, sign * balance_result.minutes, sign * balance_result.seconds, sign * balance_result.milliseconds, sign * balance_result.microseconds, sign * balance_result.nanoseconds));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 14. If ? TimeZoneEquals(zonedDateTime.[[TimeZone]], other.[[TimeZone]]) is false, then
|
||||||
|
if (!TRY(time_zone_equals(global_object, zoned_date_time.time_zone(), other->time_zone()))) {
|
||||||
|
// a. Throw a RangeError exception.
|
||||||
|
return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalDifferentTimeZones);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 15. Let untilOptions be ? MergeLargestUnitOption(options, largestUnit).
|
||||||
|
auto* until_options = TRY(merge_largest_unit_option(global_object, options, *largest_unit));
|
||||||
|
|
||||||
|
// 16. Let difference be ? DifferenceZonedDateTime(zonedDateTime.[[Nanoseconds]], other.[[Nanoseconds]], zonedDateTime.[[TimeZone]], zonedDateTime.[[Calendar]], largestUnit, untilOptions).
|
||||||
|
auto difference = TRY(difference_zoned_date_time(global_object, zoned_date_time.nanoseconds(), other->nanoseconds(), zoned_date_time.time_zone(), zoned_date_time.calendar(), *largest_unit, until_options));
|
||||||
|
|
||||||
|
// 17. Let roundResult be (? RoundDuration(difference.[[Years]], difference.[[Months]], difference.[[Weeks]], difference.[[Days]], difference.[[Hours]], difference.[[Minutes]], difference.[[Seconds]], difference.[[Milliseconds]], difference.[[Microseconds]], difference.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode, zonedDateTime)).[[DurationRecord]].
|
||||||
|
auto round_result = TRY(round_duration(global_object, difference.years, difference.months, difference.weeks, difference.days, difference.hours, difference.minutes, difference.seconds, difference.milliseconds, difference.microseconds, difference.nanoseconds, rounding_increment, *smallest_unit, rounding_mode, &zoned_date_time)).duration_record;
|
||||||
|
|
||||||
|
// 18. Let result be ? AdjustRoundedDurationDays(roundResult.[[Years]], roundResult.[[Months]], roundResult.[[Weeks]], roundResult.[[Days]], roundResult.[[Hours]], roundResult.[[Minutes]], roundResult.[[Seconds]], roundResult.[[Milliseconds]], roundResult.[[Microseconds]], roundResult.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode, zonedDateTime).
|
||||||
|
auto result = TRY(adjust_rounded_duration_days(global_object, round_result.years, round_result.months, round_result.weeks, round_result.days, round_result.hours, round_result.minutes, round_result.seconds, round_result.milliseconds, round_result.microseconds, round_result.nanoseconds, rounding_increment, *smallest_unit, rounding_mode, &zoned_date_time));
|
||||||
|
|
||||||
|
// 19. Return ! CreateTemporalDuration(sign × result.[[Years]], sign × result.[[Months]], sign × result.[[Weeks]], sign × result.[[Days]], sign × result.[[Hours]], sign × result.[[Minutes]], sign × result.[[Seconds]], sign × result.[[Milliseconds]], sign × result.[[Microseconds]], sign × result.[[Nanoseconds]]).
|
||||||
|
return MUST(create_temporal_duration(global_object, sign * result.years, sign * result.months, sign * result.weeks, sign * result.days, sign * result.hours, sign * result.minutes, sign * result.seconds, sign * result.milliseconds, sign * result.microseconds, sign * result.nanoseconds));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6.5.9 AddDurationToOrSubtractDurationFromZonedDateTime ( operation, zonedDateTime, temporalDurationLike, options ), https://tc39.es/proposal-temporal/#sec-temporal-adddurationtoOrsubtractdurationfromzoneddatetime
|
||||||
ThrowCompletionOr<ZonedDateTime*> add_duration_to_or_subtract_duration_from_zoned_date_time(GlobalObject& global_object, ArithmeticOperation operation, ZonedDateTime& zoned_date_time, Value temporal_duration_like, Value options_value)
|
ThrowCompletionOr<ZonedDateTime*> add_duration_to_or_subtract_duration_from_zoned_date_time(GlobalObject& global_object, ArithmeticOperation operation, ZonedDateTime& zoned_date_time, Value temporal_duration_like, Value options_value)
|
||||||
{
|
{
|
||||||
// 1. If operation is subtract, let sign be -1. Otherwise, let sign be 1.
|
// 1. If operation is subtract, let sign be -1. Otherwise, let sign be 1.
|
||||||
|
|
|
@ -57,6 +57,7 @@ ThrowCompletionOr<String> temporal_zoned_date_time_to_string(GlobalObject&, Zone
|
||||||
ThrowCompletionOr<BigInt*> add_zoned_date_time(GlobalObject&, BigInt const& epoch_nanoseconds, Value time_zone, Object& calendar, double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds, Object* options = nullptr);
|
ThrowCompletionOr<BigInt*> add_zoned_date_time(GlobalObject&, BigInt const& epoch_nanoseconds, Value time_zone, Object& calendar, double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds, Object* options = nullptr);
|
||||||
ThrowCompletionOr<DurationRecord> difference_zoned_date_time(GlobalObject&, BigInt const& nanoseconds1, BigInt const& nanoseconds2, Object& time_zone, Object& calendar, StringView largest_unit, Object const* options = nullptr);
|
ThrowCompletionOr<DurationRecord> difference_zoned_date_time(GlobalObject&, BigInt const& nanoseconds1, BigInt const& nanoseconds2, Object& time_zone, Object& calendar, StringView largest_unit, Object const* options = nullptr);
|
||||||
ThrowCompletionOr<NanosecondsToDaysResult> nanoseconds_to_days(GlobalObject&, Crypto::SignedBigInteger nanoseconds, Value relative_to);
|
ThrowCompletionOr<NanosecondsToDaysResult> nanoseconds_to_days(GlobalObject&, Crypto::SignedBigInteger nanoseconds, Value relative_to);
|
||||||
|
ThrowCompletionOr<Duration*> difference_temporal_zoned_date_time(GlobalObject&, DifferenceOperation, ZonedDateTime&, Value other, Value options);
|
||||||
ThrowCompletionOr<ZonedDateTime*> add_duration_to_or_subtract_duration_from_zoned_date_time(GlobalObject&, ArithmeticOperation, ZonedDateTime&, Value temporal_duration_like, Value options_value);
|
ThrowCompletionOr<ZonedDateTime*> add_duration_to_or_subtract_duration_from_zoned_date_time(GlobalObject&, ArithmeticOperation, ZonedDateTime&, Value temporal_duration_like, Value options_value);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -923,152 +923,29 @@ JS_DEFINE_NATIVE_FUNCTION(ZonedDateTimePrototype::subtract)
|
||||||
// 6.3.37 Temporal.ZonedDateTime.prototype.until ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.until
|
// 6.3.37 Temporal.ZonedDateTime.prototype.until ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.until
|
||||||
JS_DEFINE_NATIVE_FUNCTION(ZonedDateTimePrototype::until)
|
JS_DEFINE_NATIVE_FUNCTION(ZonedDateTimePrototype::until)
|
||||||
{
|
{
|
||||||
|
auto other = vm.argument(0);
|
||||||
|
auto options = vm.argument(1);
|
||||||
|
|
||||||
// 1. Let zonedDateTime be the this value.
|
// 1. Let zonedDateTime be the this value.
|
||||||
// 2. Perform ? RequireInternalSlot(zonedDateTime, [[InitializedTemporalZonedDateTime]]).
|
// 2. Perform ? RequireInternalSlot(zonedDateTime, [[InitializedTemporalZonedDateTime]]).
|
||||||
auto* zoned_date_time = TRY(typed_this_object(global_object));
|
auto* zoned_date_time = TRY(typed_this_object(global_object));
|
||||||
|
|
||||||
// 3. Set other to ? ToTemporalZonedDateTime(other).
|
// 3. Return ? DifferenceTemporalZonedDateTime(until, zonedDateTime, other, options).
|
||||||
auto* other = TRY(to_temporal_zoned_date_time(global_object, vm.argument(0)));
|
return TRY(difference_temporal_zoned_date_time(global_object, DifferenceOperation::Until, *zoned_date_time, other, options));
|
||||||
|
|
||||||
// 4. If ? CalendarEquals(zonedDateTime.[[Calendar]], other.[[Calendar]]) is false, then
|
|
||||||
if (!TRY(calendar_equals(global_object, zoned_date_time->calendar(), other->calendar()))) {
|
|
||||||
// a. Throw a RangeError exception.
|
|
||||||
return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalDifferentCalendars);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. Set options to ? GetOptionsObject(options).
|
|
||||||
auto* options = TRY(get_options_object(global_object, vm.argument(1)));
|
|
||||||
|
|
||||||
// 6. Let smallestUnit be ? ToSmallestTemporalUnit(options, « », "nanosecond").
|
|
||||||
auto smallest_unit = TRY(to_smallest_temporal_unit(global_object, *options, {}, "nanosecond"sv));
|
|
||||||
|
|
||||||
// 7. Let defaultLargestUnit be ! LargerOfTwoTemporalUnits("hour", smallestUnit).
|
|
||||||
auto default_largest_unit = larger_of_two_temporal_units("hour"sv, *smallest_unit);
|
|
||||||
|
|
||||||
// 8. Let largestUnit be ? ToLargestTemporalUnit(options, « », "auto", defaultLargestUnit).
|
|
||||||
auto largest_unit = TRY(to_largest_temporal_unit(global_object, *options, {}, "auto"sv, default_largest_unit));
|
|
||||||
|
|
||||||
// 9. Perform ? ValidateTemporalUnitRange(largestUnit, smallestUnit).
|
|
||||||
TRY(validate_temporal_unit_range(global_object, *largest_unit, *smallest_unit));
|
|
||||||
|
|
||||||
// 10. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc").
|
|
||||||
auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *options, "trunc"sv));
|
|
||||||
|
|
||||||
// 11. Let maximum be ! MaximumTemporalDurationRoundingIncrement(smallestUnit).
|
|
||||||
auto maximum = maximum_temporal_duration_rounding_increment(*smallest_unit);
|
|
||||||
|
|
||||||
// 12. Let roundingIncrement be ? ToTemporalRoundingIncrement(options, maximum, false).
|
|
||||||
auto rounding_increment = TRY(to_temporal_rounding_increment(global_object, *options, Optional<double>(maximum), false));
|
|
||||||
|
|
||||||
// 13. If largestUnit is not one of "year", "month", "week", or "day", then
|
|
||||||
if (!largest_unit->is_one_of("year"sv, "month"sv, "week"sv, "day"sv)) {
|
|
||||||
// a. Let differenceNs be ! DifferenceInstant(zonedDateTime.[[Nanoseconds]], other.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode).
|
|
||||||
auto* difference_ns = difference_instant(global_object, zoned_date_time->nanoseconds(), other->nanoseconds(), rounding_increment, *smallest_unit, rounding_mode);
|
|
||||||
|
|
||||||
// b. Let balanceResult be ! BalanceDuration(0, 0, 0, 0, 0, 0, differenceNs, largestUnit).
|
|
||||||
auto balance_result = MUST(balance_duration(global_object, 0, 0, 0, 0, 0, 0, difference_ns->big_integer(), *largest_unit));
|
|
||||||
|
|
||||||
// c. Return ? CreateTemporalDuration(0, 0, 0, 0, balanceResult.[[Hours]], balanceResult.[[Minutes]], balanceResult.[[Seconds]], balanceResult.[[Milliseconds]], balanceResult.[[Microseconds]], balanceResult.[[Nanoseconds]]).
|
|
||||||
return TRY(create_temporal_duration(global_object, 0, 0, 0, 0, balance_result.hours, balance_result.minutes, balance_result.seconds, balance_result.milliseconds, balance_result.microseconds, balance_result.nanoseconds));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 14. If ? TimeZoneEquals(zonedDateTime.[[TimeZone]], other.[[TimeZone]]) is false, then
|
|
||||||
if (!TRY(time_zone_equals(global_object, zoned_date_time->time_zone(), other->time_zone()))) {
|
|
||||||
// a. Throw a RangeError exception.
|
|
||||||
return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalDifferentTimeZones);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 15. Let untilOptions be ? MergeLargestUnitOption(options, largestUnit).
|
|
||||||
auto* until_options = TRY(merge_largest_unit_option(global_object, options, *largest_unit));
|
|
||||||
|
|
||||||
// 16. Let difference be ? DifferenceZonedDateTime(zonedDateTime.[[Nanoseconds]], other.[[Nanoseconds]], zonedDateTime.[[TimeZone]], zonedDateTime.[[Calendar]], largestUnit, untilOptions).
|
|
||||||
auto difference = TRY(difference_zoned_date_time(global_object, zoned_date_time->nanoseconds(), other->nanoseconds(), zoned_date_time->time_zone(), zoned_date_time->calendar(), *largest_unit, until_options));
|
|
||||||
|
|
||||||
// 17. Let roundResult be (? RoundDuration(difference.[[Years]], difference.[[Months]], difference.[[Weeks]], difference.[[Days]], difference.[[Hours]], difference.[[Minutes]], difference.[[Seconds]], difference.[[Milliseconds]], difference.[[Microseconds]], difference.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode, zonedDateTime)).[[DurationRecord]].
|
|
||||||
auto round_result = TRY(round_duration(global_object, difference.years, difference.months, difference.weeks, difference.days, difference.hours, difference.minutes, difference.seconds, difference.milliseconds, difference.microseconds, difference.nanoseconds, rounding_increment, *smallest_unit, rounding_mode, zoned_date_time)).duration_record;
|
|
||||||
|
|
||||||
// 18. Let result be ? AdjustRoundedDurationDays(roundResult.[[Years]], roundResult.[[Months]], roundResult.[[Weeks]], roundResult.[[Days]], roundResult.[[Hours]], roundResult.[[Minutes]], roundResult.[[Seconds]], roundResult.[[Milliseconds]], roundResult.[[Microseconds]], roundResult.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode, zonedDateTime).
|
|
||||||
auto result = TRY(adjust_rounded_duration_days(global_object, round_result.years, round_result.months, round_result.weeks, round_result.days, round_result.hours, round_result.minutes, round_result.seconds, round_result.milliseconds, round_result.microseconds, round_result.nanoseconds, rounding_increment, *smallest_unit, rounding_mode, zoned_date_time));
|
|
||||||
|
|
||||||
// 19. Return ! CreateTemporalDuration(result.[[Years]], result.[[Months]], result.[[Weeks]], result.[[Days]], result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]]).
|
|
||||||
return MUST(create_temporal_duration(global_object, result.years, result.months, result.weeks, result.days, result.hours, result.minutes, result.seconds, result.milliseconds, result.microseconds, result.nanoseconds));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6.3.38 Temporal.ZonedDateTime.prototype.since ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.since
|
// 6.3.38 Temporal.ZonedDateTime.prototype.since ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.since
|
||||||
JS_DEFINE_NATIVE_FUNCTION(ZonedDateTimePrototype::since)
|
JS_DEFINE_NATIVE_FUNCTION(ZonedDateTimePrototype::since)
|
||||||
{
|
{
|
||||||
|
auto other = vm.argument(0);
|
||||||
|
auto options = vm.argument(1);
|
||||||
|
|
||||||
// 1. Let zonedDateTime be the this value.
|
// 1. Let zonedDateTime be the this value.
|
||||||
// 2. Perform ? RequireInternalSlot(zonedDateTime, [[InitializedTemporalZonedDateTime]]).
|
// 2. Perform ? RequireInternalSlot(zonedDateTime, [[InitializedTemporalZonedDateTime]]).
|
||||||
auto* zoned_date_time = TRY(typed_this_object(global_object));
|
auto* zoned_date_time = TRY(typed_this_object(global_object));
|
||||||
|
|
||||||
// 3. Set other to ? ToTemporalZonedDateTime(other).
|
// 3. Return ? DifferenceTemporalZonedDateTime(since, zonedDateTime, other, options).
|
||||||
auto* other = TRY(to_temporal_zoned_date_time(global_object, vm.argument(0)));
|
return TRY(difference_temporal_zoned_date_time(global_object, DifferenceOperation::Since, *zoned_date_time, other, options));
|
||||||
|
|
||||||
// 4. If ? CalendarEquals(zonedDateTime.[[Calendar]], other.[[Calendar]]) is false, then
|
|
||||||
if (!TRY(calendar_equals(global_object, zoned_date_time->calendar(), other->calendar()))) {
|
|
||||||
// a. Throw a RangeError exception.
|
|
||||||
return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalDifferentCalendars);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. Set options to ? GetOptionsObject(options).
|
|
||||||
auto* options = TRY(get_options_object(global_object, vm.argument(1)));
|
|
||||||
|
|
||||||
// 6. Let smallestUnit be ? ToSmallestTemporalUnit(options, « », "nanosecond").
|
|
||||||
auto smallest_unit = TRY(to_smallest_temporal_unit(global_object, *options, {}, "nanosecond"sv));
|
|
||||||
|
|
||||||
// 7. Let defaultLargestUnit be ! LargerOfTwoTemporalUnits("hour", smallestUnit).
|
|
||||||
auto default_largest_unit = larger_of_two_temporal_units("hour"sv, *smallest_unit);
|
|
||||||
|
|
||||||
// 8. Let largestUnit be ? ToLargestTemporalUnit(options, « », "auto", defaultLargestUnit).
|
|
||||||
auto largest_unit = TRY(to_largest_temporal_unit(global_object, *options, {}, "auto"sv, default_largest_unit));
|
|
||||||
|
|
||||||
// 9. Perform ? ValidateTemporalUnitRange(largestUnit, smallestUnit).
|
|
||||||
TRY(validate_temporal_unit_range(global_object, *largest_unit, *smallest_unit));
|
|
||||||
|
|
||||||
// 10. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc").
|
|
||||||
auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *options, "trunc"sv));
|
|
||||||
|
|
||||||
// 11. Set roundingMode to ! NegateTemporalRoundingMode(roundingMode).
|
|
||||||
rounding_mode = negate_temporal_rounding_mode(rounding_mode);
|
|
||||||
|
|
||||||
// 12. Let maximum be ! MaximumTemporalDurationRoundingIncrement(smallestUnit).
|
|
||||||
auto maximum = maximum_temporal_duration_rounding_increment(*smallest_unit);
|
|
||||||
|
|
||||||
// 13. Let roundingIncrement be ? ToTemporalRoundingIncrement(options, maximum, false).
|
|
||||||
auto rounding_increment = TRY(to_temporal_rounding_increment(global_object, *options, Optional<double>(maximum), false));
|
|
||||||
|
|
||||||
// 14. If largestUnit is not one of "year", "month", "week", or "day", then
|
|
||||||
if (!largest_unit->is_one_of("year"sv, "month"sv, "week"sv, "day"sv)) {
|
|
||||||
// a. Let differenceNs be ! DifferenceInstant(zonedDateTime.[[Nanoseconds]], other.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode).
|
|
||||||
auto* difference_ns = difference_instant(global_object, zoned_date_time->nanoseconds(), other->nanoseconds(), rounding_increment, *smallest_unit, rounding_mode);
|
|
||||||
|
|
||||||
// b. Let balanceResult be ! BalanceDuration(0, 0, 0, 0, 0, 0, differenceNs, largestUnit).
|
|
||||||
auto balance_result = MUST(balance_duration(global_object, 0, 0, 0, 0, 0, 0, difference_ns->big_integer(), *largest_unit));
|
|
||||||
|
|
||||||
// c. Return ? CreateTemporalDuration(0, 0, 0, 0, -balanceResult.[[Hours]], -balanceResult.[[Minutes]], -balanceResult.[[Seconds]], -balanceResult.[[Milliseconds]], -balanceResult.[[Microseconds]], -balanceResult.[[Nanoseconds]]).
|
|
||||||
return TRY(create_temporal_duration(global_object, 0, 0, 0, 0, -balance_result.hours, -balance_result.minutes, -balance_result.seconds, -balance_result.milliseconds, -balance_result.microseconds, -balance_result.nanoseconds));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 15. If ? TimeZoneEquals(zonedDateTime.[[TimeZone]], other.[[TimeZone]]) is false, then
|
|
||||||
if (!TRY(time_zone_equals(global_object, zoned_date_time->time_zone(), other->time_zone()))) {
|
|
||||||
// a. Throw a RangeError exception.
|
|
||||||
return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalDifferentTimeZones);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 16. Let untilOptions be ? MergeLargestUnitOption(options, largestUnit).
|
|
||||||
auto* until_options = TRY(merge_largest_unit_option(global_object, options, *largest_unit));
|
|
||||||
|
|
||||||
// 17. Let difference be ? DifferenceZonedDateTime(zonedDateTime.[[Nanoseconds]], other.[[Nanoseconds]], zonedDateTime.[[TimeZone]], zonedDateTime.[[Calendar]], largestUnit, untilOptions).
|
|
||||||
auto difference = TRY(difference_zoned_date_time(global_object, zoned_date_time->nanoseconds(), other->nanoseconds(), zoned_date_time->time_zone(), zoned_date_time->calendar(), *largest_unit, until_options));
|
|
||||||
|
|
||||||
// 18. Let roundResult be (? RoundDuration(difference.[[Years]], difference.[[Months]], difference.[[Weeks]], difference.[[Days]], difference.[[Hours]], difference.[[Minutes]], difference.[[Seconds]], difference.[[Milliseconds]], difference.[[Microseconds]], difference.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode, zonedDateTime)).[[DurationRecord]].
|
|
||||||
auto round_result = TRY(round_duration(global_object, difference.years, difference.months, difference.weeks, difference.days, difference.hours, difference.minutes, difference.seconds, difference.milliseconds, difference.microseconds, difference.nanoseconds, rounding_increment, *smallest_unit, rounding_mode, zoned_date_time)).duration_record;
|
|
||||||
|
|
||||||
// 19. Let result be ? AdjustRoundedDurationDays(roundResult.[[Years]], roundResult.[[Months]], roundResult.[[Weeks]], roundResult.[[Days]], roundResult.[[Hours]], roundResult.[[Minutes]], roundResult.[[Seconds]], roundResult.[[Milliseconds]], roundResult.[[Microseconds]], roundResult.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode, zonedDateTime).
|
|
||||||
auto result = TRY(adjust_rounded_duration_days(global_object, round_result.years, round_result.months, round_result.weeks, round_result.days, round_result.hours, round_result.minutes, round_result.seconds, round_result.milliseconds, round_result.microseconds, round_result.nanoseconds, rounding_increment, *smallest_unit, rounding_mode, zoned_date_time));
|
|
||||||
|
|
||||||
// 20. Return ! CreateTemporalDuration(-result.[[Years]], -result.[[Months]], -result.[[Weeks]], -result.[[Days]], -result.[[Hours]], -result.[[Minutes]], -result.[[Seconds]], -result.[[Milliseconds]], -result.[[Microseconds]], -result.[[Nanoseconds]]).
|
|
||||||
return MUST(create_temporal_duration(global_object, -result.years, -result.months, -result.weeks, -result.days, -result.hours, -result.minutes, -result.seconds, -result.milliseconds, -result.microseconds, -result.nanoseconds));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6.3.39 Temporal.ZonedDateTime.prototype.round ( roundTo ), https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.round
|
// 6.3.39 Temporal.ZonedDateTime.prototype.round ( roundTo ), https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.round
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue