mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-05 15:49:11 +00:00
LibJS: Implement the Temporal.Duration relative-to ZonedDateTime options
This commit is contained in:
parent
3e6133cc09
commit
18f95434bc
Notes:
github-actions[bot]
2024-11-26 10:04:10 +00:00
Author: https://github.com/trflynn89
Commit: 18f95434bc
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/2577
Reviewed-by: https://github.com/shannonbooth ✅
9 changed files with 296 additions and 35 deletions
|
@ -475,12 +475,16 @@ ThrowCompletionOr<RelativeTo> get_temporal_relative_to_option(VM& vm, Object con
|
||||||
if (value.is_undefined())
|
if (value.is_undefined())
|
||||||
return RelativeTo { .plain_relative_to = {}, .zoned_relative_to = {} };
|
return RelativeTo { .plain_relative_to = {}, .zoned_relative_to = {} };
|
||||||
|
|
||||||
// FIXME: 3. Let offsetBehaviour be OPTION.
|
// 3. Let offsetBehaviour be OPTION.
|
||||||
// FIXME: 4. Let matchBehaviour be MATCH-EXACTLY.
|
auto offset_behavior = OffsetBehavior::Option;
|
||||||
|
|
||||||
|
// 4. Let matchBehaviour be MATCH-EXACTLY.
|
||||||
|
auto match_behavior = MatchBehavior::MatchExactly;
|
||||||
|
|
||||||
String calendar;
|
String calendar;
|
||||||
Optional<String> time_zone;
|
Optional<String> time_zone;
|
||||||
Optional<String> offset_string;
|
Optional<String> offset_string;
|
||||||
|
|
||||||
ISODate iso_date;
|
ISODate iso_date;
|
||||||
Variant<ParsedISODateTime::StartOfDay, Time> time { Time {} };
|
Variant<ParsedISODateTime::StartOfDay, Time> time { Time {} };
|
||||||
|
|
||||||
|
@ -488,8 +492,11 @@ ThrowCompletionOr<RelativeTo> get_temporal_relative_to_option(VM& vm, Object con
|
||||||
if (value.is_object()) {
|
if (value.is_object()) {
|
||||||
auto& object = value.as_object();
|
auto& object = value.as_object();
|
||||||
|
|
||||||
// FIXME: a. If value has an [[InitializedTemporalZonedDateTime]] internal slot, then
|
// a. If value has an [[InitializedTemporalZonedDateTime]] internal slot, then
|
||||||
// FIXME: i. Return the Record { [[PlainRelativeTo]]: undefined, [[ZonedRelativeTo]]: value }.
|
if (is<ZonedDateTime>(object)) {
|
||||||
|
// i. Return the Record { [[PlainRelativeTo]]: undefined, [[ZonedRelativeTo]]: value }.
|
||||||
|
return RelativeTo { .plain_relative_to = {}, .zoned_relative_to = static_cast<ZonedDateTime&>(object) };
|
||||||
|
}
|
||||||
|
|
||||||
// b. If value has an [[InitializedTemporalDate]] internal slot, then
|
// b. If value has an [[InitializedTemporalDate]] internal slot, then
|
||||||
if (is<PlainDate>(object)) {
|
if (is<PlainDate>(object)) {
|
||||||
|
@ -527,7 +534,8 @@ ThrowCompletionOr<RelativeTo> get_temporal_relative_to_option(VM& vm, Object con
|
||||||
|
|
||||||
// i. If offsetString is UNSET, then
|
// i. If offsetString is UNSET, then
|
||||||
if (!offset_string.has_value()) {
|
if (!offset_string.has_value()) {
|
||||||
// FIXME: i. Set offsetBehaviour to WALL.
|
// i. Set offsetBehaviour to WALL.
|
||||||
|
offset_behavior = OffsetBehavior::Wall;
|
||||||
}
|
}
|
||||||
|
|
||||||
// j. Let isoDate be result.[[ISODate]].
|
// j. Let isoDate be result.[[ISODate]].
|
||||||
|
@ -563,14 +571,17 @@ ThrowCompletionOr<RelativeTo> get_temporal_relative_to_option(VM& vm, Object con
|
||||||
|
|
||||||
// ii. If result.[[TimeZone]].[[Z]] is true, then
|
// ii. If result.[[TimeZone]].[[Z]] is true, then
|
||||||
if (result.time_zone.z_designator) {
|
if (result.time_zone.z_designator) {
|
||||||
// FIXME: 1. Set offsetBehaviour to EXACT.
|
// 1. Set offsetBehaviour to EXACT.
|
||||||
|
offset_behavior = OffsetBehavior::Exact;
|
||||||
}
|
}
|
||||||
// iii. Else if offsetString is EMPTY, then
|
// iii. Else if offsetString is EMPTY, then
|
||||||
else if (!offset_string.has_value()) {
|
else if (!offset_string.has_value()) {
|
||||||
// FIXME: 1. Set offsetBehaviour to WALL.
|
// 1. Set offsetBehaviour to WALL.
|
||||||
|
offset_behavior = OffsetBehavior::Wall;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: iv. Set matchBehaviour to MATCH-MINUTES.
|
// iv. Set matchBehaviour to MATCH-MINUTES.
|
||||||
|
match_behavior = MatchBehavior::MatchMinutes;
|
||||||
}
|
}
|
||||||
|
|
||||||
// g. Let calendar be result.[[Calendar]].
|
// g. Let calendar be result.[[Calendar]].
|
||||||
|
@ -596,15 +607,27 @@ ThrowCompletionOr<RelativeTo> get_temporal_relative_to_option(VM& vm, Object con
|
||||||
return RelativeTo { .plain_relative_to = plain_date, .zoned_relative_to = {} };
|
return RelativeTo { .plain_relative_to = plain_date, .zoned_relative_to = {} };
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: 8. If offsetBehaviour is OPTION, then
|
double offset_nanoseconds = 0;
|
||||||
// FIXME: a. Let offsetNs be ! ParseDateTimeUTCOffset(offsetString).
|
|
||||||
// FIXME: 9. Else,
|
|
||||||
// FIXME: a. Let offsetNs be 0.
|
|
||||||
// FIXME: 10. Let epochNanoseconds be ? InterpretISODateTimeOffset(isoDate, time, offsetBehaviour, offsetNs, timeZone, compatible, reject, matchBehaviour).
|
|
||||||
// FIXME: 11. Let zonedRelativeTo be ! CreateTemporalZonedDateTime(epochNanoseconds, timeZone, calendar).
|
|
||||||
// FIXME: 12. Return the Record { [[PlainRelativeTo]]: undefined, [[ZonedRelativeTo]]: zonedRelativeTo }.
|
|
||||||
|
|
||||||
return RelativeTo { .plain_relative_to = {}, .zoned_relative_to = {} };
|
// 8. If offsetBehaviour is OPTION, then
|
||||||
|
if (offset_behavior == OffsetBehavior::Option) {
|
||||||
|
// a. Let offsetNs be ! ParseDateTimeUTCOffset(offsetString).
|
||||||
|
offset_nanoseconds = parse_date_time_utc_offset(*offset_string);
|
||||||
|
}
|
||||||
|
// 9. Else,
|
||||||
|
else {
|
||||||
|
// a. Let offsetNs be 0.
|
||||||
|
offset_nanoseconds = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 10. Let epochNanoseconds be ? InterpretISODateTimeOffset(isoDate, time, offsetBehaviour, offsetNs, timeZone, COMPATIBLE, REJECT, matchBehaviour).
|
||||||
|
auto epoch_nanoseconds = TRY(interpret_iso_date_time_offset(vm, iso_date, time, offset_behavior, offset_nanoseconds, *time_zone, Disambiguation::Compatible, OffsetOption::Reject, match_behavior));
|
||||||
|
|
||||||
|
// 11. Let zonedRelativeTo be ! CreateTemporalZonedDateTime(epochNanoseconds, timeZone, calendar).
|
||||||
|
auto zoned_relative_to = MUST(create_temporal_zoned_date_time(vm, BigInt::create(vm, move(epoch_nanoseconds)), time_zone.release_value(), move(calendar)));
|
||||||
|
|
||||||
|
// 12. Return the Record { [[PlainRelativeTo]]: undefined, [[ZonedRelativeTo]]: zonedRelativeTo }.
|
||||||
|
return RelativeTo { .plain_relative_to = {}, .zoned_relative_to = zoned_relative_to };
|
||||||
}
|
}
|
||||||
|
|
||||||
// 13.19 LargerOfTwoTemporalUnits ( u1, u2 ), https://tc39.es/proposal-temporal/#sec-temporal-largeroftwotemporalunits
|
// 13.19 LargerOfTwoTemporalUnits ( u1, u2 ), https://tc39.es/proposal-temporal/#sec-temporal-largeroftwotemporalunits
|
||||||
|
|
|
@ -143,8 +143,8 @@ struct SecondsStringPrecision {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RelativeTo {
|
struct RelativeTo {
|
||||||
GC::Ptr<PlainDate> plain_relative_to; // [[PlainRelativeTo]]
|
GC::Ptr<PlainDate> plain_relative_to; // [[PlainRelativeTo]]
|
||||||
GC::Ptr<Object> zoned_relative_to; // FIXME: [[ZonedRelativeTo]]
|
GC::Ptr<ZonedDateTime> zoned_relative_to; // [[ZonedRelativeTo]]
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DifferenceSettings {
|
struct DifferenceSettings {
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
|
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
|
||||||
#include <LibJS/Runtime/Temporal/Duration.h>
|
#include <LibJS/Runtime/Temporal/Duration.h>
|
||||||
#include <LibJS/Runtime/Temporal/DurationConstructor.h>
|
#include <LibJS/Runtime/Temporal/DurationConstructor.h>
|
||||||
|
#include <LibJS/Runtime/Temporal/ZonedDateTime.h>
|
||||||
|
|
||||||
namespace JS::Temporal {
|
namespace JS::Temporal {
|
||||||
|
|
||||||
|
@ -149,12 +150,25 @@ JS_DEFINE_NATIVE_FUNCTION(DurationConstructor::compare)
|
||||||
|
|
||||||
// 12. If zonedRelativeTo is not undefined, and either TemporalUnitCategory(largestUnit1) or TemporalUnitCategory(largestUnit2) is date, then
|
// 12. If zonedRelativeTo is not undefined, and either TemporalUnitCategory(largestUnit1) or TemporalUnitCategory(largestUnit2) is date, then
|
||||||
if (zoned_relative_to && (temporal_unit_category(largest_unit1) == UnitCategory::Date || temporal_unit_category(largest_unit2) == UnitCategory::Date)) {
|
if (zoned_relative_to && (temporal_unit_category(largest_unit1) == UnitCategory::Date || temporal_unit_category(largest_unit2) == UnitCategory::Date)) {
|
||||||
// FIXME: a. Let timeZone be zonedRelativeTo.[[TimeZone]].
|
// a. Let timeZone be zonedRelativeTo.[[TimeZone]].
|
||||||
// FIXME: b. Let calendar be zonedRelativeTo.[[Calendar]].
|
auto const time_zone = zoned_relative_to->time_zone();
|
||||||
// FIXME: c. Let after1 be ? AddZonedDateTime(zonedRelativeTo.[[EpochNanoseconds]], timeZone, calendar, duration1, constrain).
|
|
||||||
// FIXME: d. Let after2 be ? AddZonedDateTime(zonedRelativeTo.[[EpochNanoseconds]], timeZone, calendar, duration2, constrain).
|
// b. Let calendar be zonedRelativeTo.[[Calendar]].
|
||||||
// FIXME: e. If after1 > after2, return 1𝔽.
|
auto const& calendar = zoned_relative_to->calendar();
|
||||||
// FIXME: f. If after1 < after2, return -1𝔽.
|
|
||||||
|
// c. Let after1 be ? AddZonedDateTime(zonedRelativeTo.[[EpochNanoseconds]], timeZone, calendar, duration1, CONSTRAIN).
|
||||||
|
auto after1 = TRY(add_zoned_date_time(vm, zoned_relative_to->epoch_nanoseconds()->big_integer(), time_zone, calendar, duration1, Overflow::Constrain));
|
||||||
|
|
||||||
|
// d. Let after2 be ? AddZonedDateTime(zonedRelativeTo.[[EpochNanoseconds]], timeZone, calendar, duration2, CONSTRAIN).
|
||||||
|
auto after2 = TRY(add_zoned_date_time(vm, zoned_relative_to->epoch_nanoseconds()->big_integer(), time_zone, calendar, duration2, Overflow::Constrain));
|
||||||
|
|
||||||
|
// e. If after1 > after2, return 1𝔽.
|
||||||
|
if (after1 > after2)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
// f. If after1 < after2, return -1𝔽.
|
||||||
|
if (after1 < after2)
|
||||||
|
return -1;
|
||||||
|
|
||||||
// g. Return +0𝔽.
|
// g. Return +0𝔽.
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include <LibJS/Runtime/Temporal/PlainDate.h>
|
#include <LibJS/Runtime/Temporal/PlainDate.h>
|
||||||
#include <LibJS/Runtime/Temporal/PlainDateTime.h>
|
#include <LibJS/Runtime/Temporal/PlainDateTime.h>
|
||||||
#include <LibJS/Runtime/Temporal/PlainTime.h>
|
#include <LibJS/Runtime/Temporal/PlainTime.h>
|
||||||
|
#include <LibJS/Runtime/Temporal/ZonedDateTime.h>
|
||||||
|
|
||||||
namespace JS::Temporal {
|
namespace JS::Temporal {
|
||||||
|
|
||||||
|
@ -347,11 +348,20 @@ JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::round)
|
||||||
// a. Let internalDuration be ToInternalDurationRecord(duration).
|
// a. Let internalDuration be ToInternalDurationRecord(duration).
|
||||||
auto internal_duration = to_internal_duration_record(vm, duration);
|
auto internal_duration = to_internal_duration_record(vm, duration);
|
||||||
|
|
||||||
// FIXME: b. Let timeZone be zonedRelativeTo.[[TimeZone]].
|
// b. Let timeZone be zonedRelativeTo.[[TimeZone]].
|
||||||
// FIXME: c. Let calendar be zonedRelativeTo.[[Calendar]].
|
auto const& time_zone = zoned_relative_to->time_zone();
|
||||||
// FIXME: d. Let relativeEpochNs be zonedRelativeTo.[[EpochNanoseconds]].
|
|
||||||
// FIXME: e. Let targetEpochNs be ? AddZonedDateTime(relativeEpochNs, timeZone, calendar, internalDuration, constrain).
|
// c. Let calendar be zonedRelativeTo.[[Calendar]].
|
||||||
// FIXME: f. Set internalDuration to ? DifferenceZonedDateTimeWithRounding(relativeEpochNs, targetEpochNs, timeZone, calendar, largestUnit, roundingIncrement, smallestUnit, roundingMode).
|
auto const& calendar = zoned_relative_to->calendar();
|
||||||
|
|
||||||
|
// d. Let relativeEpochNs be zonedRelativeTo.[[EpochNanoseconds]].
|
||||||
|
auto const& relative_epoch_nanoseconds = zoned_relative_to->epoch_nanoseconds()->big_integer();
|
||||||
|
|
||||||
|
// e. Let targetEpochNs be ? AddZonedDateTime(relativeEpochNs, timeZone, calendar, internalDuration, CONSTRAIN).
|
||||||
|
auto target_epoch_nanoseconds = TRY(add_zoned_date_time(vm, relative_epoch_nanoseconds, time_zone, calendar, internal_duration, Overflow::Constrain));
|
||||||
|
|
||||||
|
// f. Set internalDuration to ? DifferenceZonedDateTimeWithRounding(relativeEpochNs, targetEpochNs, timeZone, calendar, largestUnit, roundingIncrement, smallestUnit, roundingMode).
|
||||||
|
internal_duration = TRY(difference_zoned_date_time_with_rounding(vm, relative_epoch_nanoseconds, target_epoch_nanoseconds, time_zone, calendar, largest_unit_value, rounding_increment, smallest_unit_value, rounding_mode));
|
||||||
|
|
||||||
// g. If TemporalUnitCategory(largestUnit) is date, set largestUnit to hour.
|
// g. If TemporalUnitCategory(largestUnit) is date, set largestUnit to hour.
|
||||||
if (temporal_unit_category(largest_unit_value) == UnitCategory::Date)
|
if (temporal_unit_category(largest_unit_value) == UnitCategory::Date)
|
||||||
|
@ -479,12 +489,23 @@ JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::total)
|
||||||
|
|
||||||
// 11. If zonedRelativeTo is not undefined, then
|
// 11. If zonedRelativeTo is not undefined, then
|
||||||
if (zoned_relative_to) {
|
if (zoned_relative_to) {
|
||||||
// FIXME: a. Let internalDuration be ToInternalDurationRecord(duration).
|
// a. Let internalDuration be ToInternalDurationRecord(duration).
|
||||||
// FIXME: b. Let timeZone be zonedRelativeTo.[[TimeZone]].
|
auto internal_duration = to_internal_duration_record(vm, duration);
|
||||||
// FIXME: c. Let calendar be zonedRelativeTo.[[Calendar]].
|
|
||||||
// FIXME: d. Let relativeEpochNs be zonedRelativeTo.[[EpochNanoseconds]].
|
// b. Let timeZone be zonedRelativeTo.[[TimeZone]].
|
||||||
// FIXME: e. Let targetEpochNs be ? AddZonedDateTime(relativeEpochNs, timeZone, calendar, internalDuration, constrain).
|
auto const time_zone = zoned_relative_to->time_zone();
|
||||||
// FIXME: f. Let total be ? DifferenceZonedDateTimeWithTotal(relativeEpochNs, targetEpochNs, timeZone, calendar, unit).
|
|
||||||
|
// c. Let calendar be zonedRelativeTo.[[Calendar]].
|
||||||
|
auto const& calendar = zoned_relative_to->calendar();
|
||||||
|
|
||||||
|
// d. Let relativeEpochNs be zonedRelativeTo.[[EpochNanoseconds]].
|
||||||
|
auto const& relative_epoch_nanoseconds = zoned_relative_to->epoch_nanoseconds()->big_integer();
|
||||||
|
|
||||||
|
// e. Let targetEpochNs be ? AddZonedDateTime(relativeEpochNs, timeZone, calendar, internalDuration, CONSTRAIN).
|
||||||
|
auto target_epoch_nanoseconds = TRY(add_zoned_date_time(vm, relative_epoch_nanoseconds, time_zone, calendar, internal_duration, Overflow::Constrain));
|
||||||
|
|
||||||
|
// f. Let total be ? DifferenceZonedDateTimeWithTotal(relativeEpochNs, targetEpochNs, timeZone, calendar, unit).
|
||||||
|
total = TRY(difference_zoned_date_time_with_total(vm, relative_epoch_nanoseconds, target_epoch_nanoseconds, time_zone, calendar, unit));
|
||||||
}
|
}
|
||||||
// 12. Else if plainRelativeTo is not undefined, then
|
// 12. Else if plainRelativeTo is not undefined, then
|
||||||
else if (plain_relative_to) {
|
else if (plain_relative_to) {
|
||||||
|
|
|
@ -9,9 +9,11 @@
|
||||||
#include <LibJS/Runtime/AbstractOperations.h>
|
#include <LibJS/Runtime/AbstractOperations.h>
|
||||||
#include <LibJS/Runtime/Date.h>
|
#include <LibJS/Runtime/Date.h>
|
||||||
#include <LibJS/Runtime/Temporal/Calendar.h>
|
#include <LibJS/Runtime/Temporal/Calendar.h>
|
||||||
|
#include <LibJS/Runtime/Temporal/Duration.h>
|
||||||
#include <LibJS/Runtime/Temporal/Instant.h>
|
#include <LibJS/Runtime/Temporal/Instant.h>
|
||||||
#include <LibJS/Runtime/Temporal/PlainDate.h>
|
#include <LibJS/Runtime/Temporal/PlainDate.h>
|
||||||
#include <LibJS/Runtime/Temporal/PlainDateTime.h>
|
#include <LibJS/Runtime/Temporal/PlainDateTime.h>
|
||||||
|
#include <LibJS/Runtime/Temporal/PlainTime.h>
|
||||||
#include <LibJS/Runtime/Temporal/TimeZone.h>
|
#include <LibJS/Runtime/Temporal/TimeZone.h>
|
||||||
#include <LibJS/Runtime/Temporal/ZonedDateTime.h>
|
#include <LibJS/Runtime/Temporal/ZonedDateTime.h>
|
||||||
#include <LibJS/Runtime/Temporal/ZonedDateTimeConstructor.h>
|
#include <LibJS/Runtime/Temporal/ZonedDateTimeConstructor.h>
|
||||||
|
@ -317,4 +319,152 @@ ThrowCompletionOr<GC::Ref<ZonedDateTime>> create_temporal_zoned_date_time(VM& vm
|
||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 6.5.5 AddZonedDateTime ( epochNanoseconds, timeZone, calendar, duration, overflow ), https://tc39.es/proposal-temporal/#sec-temporal-addzoneddatetime
|
||||||
|
ThrowCompletionOr<Crypto::SignedBigInteger> add_zoned_date_time(VM& vm, Crypto::SignedBigInteger const& epoch_nanoseconds, StringView time_zone, StringView calendar, InternalDuration const& duration, Overflow overflow)
|
||||||
|
{
|
||||||
|
// 1. If DateDurationSign(duration.[[Date]]) = 0, then
|
||||||
|
if (date_duration_sign(duration.date) == 0) {
|
||||||
|
// a. Return ? AddInstant(epochNanoseconds, duration.[[Time]]).
|
||||||
|
return TRY(add_instant(vm, epoch_nanoseconds, duration.time));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Let isoDateTime be GetISODateTimeFor(timeZone, epochNanoseconds).
|
||||||
|
auto iso_date_time = get_iso_date_time_for(time_zone, epoch_nanoseconds);
|
||||||
|
|
||||||
|
// 3. Let addedDate be ? CalendarDateAdd(calendar, isoDateTime.[[ISODate]], duration.[[Date]], overflow).
|
||||||
|
auto added_date = TRY(calendar_date_add(vm, calendar, iso_date_time.iso_date, duration.date, overflow));
|
||||||
|
|
||||||
|
// 4. Let intermediateDateTime be CombineISODateAndTimeRecord(addedDate, isoDateTime.[[Time]]).
|
||||||
|
auto intermediate_date_time = combine_iso_date_and_time_record(added_date, iso_date_time.time);
|
||||||
|
|
||||||
|
// 5. If ISODateTimeWithinLimits(intermediateDateTime) is false, throw a RangeError exception.
|
||||||
|
if (!iso_date_time_within_limits(intermediate_date_time))
|
||||||
|
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidISODateTime);
|
||||||
|
|
||||||
|
// 6. Let intermediateNs be ! GetEpochNanosecondsFor(timeZone, intermediateDateTime, COMPATIBLE).
|
||||||
|
auto intermediate_nanoseconds = MUST(get_epoch_nanoseconds_for(vm, time_zone, intermediate_date_time, Disambiguation::Compatible));
|
||||||
|
|
||||||
|
// 7. Return ? AddInstant(intermediateNs, duration.[[Time]]).
|
||||||
|
return TRY(add_instant(vm, intermediate_nanoseconds, duration.time));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6.5.6 DifferenceZonedDateTime ( ns1, ns2, timeZone, calendar, largestUnit ), https://tc39.es/proposal-temporal/#sec-temporal-differencezoneddatetime
|
||||||
|
ThrowCompletionOr<InternalDuration> difference_zoned_date_time(VM& vm, Crypto::SignedBigInteger const& nanoseconds1, Crypto::SignedBigInteger const& nanoseconds2, StringView time_zone, StringView calendar, Unit largest_unit)
|
||||||
|
{
|
||||||
|
// 1. If ns1 = ns2, return ! CombineDateAndTimeDuration(ZeroDateDuration(), 0).
|
||||||
|
if (nanoseconds1 == nanoseconds2)
|
||||||
|
return MUST(combine_date_and_time_duration(vm, zero_date_duration(vm), TimeDuration { 0 }));
|
||||||
|
|
||||||
|
// 2. Let startDateTime be GetISODateTimeFor(timeZone, ns1).
|
||||||
|
auto start_date_time = get_iso_date_time_for(time_zone, nanoseconds1);
|
||||||
|
|
||||||
|
// 3. Let endDateTime be GetISODateTimeFor(timeZone, ns2).
|
||||||
|
auto end_date_time = get_iso_date_time_for(time_zone, nanoseconds2);
|
||||||
|
|
||||||
|
// 4. If ns2 - ns1 < 0, let sign be -1; else let sign be 1.
|
||||||
|
double sign = nanoseconds2 < nanoseconds1 ? -1 : 1;
|
||||||
|
|
||||||
|
// 5. If sign = 1, let maxDayCorrection be 2; else let maxDayCorrection be 1.
|
||||||
|
double max_day_correction = sign == 1 ? 2 : 1;
|
||||||
|
|
||||||
|
// 6. Let dayCorrection be 0.
|
||||||
|
double day_correction = 0;
|
||||||
|
|
||||||
|
// 7. Let timeDuration be DifferenceTime(startDateTime.[[Time]], endDateTime.[[Time]]).
|
||||||
|
auto time_duration = difference_time(start_date_time.time, end_date_time.time);
|
||||||
|
|
||||||
|
// 8. If TimeDurationSign(timeDuration) = -sign, set dayCorrection to dayCorrection + 1.
|
||||||
|
if (time_duration_sign(time_duration) == -sign)
|
||||||
|
++day_correction;
|
||||||
|
|
||||||
|
// 9. Let success be false.
|
||||||
|
auto success = false;
|
||||||
|
|
||||||
|
ISODateTime intermediate_date_time;
|
||||||
|
|
||||||
|
// 10. Repeat, while dayCorrection ≤ maxDayCorrection and success is false,
|
||||||
|
while (day_correction <= max_day_correction && !success) {
|
||||||
|
// a. Let intermediateDate be BalanceISODate(endDateTime.[[ISODate]].[[Year]], endDateTime.[[ISODate]].[[Month]], endDateTime.[[ISODate]].[[Day]] - dayCorrection × sign).
|
||||||
|
auto intermediate_date = balance_iso_date(end_date_time.iso_date.year, end_date_time.iso_date.month, static_cast<double>(end_date_time.iso_date.day) - (day_correction * sign));
|
||||||
|
|
||||||
|
// b. Let intermediateDateTime be CombineISODateAndTimeRecord(intermediateDate, startDateTime.[[Time]]).
|
||||||
|
intermediate_date_time = combine_iso_date_and_time_record(intermediate_date, start_date_time.time);
|
||||||
|
|
||||||
|
// c. Let intermediateNs be ? GetEpochNanosecondsFor(timeZone, intermediateDateTime, COMPATIBLE).
|
||||||
|
auto intermediate_nanoseconds = TRY(get_epoch_nanoseconds_for(vm, time_zone, intermediate_date_time, Disambiguation::Compatible));
|
||||||
|
|
||||||
|
// d. Set timeDuration to TimeDurationFromEpochNanosecondsDifference(ns2, intermediateNs).
|
||||||
|
time_duration = time_duration_from_epoch_nanoseconds_difference(nanoseconds2, intermediate_nanoseconds);
|
||||||
|
|
||||||
|
// e. Let timeSign be TimeDurationSign(timeDuration).
|
||||||
|
auto time_sign = time_duration_sign(time_duration);
|
||||||
|
|
||||||
|
// f. If sign ≠ -timeSign, then
|
||||||
|
if (sign != -time_sign) {
|
||||||
|
// i. Set success to true.
|
||||||
|
success = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// g. Set dayCorrection to dayCorrection + 1.
|
||||||
|
++day_correction;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 11. Assert: success is true.
|
||||||
|
VERIFY(success);
|
||||||
|
|
||||||
|
// 12. Let dateLargestUnit be LargerOfTwoTemporalUnits(largestUnit, DAY).
|
||||||
|
auto date_largest_unit = larger_of_two_temporal_units(largest_unit, Unit::Day);
|
||||||
|
|
||||||
|
// 13. Let dateDifference be CalendarDateUntil(calendar, startDateTime.[[ISODate]], intermediateDateTime.[[ISODate]], dateLargestUnit).
|
||||||
|
auto date_difference = calendar_date_until(vm, calendar, start_date_time.iso_date, intermediate_date_time.iso_date, date_largest_unit);
|
||||||
|
|
||||||
|
// 14. Return ? CombineDateAndTimeDuration(dateDifference, timeDuration).
|
||||||
|
return TRY(combine_date_and_time_duration(vm, date_difference, move(time_duration)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6.5.7 DifferenceZonedDateTimeWithRounding ( ns1, ns2, timeZone, calendar, largestUnit, roundingIncrement, smallestUnit, roundingMode ), https://tc39.es/proposal-temporal/#sec-temporal-differencezoneddatetimewithrounding
|
||||||
|
ThrowCompletionOr<InternalDuration> difference_zoned_date_time_with_rounding(VM& vm, Crypto::SignedBigInteger const& nanoseconds1, Crypto::SignedBigInteger const& nanoseconds2, StringView time_zone, StringView calendar, Unit largest_unit, u64 rounding_increment, Unit smallest_unit, RoundingMode rounding_mode)
|
||||||
|
{
|
||||||
|
// 1. If TemporalUnitCategory(largestUnit) is TIME, then
|
||||||
|
if (temporal_unit_category(largest_unit) == UnitCategory::Time) {
|
||||||
|
// a. Return DifferenceInstant(ns1, ns2, roundingIncrement, smallestUnit, roundingMode).
|
||||||
|
return difference_instant(vm, nanoseconds1, nanoseconds2, rounding_increment, smallest_unit, rounding_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Let difference be ? DifferenceZonedDateTime(ns1, ns2, timeZone, calendar, largestUnit).
|
||||||
|
auto difference = TRY(difference_zoned_date_time(vm, nanoseconds1, nanoseconds2, time_zone, calendar, largest_unit));
|
||||||
|
|
||||||
|
// 3. If smallestUnit is NANOSECOND and roundingIncrement = 1, return difference.
|
||||||
|
if (smallest_unit == Unit::Nanosecond && rounding_increment == 1)
|
||||||
|
return difference;
|
||||||
|
|
||||||
|
// 4. Let dateTime be GetISODateTimeFor(timeZone, ns1).
|
||||||
|
auto date_time = get_iso_date_time_for(time_zone, nanoseconds1);
|
||||||
|
|
||||||
|
// 5. Return ? RoundRelativeDuration(difference, ns2, dateTime, timeZone, calendar, largestUnit, roundingIncrement, smallestUnit, roundingMode).
|
||||||
|
return TRY(round_relative_duration(vm, difference, nanoseconds2, date_time, time_zone, calendar, largest_unit, rounding_increment, smallest_unit, rounding_mode));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6.5.8 DifferenceZonedDateTimeWithTotal ( ns1, ns2, timeZone, calendar, unit ), https://tc39.es/proposal-temporal/#sec-temporal-differencezoneddatetimewithtotal
|
||||||
|
ThrowCompletionOr<Crypto::BigFraction> difference_zoned_date_time_with_total(VM& vm, Crypto::SignedBigInteger const& nanoseconds1, Crypto::SignedBigInteger const& nanoseconds2, StringView time_zone, StringView calendar, Unit unit)
|
||||||
|
{
|
||||||
|
// 1. If TemporalUnitCategory(unit) is TIME, then
|
||||||
|
if (temporal_unit_category(unit) == UnitCategory::Time) {
|
||||||
|
// a. Let difference be TimeDurationFromEpochNanosecondsDifference(ns2, ns1).
|
||||||
|
auto difference = time_duration_from_epoch_nanoseconds_difference(nanoseconds2, nanoseconds1);
|
||||||
|
|
||||||
|
// b. Return TotalTimeDuration(difference, unit).
|
||||||
|
return total_time_duration(difference, unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Let difference be ? DifferenceZonedDateTime(ns1, ns2, timeZone, calendar, unit).
|
||||||
|
auto difference = TRY(difference_zoned_date_time(vm, nanoseconds1, nanoseconds2, time_zone, calendar, unit));
|
||||||
|
|
||||||
|
// 3. Let dateTime be GetISODateTimeFor(timeZone, ns1).
|
||||||
|
auto date_time = get_iso_date_time_for(time_zone, nanoseconds1);
|
||||||
|
|
||||||
|
// 4. Return ? TotalRelativeDuration(difference, ns2, dateTime, timeZone, calendar, unit).
|
||||||
|
return TRY(total_relative_duration(vm, difference, nanoseconds2, date_time, time_zone, calendar, unit));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <LibCrypto/BigFraction/BigFraction.h>
|
||||||
#include <LibJS/Runtime/BigInt.h>
|
#include <LibJS/Runtime/BigInt.h>
|
||||||
#include <LibJS/Runtime/Completion.h>
|
#include <LibJS/Runtime/Completion.h>
|
||||||
#include <LibJS/Runtime/Object.h>
|
#include <LibJS/Runtime/Object.h>
|
||||||
|
@ -50,5 +51,9 @@ enum class MatchBehavior {
|
||||||
ThrowCompletionOr<Crypto::SignedBigInteger> interpret_iso_date_time_offset(VM&, ISODate, Variant<ParsedISODateTime::StartOfDay, Time> const&, OffsetBehavior, double offset_nanoseconds, StringView time_zone, Disambiguation, OffsetOption, MatchBehavior);
|
ThrowCompletionOr<Crypto::SignedBigInteger> interpret_iso_date_time_offset(VM&, ISODate, Variant<ParsedISODateTime::StartOfDay, Time> const&, OffsetBehavior, double offset_nanoseconds, StringView time_zone, Disambiguation, OffsetOption, MatchBehavior);
|
||||||
ThrowCompletionOr<GC::Ref<ZonedDateTime>> to_temporal_zoned_date_time(VM&, Value item, Value options = js_undefined());
|
ThrowCompletionOr<GC::Ref<ZonedDateTime>> to_temporal_zoned_date_time(VM&, Value item, Value options = js_undefined());
|
||||||
ThrowCompletionOr<GC::Ref<ZonedDateTime>> create_temporal_zoned_date_time(VM&, BigInt const& epoch_nanoseconds, String time_zone, String calendar, GC::Ptr<FunctionObject> new_target = {});
|
ThrowCompletionOr<GC::Ref<ZonedDateTime>> create_temporal_zoned_date_time(VM&, BigInt const& epoch_nanoseconds, String time_zone, String calendar, GC::Ptr<FunctionObject> new_target = {});
|
||||||
|
ThrowCompletionOr<Crypto::SignedBigInteger> add_zoned_date_time(VM&, Crypto::SignedBigInteger const& epoch_nanoseconds, StringView time_zone, StringView calendar, InternalDuration const&, Overflow);
|
||||||
|
ThrowCompletionOr<InternalDuration> difference_zoned_date_time(VM&, Crypto::SignedBigInteger const& nanoseconds1, Crypto::SignedBigInteger const& nanoseconds2, StringView time_zone, StringView calendar, Unit largest_unit);
|
||||||
|
ThrowCompletionOr<InternalDuration> difference_zoned_date_time_with_rounding(VM&, Crypto::SignedBigInteger const& nanoseconds1, Crypto::SignedBigInteger const& nanoseconds2, StringView time_zone, StringView calendar, Unit largest_unit, u64 rounding_increment, Unit smallest_unit, RoundingMode);
|
||||||
|
ThrowCompletionOr<Crypto::BigFraction> difference_zoned_date_time_with_total(VM&, Crypto::SignedBigInteger const& nanoseconds1, Crypto::SignedBigInteger const& nanoseconds2, StringView time_zone, StringView calendar, Unit);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,26 @@ describe("correct behavior", () => {
|
||||||
});
|
});
|
||||||
expect(result).toBe(-1);
|
expect(result).toBe(-1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("relative to zoned date time", () => {
|
||||||
|
const oneMonth = new Temporal.Duration(0, 1);
|
||||||
|
const thirtyDays = new Temporal.Duration(0, 0, 0, 30);
|
||||||
|
|
||||||
|
let result = Temporal.Duration.compare(oneMonth, thirtyDays, {
|
||||||
|
relativeTo: Temporal.ZonedDateTime.from("2018-04-01[UTC]"),
|
||||||
|
});
|
||||||
|
expect(result).toBe(0);
|
||||||
|
|
||||||
|
result = Temporal.Duration.compare(oneMonth, thirtyDays, {
|
||||||
|
relativeTo: Temporal.ZonedDateTime.from("2018-03-01[UTC]"),
|
||||||
|
});
|
||||||
|
expect(result).toBe(1);
|
||||||
|
|
||||||
|
result = Temporal.Duration.compare(oneMonth, thirtyDays, {
|
||||||
|
relativeTo: Temporal.ZonedDateTime.from("2018-02-01[UTC]"),
|
||||||
|
});
|
||||||
|
expect(result).toBe(-1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("errors", () => {
|
describe("errors", () => {
|
||||||
|
|
|
@ -98,6 +98,20 @@ describe("correct behavior", () => {
|
||||||
expect(result.months).toBe(1);
|
expect(result.months).toBe(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("relative to zoned date time", () => {
|
||||||
|
const duration = new Temporal.Duration(0, 0, 0, 31);
|
||||||
|
|
||||||
|
[
|
||||||
|
"2000-01-01[UTC]",
|
||||||
|
"2000-01-01T00:00[UTC]",
|
||||||
|
"2000-01-01T00:00+00:00[UTC]",
|
||||||
|
"2000-01-01T00:00+00:00[UTC][u-ca=iso8601]",
|
||||||
|
].forEach(relativeTo => {
|
||||||
|
const result = duration.round({ largestUnit: "months", relativeTo });
|
||||||
|
expect(result.months).toBe(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("errors", () => {
|
describe("errors", () => {
|
||||||
|
|
|
@ -25,6 +25,20 @@ describe("correct behavior", () => {
|
||||||
expect(result).toBe(1);
|
expect(result).toBe(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("relative to zoned date time", () => {
|
||||||
|
const duration = new Temporal.Duration(0, 0, 0, 31);
|
||||||
|
|
||||||
|
[
|
||||||
|
"2000-01-01[UTC]",
|
||||||
|
"2000-01-01T00:00[UTC]",
|
||||||
|
"2000-01-01T00:00+00:00[UTC]",
|
||||||
|
"2000-01-01T00:00+00:00[UTC][u-ca=iso8601]",
|
||||||
|
].forEach(relativeTo => {
|
||||||
|
const result = duration.total({ unit: "months", relativeTo });
|
||||||
|
expect(result).toBe(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("errors", () => {
|
describe("errors", () => {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue