diff --git a/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp b/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp index addbd5cb440..fe7850367fb 100644 --- a/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp +++ b/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp @@ -475,12 +475,16 @@ ThrowCompletionOr get_temporal_relative_to_option(VM& vm, Object con if (value.is_undefined()) return RelativeTo { .plain_relative_to = {}, .zoned_relative_to = {} }; - // FIXME: 3. Let offsetBehaviour be OPTION. - // FIXME: 4. Let matchBehaviour be MATCH-EXACTLY. + // 3. Let offsetBehaviour be OPTION. + auto offset_behavior = OffsetBehavior::Option; + + // 4. Let matchBehaviour be MATCH-EXACTLY. + auto match_behavior = MatchBehavior::MatchExactly; String calendar; Optional time_zone; Optional offset_string; + ISODate iso_date; Variant time { Time {} }; @@ -488,8 +492,11 @@ ThrowCompletionOr get_temporal_relative_to_option(VM& vm, Object con if (value.is_object()) { auto& object = value.as_object(); - // FIXME: a. If value has an [[InitializedTemporalZonedDateTime]] internal slot, then - // FIXME: i. Return the Record { [[PlainRelativeTo]]: undefined, [[ZonedRelativeTo]]: value }. + // a. If value has an [[InitializedTemporalZonedDateTime]] internal slot, then + if (is(object)) { + // i. Return the Record { [[PlainRelativeTo]]: undefined, [[ZonedRelativeTo]]: value }. + return RelativeTo { .plain_relative_to = {}, .zoned_relative_to = static_cast(object) }; + } // b. If value has an [[InitializedTemporalDate]] internal slot, then if (is(object)) { @@ -527,7 +534,8 @@ ThrowCompletionOr get_temporal_relative_to_option(VM& vm, Object con // i. If offsetString is UNSET, then 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]]. @@ -563,14 +571,17 @@ ThrowCompletionOr get_temporal_relative_to_option(VM& vm, Object con // ii. If result.[[TimeZone]].[[Z]] is true, then 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 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]]. @@ -596,15 +607,27 @@ ThrowCompletionOr get_temporal_relative_to_option(VM& vm, Object con return RelativeTo { .plain_relative_to = plain_date, .zoned_relative_to = {} }; } - // FIXME: 8. If offsetBehaviour is OPTION, then - // 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 }. + double offset_nanoseconds = 0; - 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 diff --git a/Libraries/LibJS/Runtime/Temporal/AbstractOperations.h b/Libraries/LibJS/Runtime/Temporal/AbstractOperations.h index 89afe895af4..5f9083f9656 100644 --- a/Libraries/LibJS/Runtime/Temporal/AbstractOperations.h +++ b/Libraries/LibJS/Runtime/Temporal/AbstractOperations.h @@ -143,8 +143,8 @@ struct SecondsStringPrecision { }; struct RelativeTo { - GC::Ptr plain_relative_to; // [[PlainRelativeTo]] - GC::Ptr zoned_relative_to; // FIXME: [[ZonedRelativeTo]] + GC::Ptr plain_relative_to; // [[PlainRelativeTo]] + GC::Ptr zoned_relative_to; // [[ZonedRelativeTo]] }; struct DifferenceSettings { diff --git a/Libraries/LibJS/Runtime/Temporal/DurationConstructor.cpp b/Libraries/LibJS/Runtime/Temporal/DurationConstructor.cpp index b36c25c42f9..17b071d04ce 100644 --- a/Libraries/LibJS/Runtime/Temporal/DurationConstructor.cpp +++ b/Libraries/LibJS/Runtime/Temporal/DurationConstructor.cpp @@ -9,6 +9,7 @@ #include #include #include +#include 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 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]]. - // FIXME: b. Let calendar be zonedRelativeTo.[[Calendar]]. - // FIXME: c. Let after1 be ? AddZonedDateTime(zonedRelativeTo.[[EpochNanoseconds]], timeZone, calendar, duration1, constrain). - // FIXME: d. Let after2 be ? AddZonedDateTime(zonedRelativeTo.[[EpochNanoseconds]], timeZone, calendar, duration2, constrain). - // FIXME: e. If after1 > after2, return 1𝔽. - // FIXME: f. If after1 < after2, return -1𝔽. + // a. Let timeZone be zonedRelativeTo.[[TimeZone]]. + auto const time_zone = zoned_relative_to->time_zone(); + + // b. Let calendar be zonedRelativeTo.[[Calendar]]. + auto const& calendar = zoned_relative_to->calendar(); + + // 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𝔽. return 0; diff --git a/Libraries/LibJS/Runtime/Temporal/DurationPrototype.cpp b/Libraries/LibJS/Runtime/Temporal/DurationPrototype.cpp index 5035de8ca05..59432040299 100644 --- a/Libraries/LibJS/Runtime/Temporal/DurationPrototype.cpp +++ b/Libraries/LibJS/Runtime/Temporal/DurationPrototype.cpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace JS::Temporal { @@ -347,11 +348,20 @@ JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::round) // a. Let internalDuration be ToInternalDurationRecord(duration). auto internal_duration = to_internal_duration_record(vm, duration); - // FIXME: b. Let timeZone be zonedRelativeTo.[[TimeZone]]. - // FIXME: c. Let calendar be zonedRelativeTo.[[Calendar]]. - // FIXME: d. Let relativeEpochNs be zonedRelativeTo.[[EpochNanoseconds]]. - // FIXME: e. Let targetEpochNs be ? AddZonedDateTime(relativeEpochNs, timeZone, calendar, internalDuration, constrain). - // FIXME: f. Set internalDuration to ? DifferenceZonedDateTimeWithRounding(relativeEpochNs, targetEpochNs, timeZone, calendar, largestUnit, roundingIncrement, smallestUnit, roundingMode). + // b. Let timeZone be zonedRelativeTo.[[TimeZone]]. + auto const& time_zone = zoned_relative_to->time_zone(); + + // 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. 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. 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 if (zoned_relative_to) { - // FIXME: a. Let internalDuration be ToInternalDurationRecord(duration). - // FIXME: b. Let timeZone be zonedRelativeTo.[[TimeZone]]. - // FIXME: c. Let calendar be zonedRelativeTo.[[Calendar]]. - // FIXME: d. Let relativeEpochNs be zonedRelativeTo.[[EpochNanoseconds]]. - // FIXME: e. Let targetEpochNs be ? AddZonedDateTime(relativeEpochNs, timeZone, calendar, internalDuration, constrain). - // FIXME: f. Let total be ? DifferenceZonedDateTimeWithTotal(relativeEpochNs, targetEpochNs, timeZone, calendar, unit). + // a. Let internalDuration be ToInternalDurationRecord(duration). + auto internal_duration = to_internal_duration_record(vm, duration); + + // b. Let timeZone be zonedRelativeTo.[[TimeZone]]. + auto const time_zone = zoned_relative_to->time_zone(); + + // 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 else if (plain_relative_to) { diff --git a/Libraries/LibJS/Runtime/Temporal/ZonedDateTime.cpp b/Libraries/LibJS/Runtime/Temporal/ZonedDateTime.cpp index 7fab84a3f79..5dd7bde51ca 100644 --- a/Libraries/LibJS/Runtime/Temporal/ZonedDateTime.cpp +++ b/Libraries/LibJS/Runtime/Temporal/ZonedDateTime.cpp @@ -9,9 +9,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -317,4 +319,152 @@ ThrowCompletionOr> create_temporal_zoned_date_time(VM& vm return object; } +// 6.5.5 AddZonedDateTime ( epochNanoseconds, timeZone, calendar, duration, overflow ), https://tc39.es/proposal-temporal/#sec-temporal-addzoneddatetime +ThrowCompletionOr 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(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 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(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 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 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)); +} + } diff --git a/Libraries/LibJS/Runtime/Temporal/ZonedDateTime.h b/Libraries/LibJS/Runtime/Temporal/ZonedDateTime.h index bc63c65081d..bd50ea1234f 100644 --- a/Libraries/LibJS/Runtime/Temporal/ZonedDateTime.h +++ b/Libraries/LibJS/Runtime/Temporal/ZonedDateTime.h @@ -7,6 +7,7 @@ #pragma once +#include #include #include #include @@ -50,5 +51,9 @@ enum class MatchBehavior { ThrowCompletionOr interpret_iso_date_time_offset(VM&, ISODate, Variant const&, OffsetBehavior, double offset_nanoseconds, StringView time_zone, Disambiguation, OffsetOption, MatchBehavior); ThrowCompletionOr> to_temporal_zoned_date_time(VM&, Value item, Value options = js_undefined()); ThrowCompletionOr> create_temporal_zoned_date_time(VM&, BigInt const& epoch_nanoseconds, String time_zone, String calendar, GC::Ptr new_target = {}); +ThrowCompletionOr add_zoned_date_time(VM&, Crypto::SignedBigInteger const& epoch_nanoseconds, StringView time_zone, StringView calendar, InternalDuration const&, Overflow); +ThrowCompletionOr difference_zoned_date_time(VM&, Crypto::SignedBigInteger const& nanoseconds1, Crypto::SignedBigInteger const& nanoseconds2, StringView time_zone, StringView calendar, Unit largest_unit); +ThrowCompletionOr 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 difference_zoned_date_time_with_total(VM&, Crypto::SignedBigInteger const& nanoseconds1, Crypto::SignedBigInteger const& nanoseconds2, StringView time_zone, StringView calendar, Unit); } diff --git a/Libraries/LibJS/Tests/builtins/Temporal/Duration/Duration.compare.js b/Libraries/LibJS/Tests/builtins/Temporal/Duration/Duration.compare.js index 3eefcd66808..ab0568d5f9a 100644 --- a/Libraries/LibJS/Tests/builtins/Temporal/Duration/Duration.compare.js +++ b/Libraries/LibJS/Tests/builtins/Temporal/Duration/Duration.compare.js @@ -47,6 +47,26 @@ describe("correct behavior", () => { }); 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", () => { diff --git a/Libraries/LibJS/Tests/builtins/Temporal/Duration/Duration.prototype.round.js b/Libraries/LibJS/Tests/builtins/Temporal/Duration/Duration.prototype.round.js index 731cd68d072..b7523bafcf0 100644 --- a/Libraries/LibJS/Tests/builtins/Temporal/Duration/Duration.prototype.round.js +++ b/Libraries/LibJS/Tests/builtins/Temporal/Duration/Duration.prototype.round.js @@ -98,6 +98,20 @@ describe("correct behavior", () => { 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", () => { diff --git a/Libraries/LibJS/Tests/builtins/Temporal/Duration/Duration.prototype.total.js b/Libraries/LibJS/Tests/builtins/Temporal/Duration/Duration.prototype.total.js index 1c31188e142..42a4e7cebb5 100644 --- a/Libraries/LibJS/Tests/builtins/Temporal/Duration/Duration.prototype.total.js +++ b/Libraries/LibJS/Tests/builtins/Temporal/Duration/Duration.prototype.total.js @@ -25,6 +25,20 @@ describe("correct behavior", () => { 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", () => {