2021-08-01 17:20:11 +01:00
/*
2023-01-26 12:17:54 +00:00
* Copyright ( c ) 2021 - 2023 , Linus Groh < linusg @ serenityos . org >
2023-02-11 00:39:05 +00:00
* Copyright ( c ) 2021 - 2023 , Luke Wilde < lukew @ serenityos . org >
2021-08-01 17:20:11 +01:00
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
2022-11-23 13:41:50 +01:00
# include <AK/TypeCasts.h>
2021-08-01 17:20:11 +01:00
# include <LibJS/Runtime/AbstractOperations.h>
2022-05-06 19:28:56 +02:00
# include <LibJS/Runtime/Date.h>
2021-08-01 17:20:11 +01:00
# include <LibJS/Runtime/GlobalObject.h>
2021-11-02 00:14:47 +01:00
# include <LibJS/Runtime/Temporal/Calendar.h>
# include <LibJS/Runtime/Temporal/Duration.h>
2021-08-01 17:20:11 +01:00
# include <LibJS/Runtime/Temporal/Instant.h>
2021-11-02 00:14:47 +01:00
# include <LibJS/Runtime/Temporal/PlainDate.h>
# include <LibJS/Runtime/Temporal/PlainDateTime.h>
# include <LibJS/Runtime/Temporal/TimeZone.h>
2021-08-01 17:20:11 +01:00
# include <LibJS/Runtime/Temporal/ZonedDateTime.h>
# include <LibJS/Runtime/Temporal/ZonedDateTimeConstructor.h>
namespace JS : : Temporal {
// 6 Temporal.ZonedDateTime Objects, https://tc39.es/proposal-temporal/#sec-temporal-zoneddatetime-objects
2021-09-09 21:18:08 +01:00
ZonedDateTime : : ZonedDateTime ( BigInt const & nanoseconds , Object & time_zone , Object & calendar , Object & prototype )
2022-12-14 12:17:58 +01:00
: Object ( ConstructWithPrototypeTag : : Tag , prototype )
2021-08-01 17:20:11 +01:00
, m_nanoseconds ( nanoseconds )
, m_time_zone ( time_zone )
, m_calendar ( calendar )
{
}
void ZonedDateTime : : visit_edges ( Cell : : Visitor & visitor )
{
Base : : visit_edges ( visitor ) ;
2023-02-26 16:09:02 -07:00
visitor . visit ( m_nanoseconds ) ;
visitor . visit ( m_time_zone ) ;
visitor . visit ( m_calendar ) ;
2021-08-01 17:20:11 +01:00
}
2021-11-07 00:51:19 +00:00
// 6.5.1 InterpretISODateTimeOffset ( year, month, day, hour, minute, second, millisecond, microsecond, nanosecond, offsetBehaviour, offsetNanoseconds, timeZone, disambiguation, offsetOption, matchBehaviour ), https://tc39.es/proposal-temporal/#sec-temporal-interpretisodatetimeoffset
2022-08-20 08:52:42 +01:00
ThrowCompletionOr < BigInt const * > interpret_iso_date_time_offset ( VM & vm , i32 year , u8 month , u8 day , u8 hour , u8 minute , u8 second , u16 millisecond , u16 microsecond , u16 nanosecond , OffsetBehavior offset_behavior , double offset_nanoseconds , Value time_zone , StringView disambiguation , StringView offset_option , MatchBehavior match_behavior )
2021-11-07 00:51:19 +00:00
{
2022-04-11 23:10:22 +01:00
// 1. Let calendar be ! GetISO8601Calendar().
2022-08-20 08:52:42 +01:00
auto * calendar = get_iso8601_calendar ( vm ) ;
2021-11-07 00:51:19 +00:00
2022-04-11 23:10:22 +01:00
// 2. Let dateTime be ? CreateTemporalDateTime(year, month, day, hour, minute, second, millisecond, microsecond, nanosecond, calendar).
2022-08-20 08:52:42 +01:00
auto * date_time = TRY ( create_temporal_date_time ( vm , year , month , day , hour , minute , second , millisecond , microsecond , nanosecond , * calendar ) ) ;
2021-11-07 00:51:19 +00:00
2022-06-14 23:32:13 +01:00
// 3. If offsetBehaviour is wall or offsetOption is "ignore", then
2021-11-07 00:51:19 +00:00
if ( offset_behavior = = OffsetBehavior : : Wall | | offset_option = = " ignore " sv ) {
// a. Let instant be ? BuiltinTimeZoneGetInstantFor(timeZone, dateTime, disambiguation).
2022-08-20 08:52:42 +01:00
auto * instant = TRY ( builtin_time_zone_get_instant_for ( vm , time_zone , * date_time , disambiguation ) ) ;
2021-11-07 00:51:19 +00:00
// b. Return instant.[[Nanoseconds]].
return & instant - > nanoseconds ( ) ;
}
2022-06-14 23:32:13 +01:00
// 4. If offsetBehaviour is exact or offsetOption is "use", then
2021-11-07 00:51:19 +00:00
if ( offset_behavior = = OffsetBehavior : : Exact | | offset_option = = " use " sv ) {
2022-10-14 09:47:17 -04:00
// a. Let epochNanoseconds be GetUTCEpochNanoseconds(year, month, day, hour, minute, second, millisecond, microsecond, nanosecond).
auto epoch_nanoseconds = get_utc_epoch_nanoseconds ( year , month , day , hour , minute , second , millisecond , microsecond , nanosecond ) ;
2021-11-07 00:51:19 +00:00
2022-06-23 19:49:11 +01:00
// b. Set epochNanoseconds to epochNanoseconds - ℤ (offsetNanoseconds).
2022-10-14 09:47:17 -04:00
epoch_nanoseconds = epoch_nanoseconds . minus ( Crypto : : SignedBigInteger { offset_nanoseconds } ) ;
2022-06-23 19:49:11 +01:00
// c. If ! IsValidEpochNanoseconds(epochNanoseconds) is false, throw a RangeError exception.
2022-10-14 09:47:17 -04:00
if ( ! is_valid_epoch_nanoseconds ( epoch_nanoseconds ) )
2022-08-16 20:33:17 +01:00
return vm . throw_completion < RangeError > ( ErrorType : : TemporalInvalidEpochNanoseconds ) ;
2022-06-23 19:49:11 +01:00
// d. Return epochNanoseconds.
2022-12-06 22:03:52 +00:00
return BigInt : : create ( vm , move ( epoch_nanoseconds ) ) . ptr ( ) ;
2021-11-07 00:51:19 +00:00
}
2022-04-11 23:10:22 +01:00
// 5. Assert: offsetBehaviour is option.
2021-11-07 00:51:19 +00:00
VERIFY ( offset_behavior = = OffsetBehavior : : Option ) ;
2022-04-11 23:10:22 +01:00
// 6. Assert: offsetOption is "prefer" or "reject".
2021-11-07 00:51:19 +00:00
VERIFY ( offset_option . is_one_of ( " prefer " sv , " reject " sv ) ) ;
2022-04-11 23:10:22 +01:00
// 7. Let possibleInstants be ? GetPossibleInstantsFor(timeZone, dateTime).
2022-08-20 08:52:42 +01:00
auto possible_instants = TRY ( get_possible_instants_for ( vm , time_zone , * date_time ) ) ;
2021-11-07 00:51:19 +00:00
2022-04-11 23:10:22 +01:00
// 8. For each element candidate of possibleInstants, do
2022-02-09 09:41:08 +00:00
for ( auto * candidate : possible_instants ) {
2021-11-07 00:51:19 +00:00
// a. Let candidateNanoseconds be ? GetOffsetNanosecondsFor(timeZone, candidate).
2022-08-20 08:52:42 +01:00
auto candidate_nanoseconds = TRY ( get_offset_nanoseconds_for ( vm , time_zone , * candidate ) ) ;
2021-11-07 00:51:19 +00:00
// b. If candidateNanoseconds = offsetNanoseconds, then
if ( candidate_nanoseconds = = offset_nanoseconds ) {
// i. Return candidate.[[Nanoseconds]].
2022-02-09 09:41:08 +00:00
return & candidate - > nanoseconds ( ) ;
2021-11-07 00:51:19 +00:00
}
// c. If matchBehaviour is match minutes, then
2021-11-12 23:37:20 +00:00
if ( match_behavior = = MatchBehavior : : MatchMinutes ) {
2022-05-24 18:26:13 +01:00
// i. Let roundedCandidateNanoseconds be RoundNumberToIncrement(candidateNanoseconds, 60 × 10^9, "halfExpand").
2021-11-07 00:51:19 +00:00
auto rounded_candidate_nanoseconds = round_number_to_increment ( candidate_nanoseconds , 60000000000 , " halfExpand " sv ) ;
// ii. If roundedCandidateNanoseconds = offsetNanoseconds, then
if ( rounded_candidate_nanoseconds = = offset_nanoseconds ) {
// 1. Return candidate.[[Nanoseconds]].
2022-02-09 09:41:08 +00:00
return & candidate - > nanoseconds ( ) ;
2021-11-07 00:51:19 +00:00
}
}
}
2022-04-11 23:10:22 +01:00
// 9. If offsetOption is "reject", throw a RangeError exception.
2021-11-07 00:51:19 +00:00
if ( offset_option = = " reject " sv )
2022-08-16 20:33:17 +01:00
return vm . throw_completion < RangeError > ( ErrorType : : TemporalInvalidZonedDateTimeOffset ) ;
2021-11-07 00:51:19 +00:00
2022-04-11 23:10:22 +01:00
// 10. Let instant be ? DisambiguatePossibleInstants(possibleInstants, timeZone, dateTime, disambiguation).
2022-08-20 08:52:42 +01:00
auto * instant = TRY ( disambiguate_possible_instants ( vm , possible_instants , time_zone , * date_time , disambiguation ) ) ;
2021-11-07 00:51:19 +00:00
2022-04-11 23:10:22 +01:00
// 11. Return instant.[[Nanoseconds]].
2021-11-07 00:51:19 +00:00
return & instant - > nanoseconds ( ) ;
}
// 6.5.2 ToTemporalZonedDateTime ( item [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal-totemporalzoneddatetime
2022-08-20 08:52:42 +01:00
ThrowCompletionOr < ZonedDateTime * > to_temporal_zoned_date_time ( VM & vm , Value item , Object const * options )
2021-11-07 00:51:19 +00:00
{
2022-04-06 23:56:57 +01:00
// 1. If options is not present, set options to undefined.
// 2. Assert: Type(options) is Object or Undefined.
2021-11-07 00:51:19 +00:00
2022-04-06 23:56:57 +01:00
// 3. Let offsetBehaviour be option.
2021-11-07 00:51:19 +00:00
auto offset_behavior = OffsetBehavior : : Option ;
2022-04-06 23:56:57 +01:00
// 4. Let matchBehaviour be match exactly.
2021-11-12 23:37:20 +00:00
auto match_behavior = MatchBehavior : : MatchExactly ;
2021-11-07 00:51:19 +00:00
Object * calendar = nullptr ;
Object * time_zone = nullptr ;
2023-01-26 15:00:53 +00:00
Optional < String > offset_string ;
2021-11-07 00:51:19 +00:00
ISODateTime result ;
2022-04-06 23:56:57 +01:00
// 5. If Type(item) is Object, then
2021-11-07 00:51:19 +00:00
if ( item . is_object ( ) ) {
auto & item_object = item . as_object ( ) ;
// a. If item has an [[InitializedTemporalZonedDateTime]] internal slot, then
if ( is < ZonedDateTime > ( item_object ) ) {
// i. Return item.
return & static_cast < ZonedDateTime & > ( item_object ) ;
}
// b. Let calendar be ? GetTemporalCalendarWithISODefault(item).
2022-08-20 08:52:42 +01:00
calendar = TRY ( get_temporal_calendar_with_iso_default ( vm , item_object ) ) ;
2021-11-07 00:51:19 +00:00
// c. Let fieldNames be ? CalendarFields(calendar, « "day", "hour", "microsecond", "millisecond", "minute", "month", "monthCode", "nanosecond", "second", "year" »).
2022-08-20 08:52:42 +01:00
auto field_names = TRY ( calendar_fields ( vm , * calendar , { " day " sv , " hour " sv , " microsecond " sv , " millisecond " sv , " minute " sv , " month " sv , " monthCode " sv , " nanosecond " sv , " second " sv , " year " sv } ) ) ;
2021-11-07 00:51:19 +00:00
// d. Append "timeZone" to fieldNames.
2023-02-25 16:40:37 +01:00
field_names . append ( TRY_OR_THROW_OOM ( vm , " timeZone " _string ) ) ;
2021-11-07 00:51:19 +00:00
// e. Append "offset" to fieldNames.
2023-02-25 16:40:37 +01:00
field_names . append ( TRY_OR_THROW_OOM ( vm , " offset " _string ) ) ;
2021-11-07 00:51:19 +00:00
// f. Let fields be ? PrepareTemporalFields(item, fieldNames, « "timeZone" »).
2022-08-20 08:52:42 +01:00
auto * fields = TRY ( prepare_temporal_fields ( vm , item_object , field_names , Vector < StringView > { " timeZone " sv } ) ) ;
2021-11-07 00:51:19 +00:00
2022-05-17 21:24:50 +01:00
// g. Let timeZone be ! Get(fields, "timeZone").
auto time_zone_value = MUST ( fields - > get ( vm . names . timeZone ) ) ;
2021-11-07 00:51:19 +00:00
// h. Set timeZone to ? ToTemporalTimeZone(timeZone).
2022-08-20 08:52:42 +01:00
time_zone = TRY ( to_temporal_time_zone ( vm , time_zone_value ) ) ;
2021-11-07 00:51:19 +00:00
2022-05-17 21:24:50 +01:00
// i. Let offsetString be ! Get(fields, "offset").
auto offset_string_value = MUST ( fields - > get ( vm . names . offset ) ) ;
2021-11-07 00:51:19 +00:00
2023-01-14 04:30:51 +01:00
// j. Assert: offsetString is a String or undefined.
VERIFY ( offset_string_value . is_string ( ) | | offset_string_value . is_undefined ( ) ) ;
// k. If offsetString is undefined, then
2021-11-07 00:51:19 +00:00
if ( offset_string_value . is_undefined ( ) ) {
// i. Set offsetBehaviour to wall.
offset_behavior = OffsetBehavior : : Wall ;
2023-02-11 00:39:05 +00:00
} else {
// NOTE: Not in the spec, since it directly assigns to offsetString in step i, but we can't do it there as it's a type mismatch.
offset_string = TRY ( offset_string_value . as_string ( ) . utf8_string ( ) ) ;
2021-11-07 00:51:19 +00:00
}
// l. Let result be ? InterpretTemporalDateTimeFields(calendar, fields, options).
2022-08-20 08:52:42 +01:00
result = TRY ( interpret_temporal_date_time_fields ( vm , * calendar , * fields , * options ) ) ;
2021-11-07 00:51:19 +00:00
}
2022-04-06 23:56:57 +01:00
// 6. Else,
2021-11-07 00:51:19 +00:00
else {
// a. Perform ? ToTemporalOverflow(options).
2022-08-20 08:52:42 +01:00
( void ) TRY ( to_temporal_overflow ( vm , options ) ) ;
2021-11-07 00:51:19 +00:00
// b. Let string be ? ToString(item).
2023-01-26 14:22:47 +00:00
auto string = TRY ( item . to_string ( vm ) ) ;
2021-11-07 00:51:19 +00:00
// c. Let result be ? ParseTemporalZonedDateTimeString(string).
2022-10-15 03:32:18 +05:30
result = TRY ( parse_temporal_zoned_date_time_string ( vm , string ) ) ;
2021-11-07 00:51:19 +00:00
2022-10-15 03:32:18 +05:30
// d. Let timeZoneName be result.[[TimeZone]].[[Name]].
auto time_zone_name = result . time_zone . name ;
2021-11-07 00:51:19 +00:00
2022-01-13 00:34:12 +01:00
// e. Assert: timeZoneName is not undefined.
VERIFY ( time_zone_name . has_value ( ) ) ;
2022-10-14 11:42:12 -04:00
// f. If IsTimeZoneOffsetString(timeZoneName) is false, then
if ( ! is_time_zone_offset_string ( * time_zone_name ) ) {
2022-10-17 09:11:32 +02:00
// i. If IsAvailableTimeZoneName(timeZoneName) is false, throw a RangeError exception.
if ( ! is_available_time_zone_name ( * time_zone_name ) )
2022-08-16 20:33:17 +01:00
return vm . throw_completion < RangeError > ( ErrorType : : TemporalInvalidTimeZoneName , * time_zone_name ) ;
2022-01-13 00:34:12 +01:00
// ii. Set timeZoneName to ! CanonicalizeTimeZoneName(timeZoneName).
2023-01-26 15:09:59 +00:00
time_zone_name = MUST_OR_THROW_OOM ( canonicalize_time_zone_name ( vm , * time_zone_name ) ) ;
2022-01-13 00:34:12 +01:00
}
2022-10-15 03:32:18 +05:30
// g. Let offsetString be result.[[TimeZone]].[[OffsetString]].
offset_string = move ( result . time_zone . offset_string ) ;
2021-11-07 00:51:19 +00:00
2022-10-15 03:32:18 +05:30
// h. If result.[[TimeZone]].[[Z]] is true, then
if ( result . time_zone . z ) {
2021-11-07 00:51:19 +00:00
// i. Set offsetBehaviour to exact.
offset_behavior = OffsetBehavior : : Exact ;
}
2022-01-13 00:34:12 +01:00
// i. Else if offsetString is undefined, then
2021-11-07 00:51:19 +00:00
else if ( ! offset_string . has_value ( ) ) {
// i. Set offsetBehaviour to wall.
offset_behavior = OffsetBehavior : : Wall ;
}
2022-01-13 00:34:12 +01:00
// j. Let timeZone be ! CreateTemporalTimeZone(timeZoneName).
2023-01-26 16:06:23 +00:00
time_zone = MUST_OR_THROW_OOM ( create_temporal_time_zone ( vm , * time_zone_name ) ) ;
2021-11-07 00:51:19 +00:00
2022-01-13 00:34:12 +01:00
// k. Let calendar be ? ToTemporalCalendarWithISODefault(result.[[Calendar]]).
2022-10-15 03:32:18 +05:30
auto temporal_calendar_like = result . calendar . has_value ( )
2022-12-06 22:17:27 +00:00
? PrimitiveString : : create ( vm , result . calendar . value ( ) )
2021-11-20 22:05:39 +00:00
: js_undefined ( ) ;
2022-08-20 08:52:42 +01:00
calendar = TRY ( to_temporal_calendar_with_iso_default ( vm , temporal_calendar_like ) ) ;
2021-11-07 00:51:19 +00:00
2022-01-13 00:34:12 +01:00
// l. Set matchBehaviour to match minutes.
2021-11-12 23:37:20 +00:00
match_behavior = MatchBehavior : : MatchMinutes ;
2021-11-07 00:51:19 +00:00
}
2022-04-06 23:56:57 +01:00
// 7. Let offsetNanoseconds be 0.
2021-11-07 00:51:19 +00:00
double offset_nanoseconds = 0 ;
2022-04-06 23:56:57 +01:00
// 8. If offsetBehaviour is option, then
2021-11-07 00:51:19 +00:00
if ( offset_behavior = = OffsetBehavior : : Option ) {
2022-10-14 11:42:12 -04:00
// a. If IsTimeZoneOffsetString(offsetString) is false, throw a RangeError exception.
if ( ! is_time_zone_offset_string ( * offset_string ) )
return vm . throw_completion < RangeError > ( ErrorType : : TemporalInvalidTimeZoneName , * offset_string ) ;
2021-11-07 00:51:19 +00:00
// a. Set offsetNanoseconds to ? ParseTimeZoneOffsetString(offsetString).
2022-10-14 11:42:12 -04:00
offset_nanoseconds = parse_time_zone_offset_string ( * offset_string ) ;
2021-11-07 00:51:19 +00:00
}
2022-04-06 23:56:57 +01:00
// 9. Let disambiguation be ? ToTemporalDisambiguation(options).
2022-08-20 08:52:42 +01:00
auto disambiguation = TRY ( to_temporal_disambiguation ( vm , options ) ) ;
2021-11-07 00:51:19 +00:00
2022-04-06 23:56:57 +01:00
// 10. Let offsetOption be ? ToTemporalOffset(options, "reject").
2023-01-26 12:17:54 +00:00
auto offset_option = TRY ( to_temporal_offset ( vm , options , " reject " sv ) ) ;
2021-11-07 00:51:19 +00:00
2022-04-06 23:56:57 +01:00
// 11. Let epochNanoseconds be ? InterpretISODateTimeOffset(result.[[Year]], result.[[Month]], result.[[Day]], result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]], offsetBehaviour, offsetNanoseconds, timeZone, disambiguation, offsetOption, matchBehaviour).
2022-08-20 08:52:42 +01:00
auto * epoch_nanoseconds = TRY ( interpret_iso_date_time_offset ( vm , result . year , result . month , result . day , result . hour , result . minute , result . second , result . millisecond , result . microsecond , result . nanosecond , offset_behavior , offset_nanoseconds , time_zone , disambiguation , offset_option , match_behavior ) ) ;
2021-11-07 00:51:19 +00:00
2022-04-06 23:56:57 +01:00
// 12. Return ! CreateTemporalZonedDateTime(epochNanoseconds, timeZone, calendar).
2022-08-20 08:52:42 +01:00
return MUST ( create_temporal_zoned_date_time ( vm , * epoch_nanoseconds , * time_zone , * calendar ) ) ;
2021-11-07 00:51:19 +00:00
}
2021-08-01 17:20:11 +01:00
// 6.5.3 CreateTemporalZonedDateTime ( epochNanoseconds, timeZone, calendar [ , newTarget ] ), https://tc39.es/proposal-temporal/#sec-temporal-createtemporalzoneddatetime
2022-08-20 08:52:42 +01:00
ThrowCompletionOr < ZonedDateTime * > create_temporal_zoned_date_time ( VM & vm , BigInt const & epoch_nanoseconds , Object & time_zone , Object & calendar , FunctionObject const * new_target )
2021-08-01 17:20:11 +01:00
{
2022-08-20 08:52:42 +01:00
auto & realm = * vm . current_realm ( ) ;
2022-04-11 23:10:22 +01:00
// 1. Assert: ! IsValidEpochNanoseconds(epochNanoseconds) is true.
2021-08-01 17:20:11 +01:00
VERIFY ( is_valid_epoch_nanoseconds ( epoch_nanoseconds ) ) ;
2022-04-11 23:10:22 +01:00
// 2. If newTarget is not present, set newTarget to %Temporal.ZonedDateTime%.
2021-08-01 17:20:11 +01:00
if ( ! new_target )
2022-08-27 00:54:55 +01:00
new_target = realm . intrinsics ( ) . temporal_zoned_date_time_constructor ( ) ;
2021-08-01 17:20:11 +01:00
2022-04-11 23:10:22 +01:00
// 3. Let object be ? OrdinaryCreateFromConstructor(newTarget, "%Temporal.ZonedDateTime.prototype%", « [[InitializedTemporalZonedDateTime]], [[Nanoseconds]], [[TimeZone]], [[Calendar]] »).
// 4. Set object.[[Nanoseconds]] to epochNanoseconds.
// 5. Set object.[[TimeZone]] to timeZone.
// 6. Set object.[[Calendar]] to calendar.
2022-12-14 18:34:32 +00:00
auto object = TRY ( ordinary_create_from_constructor < ZonedDateTime > ( vm , * new_target , & Intrinsics : : temporal_time_zone_prototype , epoch_nanoseconds , time_zone , calendar ) ) ;
2021-08-01 17:20:11 +01:00
2022-04-11 23:10:22 +01:00
// 7. Return object.
2022-12-14 18:34:32 +00:00
return object . ptr ( ) ;
2021-08-01 17:20:11 +01:00
}
2021-11-10 00:14:16 +00:00
// 6.5.4 TemporalZonedDateTimeToString ( zonedDateTime, precision, showCalendar, showTimeZone, showOffset [ , increment, unit, roundingMode ] ), https://tc39.es/proposal-temporal/#sec-temporal-temporalzoneddatetimetostring
2023-01-26 16:17:24 +00:00
ThrowCompletionOr < String > temporal_zoned_date_time_to_string ( VM & vm , ZonedDateTime & zoned_date_time , Variant < StringView , u8 > const & precision , StringView show_calendar , StringView show_time_zone , StringView show_offset , Optional < u64 > increment , Optional < StringView > unit , Optional < StringView > rounding_mode )
2021-11-10 00:14:16 +00:00
{
2022-04-11 23:10:22 +01:00
// 1. If increment is not present, set increment to 1.
2021-11-10 00:14:16 +00:00
if ( ! increment . has_value ( ) )
increment = 1 ;
2022-04-11 23:10:22 +01:00
// 2. If unit is not present, set unit to "nanosecond".
2021-11-10 00:14:16 +00:00
if ( ! unit . has_value ( ) )
unit = " nanosecond " sv ;
2022-04-11 23:10:22 +01:00
// 3. If roundingMode is not present, set roundingMode to "trunc".
2021-11-10 00:14:16 +00:00
if ( ! rounding_mode . has_value ( ) )
rounding_mode = " trunc " sv ;
2022-04-11 23:10:22 +01:00
// 4. Let ns be ! RoundTemporalInstant(zonedDateTime.[[Nanoseconds]], increment, unit, roundingMode).
2022-08-20 08:52:42 +01:00
auto * ns = round_temporal_instant ( vm , zoned_date_time . nanoseconds ( ) , * increment , * unit , * rounding_mode ) ;
2021-11-10 00:14:16 +00:00
2022-04-11 23:10:22 +01:00
// 5. Let timeZone be zonedDateTime.[[TimeZone]].
2021-11-10 00:14:16 +00:00
auto & time_zone = zoned_date_time . time_zone ( ) ;
2022-04-11 23:10:22 +01:00
// 6. Let instant be ! CreateTemporalInstant(ns).
2022-08-20 08:52:42 +01:00
auto * instant = MUST ( create_temporal_instant ( vm , * ns ) ) ;
2021-11-10 00:14:16 +00:00
2022-04-11 23:10:22 +01:00
// 7. Let isoCalendar be ! GetISO8601Calendar().
2022-08-20 08:52:42 +01:00
auto * iso_calendar = get_iso8601_calendar ( vm ) ;
2021-11-10 00:14:16 +00:00
2022-04-11 23:10:22 +01:00
// 8. Let temporalDateTime be ? BuiltinTimeZoneGetPlainDateTimeFor(timeZone, instant, isoCalendar).
2022-08-20 08:52:42 +01:00
auto * temporal_date_time = TRY ( builtin_time_zone_get_plain_date_time_for ( vm , & time_zone , * instant , * iso_calendar ) ) ;
2021-11-10 00:14:16 +00:00
2023-01-14 02:52:09 +01:00
// 9. Let dateTimeString be ! TemporalDateTimeToString(temporalDateTime.[[ISOYear]], temporalDateTime.[[ISOMonth]], temporalDateTime.[[ISODay]], temporalDateTime.[[ISOHour]], temporalDateTime.[[ISOMinute]], temporalDateTime.[[ISOSecond]], temporalDateTime.[[ISOMillisecond]], temporalDateTime.[[ISOMicrosecond]], temporalDateTime.[[ISONanosecond]], isoCalendar, precision, "never").
2023-01-26 15:57:49 +00:00
auto date_time_string = MUST_OR_THROW_OOM ( temporal_date_time_to_string ( vm , temporal_date_time - > iso_year ( ) , temporal_date_time - > iso_month ( ) , temporal_date_time - > iso_day ( ) , temporal_date_time - > iso_hour ( ) , temporal_date_time - > iso_minute ( ) , temporal_date_time - > iso_second ( ) , temporal_date_time - > iso_millisecond ( ) , temporal_date_time - > iso_microsecond ( ) , temporal_date_time - > iso_nanosecond ( ) , iso_calendar , precision , " never " sv ) ) ;
2021-11-10 00:14:16 +00:00
2023-01-26 16:10:45 +00:00
String offset_string ;
2021-11-10 00:14:16 +00:00
2022-04-11 23:10:22 +01:00
// 10. If showOffset is "never", then
2021-11-10 00:14:16 +00:00
if ( show_offset = = " never " sv ) {
// a. Let offsetString be the empty String.
2023-01-26 16:10:45 +00:00
offset_string = { } ;
2021-11-10 00:14:16 +00:00
}
2022-04-11 23:10:22 +01:00
// 11. Else,
2021-11-10 00:14:16 +00:00
else {
// a. Let offsetNs be ? GetOffsetNanosecondsFor(timeZone, instant).
2022-08-20 08:52:42 +01:00
auto offset_ns = TRY ( get_offset_nanoseconds_for ( vm , & time_zone , * instant ) ) ;
2021-11-10 00:14:16 +00:00
// b. Let offsetString be ! FormatISOTimeZoneOffsetString(offsetNs).
2023-01-26 16:10:45 +00:00
offset_string = MUST_OR_THROW_OOM ( format_iso_time_zone_offset_string ( vm , offset_ns ) ) ;
2021-11-10 00:14:16 +00:00
}
2023-01-26 16:17:24 +00:00
String time_zone_string ;
2021-11-10 00:14:16 +00:00
2022-04-11 23:10:22 +01:00
// 12. If showTimeZone is "never", then
2021-11-10 00:14:16 +00:00
if ( show_time_zone = = " never " sv ) {
// a. Let timeZoneString be the empty String.
2023-01-26 16:17:24 +00:00
time_zone_string = { } ;
2021-11-10 00:14:16 +00:00
}
2022-04-11 23:10:22 +01:00
// 13. Else,
2021-11-10 00:14:16 +00:00
else {
// a. Let timeZoneID be ? ToString(timeZone).
2023-01-28 22:54:44 +00:00
auto time_zone_id = TRY ( Value ( & time_zone ) . to_string ( vm ) ) ;
2021-11-10 00:14:16 +00:00
2022-11-02 19:40:49 +00:00
// b. If showTimeZone is "critical", let flag be "!"; else let flag be the empty String.
auto flag = show_time_zone = = " critical " sv ? " ! " sv : " " sv ;
// c. Let timeZoneString be the string-concatenation of the code unit 0x005B (LEFT SQUARE BRACKET), flag, timeZoneID, and the code unit 0x005D (RIGHT SQUARE BRACKET).
2023-01-26 16:17:24 +00:00
time_zone_string = TRY_OR_THROW_OOM ( vm , String : : formatted ( " [{}{}] " , flag , time_zone_id ) ) ;
2021-11-10 00:14:16 +00:00
}
2022-08-08 14:48:48 +01:00
// 14. Let calendarString be ? MaybeFormatCalendarAnnotation(zonedDateTime.[[Calendar]], showCalendar).
auto calendar_string = TRY ( maybe_format_calendar_annotation ( vm , & zoned_date_time . calendar ( ) , show_calendar ) ) ;
2021-11-10 00:14:16 +00:00
2022-08-08 14:48:48 +01:00
// 15. Return the string-concatenation of dateTimeString, offsetString, timeZoneString, and calendarString.
2023-01-26 16:17:24 +00:00
return TRY_OR_THROW_OOM ( vm , String : : formatted ( " {}{}{}{} " , date_time_string , offset_string , time_zone_string , calendar_string ) ) ;
2021-11-10 00:14:16 +00:00
}
2021-11-02 00:14:47 +01:00
// 6.5.5 AddZonedDateTime ( epochNanoseconds, timeZone, calendar, years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal-addzoneddatetime
2022-08-20 08:52:42 +01:00
ThrowCompletionOr < BigInt * > add_zoned_date_time ( VM & vm , 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 )
2021-11-02 00:14:47 +01:00
{
2022-04-06 23:56:57 +01:00
// 1. If options is not present, set options to undefined.
// 2. Assert: Type(options) is Object or Undefined.
2021-11-02 00:14:47 +01:00
2022-04-06 23:56:57 +01:00
// 3. If all of years, months, weeks, and days are 0, then
2021-11-02 00:14:47 +01:00
if ( years = = 0 & & months = = 0 & & weeks = = 0 & & days = = 0 ) {
2022-06-15 00:59:01 +01:00
// a. Return ? AddInstant(epochNanoseconds, hours, minutes, seconds, milliseconds, microseconds, nanoseconds).
2022-08-20 08:52:42 +01:00
return add_instant ( vm , epoch_nanoseconds , hours , minutes , seconds , milliseconds , microseconds , nanoseconds ) ;
2021-11-02 00:14:47 +01:00
}
2022-04-06 23:56:57 +01:00
// 4. Let instant be ! CreateTemporalInstant(epochNanoseconds).
2022-08-20 08:52:42 +01:00
auto * instant = MUST ( create_temporal_instant ( vm , epoch_nanoseconds ) ) ;
2021-11-02 00:14:47 +01:00
2022-04-06 23:56:57 +01:00
// 5. Let temporalDateTime be ? BuiltinTimeZoneGetPlainDateTimeFor(timeZone, instant, calendar).
2022-08-20 08:52:42 +01:00
auto * temporal_date_time = TRY ( builtin_time_zone_get_plain_date_time_for ( vm , time_zone , * instant , calendar ) ) ;
2021-11-02 00:14:47 +01:00
2022-06-23 23:11:02 +01:00
// 6. Let datePart be ! CreateTemporalDate(temporalDateTime.[[ISOYear]], temporalDateTime.[[ISOMonth]], temporalDateTime.[[ISODay]], calendar).
2022-08-20 08:52:42 +01:00
auto * date_part = MUST ( create_temporal_date ( vm , temporal_date_time - > iso_year ( ) , temporal_date_time - > iso_month ( ) , temporal_date_time - > iso_day ( ) , calendar ) ) ;
2021-11-02 00:14:47 +01:00
2022-04-06 23:56:57 +01:00
// 7. Let dateDuration be ! CreateTemporalDuration(years, months, weeks, days, 0, 0, 0, 0, 0, 0).
2022-08-20 08:52:42 +01:00
auto * date_duration = MUST ( create_temporal_duration ( vm , years , months , weeks , days , 0 , 0 , 0 , 0 , 0 , 0 ) ) ;
2021-11-02 00:14:47 +01:00
2022-04-06 23:56:57 +01:00
// 8. Let addedDate be ? CalendarDateAdd(calendar, datePart, dateDuration, options).
2022-08-20 08:52:42 +01:00
auto * added_date = TRY ( calendar_date_add ( vm , calendar , date_part , * date_duration , options ) ) ;
2021-11-02 00:14:47 +01:00
2022-04-06 23:56:57 +01:00
// 9. Let intermediateDateTime be ? CreateTemporalDateTime(addedDate.[[ISOYear]], addedDate.[[ISOMonth]], addedDate.[[ISODay]], temporalDateTime.[[ISOHour]], temporalDateTime.[[ISOMinute]], temporalDateTime.[[ISOSecond]], temporalDateTime.[[ISOMillisecond]], temporalDateTime.[[ISOMicrosecond]], temporalDateTime.[[ISONanosecond]], calendar).
2022-08-20 08:52:42 +01:00
auto * intermediate_date_time = TRY ( create_temporal_date_time ( vm , added_date - > iso_year ( ) , added_date - > iso_month ( ) , added_date - > iso_day ( ) , temporal_date_time - > iso_hour ( ) , temporal_date_time - > iso_minute ( ) , temporal_date_time - > iso_second ( ) , temporal_date_time - > iso_millisecond ( ) , temporal_date_time - > iso_microsecond ( ) , temporal_date_time - > iso_nanosecond ( ) , calendar ) ) ;
2021-11-02 00:14:47 +01:00
2022-04-06 23:56:57 +01:00
// 10. Let intermediateInstant be ? BuiltinTimeZoneGetInstantFor(timeZone, intermediateDateTime, "compatible").
2022-08-20 08:52:42 +01:00
auto * intermediate_instant = TRY ( builtin_time_zone_get_instant_for ( vm , time_zone , * intermediate_date_time , " compatible " sv ) ) ;
2021-11-02 00:14:47 +01:00
2022-06-15 00:59:01 +01:00
// 11. Return ? AddInstant(intermediateInstant.[[Nanoseconds]], hours, minutes, seconds, milliseconds, microseconds, nanoseconds).
2022-08-20 08:52:42 +01:00
return add_instant ( vm , intermediate_instant - > nanoseconds ( ) , hours , minutes , seconds , milliseconds , microseconds , nanoseconds ) ;
2021-11-02 00:14:47 +01:00
}
2022-06-15 00:28:03 +01:00
// 6.5.6 DifferenceZonedDateTime ( ns1, ns2, timeZone, calendar, largestUnit, options ), https://tc39.es/proposal-temporal/#sec-temporal-differencezoneddatetime
2022-08-20 08:52:42 +01:00
ThrowCompletionOr < DurationRecord > difference_zoned_date_time ( VM & vm , BigInt const & nanoseconds1 , BigInt const & nanoseconds2 , Object & time_zone , Object & calendar , StringView largest_unit , Object const & options )
2021-11-23 23:07:21 +00:00
{
2022-06-15 00:49:45 +01:00
// 1. If ns1 is ns2, then
2021-11-23 23:07:21 +00:00
if ( nanoseconds1 . big_integer ( ) = = nanoseconds2 . big_integer ( ) ) {
2022-03-10 16:52:25 +01:00
// a. Return ! CreateDurationRecord(0, 0, 0, 0, 0, 0, 0, 0, 0, 0).
return create_duration_record ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ) ;
2021-11-23 23:07:21 +00:00
}
2022-06-15 00:49:45 +01:00
// 2. Let startInstant be ! CreateTemporalInstant(ns1).
2022-08-20 08:52:42 +01:00
auto * start_instant = MUST ( create_temporal_instant ( vm , nanoseconds1 ) ) ;
2021-11-23 23:07:21 +00:00
2022-06-15 00:49:45 +01:00
// 3. Let startDateTime be ? BuiltinTimeZoneGetPlainDateTimeFor(timeZone, startInstant, calendar).
2022-08-20 08:52:42 +01:00
auto * start_date_time = TRY ( builtin_time_zone_get_plain_date_time_for ( vm , & time_zone , * start_instant , calendar ) ) ;
2021-11-23 23:07:21 +00:00
2022-06-15 00:49:45 +01:00
// 4. Let endInstant be ! CreateTemporalInstant(ns2).
2022-08-20 08:52:42 +01:00
auto * end_instant = MUST ( create_temporal_instant ( vm , nanoseconds2 ) ) ;
2021-11-23 23:07:21 +00:00
2022-06-15 00:49:45 +01:00
// 5. Let endDateTime be ? BuiltinTimeZoneGetPlainDateTimeFor(timeZone, endInstant, calendar).
2022-08-20 08:52:42 +01:00
auto * end_date_time = TRY ( builtin_time_zone_get_plain_date_time_for ( vm , & time_zone , * end_instant , calendar ) ) ;
2021-11-23 23:07:21 +00:00
2022-06-15 00:49:45 +01:00
// 6. Let dateDifference be ? DifferenceISODateTime(startDateTime.[[ISOYear]], startDateTime.[[ISOMonth]], startDateTime.[[ISODay]], startDateTime.[[ISOHour]], startDateTime.[[ISOMinute]], startDateTime.[[ISOSecond]], startDateTime.[[ISOMillisecond]], startDateTime.[[ISOMicrosecond]], startDateTime.[[ISONanosecond]], endDateTime.[[ISOYear]], endDateTime.[[ISOMonth]], endDateTime.[[ISODay]], endDateTime.[[ISOHour]], endDateTime.[[ISOMinute]], endDateTime.[[ISOSecond]], endDateTime.[[ISOMillisecond]], endDateTime.[[ISOMicrosecond]], endDateTime.[[ISONanosecond]], calendar, largestUnit, options).
2022-08-20 08:52:42 +01:00
auto date_difference = TRY ( difference_iso_date_time ( vm , start_date_time - > iso_year ( ) , start_date_time - > iso_month ( ) , start_date_time - > iso_day ( ) , start_date_time - > iso_hour ( ) , start_date_time - > iso_minute ( ) , start_date_time - > iso_second ( ) , start_date_time - > iso_millisecond ( ) , start_date_time - > iso_microsecond ( ) , start_date_time - > iso_nanosecond ( ) , end_date_time - > iso_year ( ) , end_date_time - > iso_month ( ) , end_date_time - > iso_day ( ) , end_date_time - > iso_hour ( ) , end_date_time - > iso_minute ( ) , end_date_time - > iso_second ( ) , end_date_time - > iso_millisecond ( ) , end_date_time - > iso_microsecond ( ) , end_date_time - > iso_nanosecond ( ) , calendar , largest_unit , options ) ) ;
2021-11-23 23:07:21 +00:00
2022-06-15 00:49:45 +01:00
// 7. Let intermediateNs be ? AddZonedDateTime(ns1, timeZone, calendar, dateDifference.[[Years]], dateDifference.[[Months]], dateDifference.[[Weeks]], 0, 0, 0, 0, 0, 0, 0).
2022-08-20 08:52:42 +01:00
auto * intermediate_ns = TRY ( add_zoned_date_time ( vm , nanoseconds1 , & time_zone , calendar , date_difference . years , date_difference . months , date_difference . weeks , 0 , 0 , 0 , 0 , 0 , 0 , 0 ) ) ;
2021-11-23 23:07:21 +00:00
2022-06-15 00:49:45 +01:00
// 8. Let timeRemainderNs be ns2 - intermediateNs.
2021-11-23 23:07:21 +00:00
auto time_remainder_ns = nanoseconds2 . big_integer ( ) . minus ( intermediate_ns - > big_integer ( ) ) ;
2022-06-15 00:49:45 +01:00
// 9. Let intermediate be ! CreateTemporalZonedDateTime(intermediateNs, timeZone, calendar).
2022-08-20 08:52:42 +01:00
auto * intermediate = MUST ( create_temporal_zoned_date_time ( vm , * intermediate_ns , time_zone , calendar ) ) ;
2021-11-23 23:07:21 +00:00
2022-06-15 00:49:45 +01:00
// 10. Let result be ? NanosecondsToDays(timeRemainderNs, intermediate).
2022-08-20 08:52:42 +01:00
auto result = TRY ( nanoseconds_to_days ( vm , time_remainder_ns , intermediate ) ) ;
2021-11-23 23:07:21 +00:00
2022-06-15 00:49:45 +01:00
// 11. Let timeDifference be ! BalanceDuration(0, 0, 0, 0, 0, 0, result.[[Nanoseconds]], "hour").
2022-08-20 08:52:42 +01:00
auto time_difference = MUST ( balance_duration ( vm , 0 , 0 , 0 , 0 , 0 , 0 , result . nanoseconds , " hour " sv ) ) ;
2021-11-23 23:07:21 +00:00
2022-06-15 00:49:45 +01:00
// 12. Return ! CreateDurationRecord(dateDifference.[[Years]], dateDifference.[[Months]], dateDifference.[[Weeks]], result.[[Days]], timeDifference.[[Hours]], timeDifference.[[Minutes]], timeDifference.[[Seconds]], timeDifference.[[Milliseconds]], timeDifference.[[Microseconds]], timeDifference.[[Nanoseconds]]).
2022-03-10 16:52:25 +01:00
return create_duration_record ( date_difference . years , date_difference . months , date_difference . weeks , result . days , time_difference . hours , time_difference . minutes , time_difference . seconds , time_difference . milliseconds , time_difference . microseconds , time_difference . nanoseconds ) ;
2021-11-23 23:07:21 +00:00
}
2021-11-02 00:14:47 +01:00
// 6.5.7 NanosecondsToDays ( nanoseconds, relativeTo ), https://tc39.es/proposal-temporal/#sec-temporal-nanosecondstodays
2022-08-20 08:52:42 +01:00
ThrowCompletionOr < NanosecondsToDaysResult > nanoseconds_to_days ( VM & vm , Crypto : : SignedBigInteger nanoseconds , Value relative_to_value )
2021-11-02 00:14:47 +01:00
{
2022-08-20 08:52:42 +01:00
auto & realm = * vm . current_realm ( ) ;
2021-11-02 00:14:47 +01:00
2022-05-06 19:28:56 +02:00
// 1. Let dayLengthNs be nsPerDay.
auto day_length_ns = ns_per_day_bigint ;
2021-11-02 00:14:47 +01:00
2022-03-18 23:20:14 +00:00
// 2. If nanoseconds = 0, then
2022-07-16 12:43:23 -04:00
if ( nanoseconds . is_zero ( ) ) {
2021-11-02 00:14:47 +01:00
// a. Return the Record { [[Days]]: 0, [[Nanoseconds]]: 0, [[DayLength]]: dayLengthNs }.
2022-03-18 23:11:33 +00:00
return NanosecondsToDaysResult { . days = 0 , . nanoseconds = " 0 " _sbigint , . day_length = day_length_ns . to_double ( ) } ;
2021-11-02 00:14:47 +01:00
}
2022-04-29 21:09:10 +02:00
// 3. If nanoseconds < 0, let sign be -1; else, let sign be 1.
2022-03-10 17:56:58 +01:00
auto sign = nanoseconds . is_negative ( ) ? - 1 : 1 ;
2022-03-18 23:20:14 +00:00
// 4. If Type(relativeTo) is not Object or relativeTo does not have an [[InitializedTemporalZonedDateTime]] internal slot, then
2021-11-02 00:14:47 +01:00
if ( ! relative_to_value . is_object ( ) | | ! is < ZonedDateTime > ( relative_to_value . as_object ( ) ) ) {
2022-10-14 09:29:16 -04:00
// a. Return the Record { [[Days]]: truncate(nanoseconds / dayLengthNs), [[Nanoseconds]]: (abs(nanoseconds) modulo dayLengthNs) × sign, [[DayLength]]: dayLengthNs }.
2021-11-02 00:14:47 +01:00
return NanosecondsToDaysResult {
. days = nanoseconds . divided_by ( day_length_ns ) . quotient . to_double ( ) ,
2022-08-26 00:58:37 +02:00
. nanoseconds = Crypto : : SignedBigInteger { nanoseconds . unsigned_value ( ) } . divided_by ( day_length_ns ) . remainder . multiplied_by ( Crypto : : SignedBigInteger { sign } ) ,
2021-11-02 00:14:47 +01:00
. day_length = day_length_ns . to_double ( )
} ;
}
auto & relative_to = static_cast < ZonedDateTime & > ( relative_to_value . as_object ( ) ) ;
2022-03-18 23:20:14 +00:00
// 5. Let startNs be ℝ (relativeTo.[[Nanoseconds]]).
2021-11-02 00:14:47 +01:00
auto & start_ns = relative_to . nanoseconds ( ) . big_integer ( ) ;
2022-03-18 23:20:14 +00:00
// 6. Let startInstant be ! CreateTemporalInstant(ℤ (startNs)).
2022-12-06 22:03:52 +00:00
auto * start_instant = MUST ( create_temporal_instant ( vm , BigInt : : create ( vm , start_ns ) ) ) ;
2021-11-02 00:14:47 +01:00
2022-03-18 23:20:14 +00:00
// 7. Let startDateTime be ? BuiltinTimeZoneGetPlainDateTimeFor(relativeTo.[[TimeZone]], startInstant, relativeTo.[[Calendar]]).
2022-08-20 08:52:42 +01:00
auto * start_date_time = TRY ( builtin_time_zone_get_plain_date_time_for ( vm , & relative_to . time_zone ( ) , * start_instant , relative_to . calendar ( ) ) ) ;
2021-11-02 00:14:47 +01:00
2022-03-18 23:20:14 +00:00
// 8. Let endNs be startNs + nanoseconds.
2021-11-02 00:14:47 +01:00
auto end_ns = start_ns . plus ( nanoseconds ) ;
2022-12-06 22:03:52 +00:00
auto end_ns_bigint = BigInt : : create ( vm , end_ns ) ;
2021-11-02 00:14:47 +01:00
2022-06-23 19:51:44 +01:00
// 9. If ! IsValidEpochNanoseconds(ℤ (endNs)) is false, throw a RangeError exception.
2022-12-06 22:03:52 +00:00
if ( ! is_valid_epoch_nanoseconds ( end_ns_bigint ) )
2022-08-16 20:33:17 +01:00
return vm . throw_completion < RangeError > ( ErrorType : : TemporalInvalidEpochNanoseconds ) ;
2022-06-23 19:51:44 +01:00
// 10. Let endInstant be ! CreateTemporalInstant(ℤ (endNs)).
2022-12-06 22:03:52 +00:00
auto * end_instant = MUST ( create_temporal_instant ( vm , end_ns_bigint ) ) ;
2022-06-23 19:51:44 +01:00
// 11. Let endDateTime be ? BuiltinTimeZoneGetPlainDateTimeFor(relativeTo.[[TimeZone]], endInstant, relativeTo.[[Calendar]]).
2022-08-20 08:52:42 +01:00
auto * end_date_time = TRY ( builtin_time_zone_get_plain_date_time_for ( vm , & relative_to . time_zone ( ) , * end_instant , relative_to . calendar ( ) ) ) ;
2021-11-02 00:14:47 +01:00
2022-06-23 19:51:44 +01:00
// 12. Let dateDifference be ? DifferenceISODateTime(startDateTime.[[ISOYear]], startDateTime.[[ISOMonth]], startDateTime.[[ISODay]], startDateTime.[[ISOHour]], startDateTime.[[ISOMinute]], startDateTime.[[ISOSecond]], startDateTime.[[ISOMillisecond]], startDateTime.[[ISOMicrosecond]], startDateTime.[[ISONanosecond]], endDateTime.[[ISOYear]], endDateTime.[[ISOMonth]], endDateTime.[[ISODay]], endDateTime.[[ISOHour]], endDateTime.[[ISOMinute]], endDateTime.[[ISOSecond]], endDateTime.[[ISOMillisecond]], endDateTime.[[ISOMicrosecond]], endDateTime.[[ISONanosecond]], relativeTo.[[Calendar]], "day", OrdinaryObjectCreate(null)).
2022-08-20 08:52:42 +01:00
auto date_difference = TRY ( difference_iso_date_time ( vm , start_date_time - > iso_year ( ) , start_date_time - > iso_month ( ) , start_date_time - > iso_day ( ) , start_date_time - > iso_hour ( ) , start_date_time - > iso_minute ( ) , start_date_time - > iso_second ( ) , start_date_time - > iso_millisecond ( ) , start_date_time - > iso_microsecond ( ) , start_date_time - > iso_nanosecond ( ) , end_date_time - > iso_year ( ) , end_date_time - > iso_month ( ) , end_date_time - > iso_day ( ) , end_date_time - > iso_hour ( ) , end_date_time - > iso_minute ( ) , end_date_time - > iso_second ( ) , end_date_time - > iso_millisecond ( ) , end_date_time - > iso_microsecond ( ) , end_date_time - > iso_nanosecond ( ) , relative_to . calendar ( ) , " day " sv , * Object : : create ( realm , nullptr ) ) ) ;
2021-11-02 00:14:47 +01:00
2022-06-23 19:51:44 +01:00
// 13. Let days be dateDifference.[[Days]].
2021-11-02 00:14:47 +01:00
auto days = date_difference . days ;
2022-06-23 19:51:44 +01:00
// 14. Let intermediateNs be ℝ (? AddZonedDateTime(ℤ (startNs), relativeTo.[[TimeZone]], relativeTo.[[Calendar]], 0, 0, 0, days, 0, 0, 0, 0, 0, 0)).
2022-12-06 22:03:52 +00:00
auto intermediate_ns = TRY ( add_zoned_date_time ( vm , BigInt : : create ( vm , start_ns ) , & relative_to . time_zone ( ) , relative_to . calendar ( ) , 0 , 0 , 0 , days , 0 , 0 , 0 , 0 , 0 , 0 ) ) - > big_integer ( ) ;
2021-11-02 00:14:47 +01:00
2022-06-23 19:51:44 +01:00
// 15. If sign is 1, then
2021-11-02 00:14:47 +01:00
if ( sign = = 1 ) {
// a. Repeat, while days > 0 and intermediateNs > endNs,
while ( days > 0 & & intermediate_ns > end_ns ) {
2022-04-29 21:09:10 +02:00
// i. Set days to days - 1.
2021-11-02 00:14:47 +01:00
days - - ;
// ii. Set intermediateNs to ℝ (? AddZonedDateTime(ℤ (startNs), relativeTo.[[TimeZone]], relativeTo.[[Calendar]], 0, 0, 0, days, 0, 0, 0, 0, 0, 0)).
2022-12-06 22:03:52 +00:00
intermediate_ns = TRY ( add_zoned_date_time ( vm , BigInt : : create ( vm , start_ns ) , & relative_to . time_zone ( ) , relative_to . calendar ( ) , 0 , 0 , 0 , days , 0 , 0 , 0 , 0 , 0 , 0 ) ) - > big_integer ( ) ;
2021-11-02 00:14:47 +01:00
}
}
2022-06-23 19:51:44 +01:00
// 16. Set nanoseconds to endNs - intermediateNs.
2021-11-02 00:14:47 +01:00
nanoseconds = end_ns . minus ( intermediate_ns ) ;
2022-06-23 19:51:44 +01:00
// 17. Let done be false.
// 18. Repeat, while done is false,
2021-11-02 00:14:47 +01:00
while ( true ) {
// a. Let oneDayFartherNs be ℝ (? AddZonedDateTime(ℤ (intermediateNs), relativeTo.[[TimeZone]], relativeTo.[[Calendar]], 0, 0, 0, sign, 0, 0, 0, 0, 0, 0)).
2022-12-06 22:03:52 +00:00
auto one_day_farther_ns = TRY ( add_zoned_date_time ( vm , BigInt : : create ( vm , intermediate_ns ) , & relative_to . time_zone ( ) , relative_to . calendar ( ) , 0 , 0 , 0 , sign , 0 , 0 , 0 , 0 , 0 , 0 ) ) - > big_integer ( ) ;
2021-11-02 00:14:47 +01:00
2022-04-29 21:09:10 +02:00
// b. Set dayLengthNs to oneDayFartherNs - intermediateNs.
2021-11-02 00:14:47 +01:00
day_length_ns = one_day_farther_ns . minus ( intermediate_ns ) ;
2022-04-29 21:09:10 +02:00
// c. If (nanoseconds - dayLengthNs) × sign ≥ 0, then
2022-08-26 00:58:37 +02:00
if ( nanoseconds . minus ( day_length_ns ) . multiplied_by ( Crypto : : SignedBigInteger { sign } ) > = " 0 " _sbigint ) {
2022-04-29 21:09:10 +02:00
// i. Set nanoseconds to nanoseconds - dayLengthNs.
2021-11-02 00:14:47 +01:00
nanoseconds = nanoseconds . minus ( day_length_ns ) ;
// ii. Set intermediateNs to oneDayFartherNs.
intermediate_ns = move ( one_day_farther_ns ) ;
// iii. Set days to days + sign.
days + = sign ;
}
// d. Else,
else {
// i. Set done to true.
break ;
}
}
2022-10-21 22:36:35 +01:00
// 19. If days < 0 and sign = 1, throw a RangeError exception.
if ( days < 0 & & sign = = 1 )
return vm . throw_completion < RangeError > ( ErrorType : : TemporalNanosecondsConvertedToDaysWithOppositeSign ) ;
// 20. If days > 0 and sign = -1, throw a RangeError exception.
if ( days > 0 & & sign = = - 1 )
return vm . throw_completion < RangeError > ( ErrorType : : TemporalNanosecondsConvertedToDaysWithOppositeSign ) ;
// 21. If nanoseconds < 0 and sign = 1, throw a RangeError exception.
if ( nanoseconds . is_negative ( ) & & sign = = 1 )
return vm . throw_completion < RangeError > ( ErrorType : : TemporalNanosecondsConvertedToRemainderOfNanosecondsWithOppositeSign ) ;
// 22. If nanoseconds > 0 and sign = -1, throw a RangeError exception.
if ( nanoseconds . is_positive ( ) & & sign = = - 1 )
return vm . throw_completion < RangeError > ( ErrorType : : TemporalNanosecondsConvertedToRemainderOfNanosecondsWithOppositeSign ) ;
2022-10-21 23:03:44 +01:00
// 23. If abs(nanoseconds) ≥ abs(dayLengthNs), throw a RangeError exception.
2022-10-23 19:53:23 +01:00
auto compare_result = nanoseconds . unsigned_value ( ) . compare_to_double ( fabs ( day_length_ns . to_double ( ) ) ) ;
2022-10-23 16:46:35 +01:00
if ( compare_result = = Crypto : : UnsignedBigInteger : : CompareResult : : DoubleLessThanBigInt | | compare_result = = Crypto : : UnsignedBigInteger : : CompareResult : : DoubleEqualsBigInt )
2022-10-21 23:03:44 +01:00
return vm . throw_completion < RangeError > ( ErrorType : : TemporalNanosecondsConvertedToRemainderOfNanosecondsLongerThanDayLength ) ;
// 24. Return the Record { [[Days]]: days, [[Nanoseconds]]: nanoseconds, [[DayLength]]: abs(dayLengthNs) }.
2022-03-18 23:11:33 +00:00
return NanosecondsToDaysResult { . days = days , . nanoseconds = move ( nanoseconds ) , . day_length = fabs ( day_length_ns . to_double ( ) ) } ;
2021-11-02 00:14:47 +01:00
}
2022-05-07 13:32:19 +02:00
// 6.5.8 DifferenceTemporalZonedDateTime ( operation, zonedDateTime, other, options ), https://tc39.es/proposal-temporal/#sec-temporal-differencetemporalzoneddatetime
2022-08-20 08:52:42 +01:00
ThrowCompletionOr < Duration * > difference_temporal_zoned_date_time ( VM & vm , DifferenceOperation operation , ZonedDateTime & zoned_date_time , Value other_value , Value options_value )
2022-05-07 13:32:19 +02:00
{
// 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).
2022-08-20 08:52:42 +01:00
auto * other = TRY ( to_temporal_zoned_date_time ( vm , other_value ) ) ;
2022-05-07 13:32:19 +02:00
// 3. If ? CalendarEquals(zonedDateTime.[[Calendar]], other.[[Calendar]]) is false, then
2022-08-20 08:52:42 +01:00
if ( ! TRY ( calendar_equals ( vm , zoned_date_time . calendar ( ) , other - > calendar ( ) ) ) ) {
2022-05-07 13:32:19 +02:00
// a. Throw a RangeError exception.
2022-08-16 20:33:17 +01:00
return vm . throw_completion < RangeError > ( ErrorType : : TemporalDifferentCalendars ) ;
2022-05-07 13:32:19 +02:00
}
2022-06-24 00:27:29 +01:00
// 4. Let settings be ? GetDifferenceSettings(operation, options, datetime, « », "nanosecond", "hour").
2022-08-20 08:52:42 +01:00
auto settings = TRY ( get_difference_settings ( vm , operation , options_value , UnitGroup : : DateTime , { } , { " nanosecond " sv } , " hour " sv ) ) ;
2022-05-07 13:32:19 +02:00
2022-06-24 00:27:29 +01:00
// 5. If settings.[[LargestUnit]] is not one of "year", "month", "week", or "day", then
if ( ! settings . largest_unit . is_one_of ( " year " sv , " month " sv , " week " sv , " day " sv ) ) {
// a. Let differenceNs be ! DifferenceInstant(zonedDateTime.[[Nanoseconds]], other.[[Nanoseconds]], settings.[[RoundingIncrement]], settings.[[SmallestUnit]], settings.[[RoundingMode]]).
2022-08-20 08:52:42 +01:00
auto * difference_ns = difference_instant ( vm , zoned_date_time . nanoseconds ( ) , other - > nanoseconds ( ) , settings . rounding_increment , settings . smallest_unit , settings . rounding_mode ) ;
2022-05-07 13:32:19 +02:00
2022-05-09 20:27:20 +02:00
// b. Assert: The following steps cannot fail due to overflow in the Number domain because abs(differenceNs) ≤ 2 × nsMaxInstant.
2022-05-07 13:32:19 +02:00
2022-06-24 00:27:29 +01:00
// c. Let balanceResult be ! BalanceDuration(0, 0, 0, 0, 0, 0, differenceNs, settings.[[LargestUnit]]).
2022-08-20 08:52:42 +01:00
auto balance_result = MUST ( balance_duration ( vm , 0 , 0 , 0 , 0 , 0 , 0 , difference_ns - > big_integer ( ) , settings . largest_unit ) ) ;
2022-05-07 13:32:19 +02:00
// 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]]).
2022-08-20 08:52:42 +01:00
return MUST ( create_temporal_duration ( vm , 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 ) ) ;
2022-05-07 13:32:19 +02:00
}
2022-06-24 00:27:29 +01:00
// 6. If ? TimeZoneEquals(zonedDateTime.[[TimeZone]], other.[[TimeZone]]) is false, then
2022-08-20 08:52:42 +01:00
if ( ! TRY ( time_zone_equals ( vm , zoned_date_time . time_zone ( ) , other - > time_zone ( ) ) ) ) {
2022-05-07 13:32:19 +02:00
// a. Throw a RangeError exception.
2022-08-16 20:33:17 +01:00
return vm . throw_completion < RangeError > ( ErrorType : : TemporalDifferentTimeZones ) ;
2022-05-07 13:32:19 +02:00
}
2022-06-24 00:27:29 +01:00
// 7. Let untilOptions be ? MergeLargestUnitOption(settings.[[Options]], settings.[[LargestUnit]]).
2023-01-26 14:43:42 +00:00
auto * until_options = TRY ( merge_largest_unit_option ( vm , settings . options , settings . largest_unit ) ) ;
2022-05-07 13:32:19 +02:00
2022-06-24 00:27:29 +01:00
// 8. Let difference be ? DifferenceZonedDateTime(zonedDateTime.[[Nanoseconds]], other.[[Nanoseconds]], zonedDateTime.[[TimeZone]], zonedDateTime.[[Calendar]], settings.[[LargestUnit]], untilOptions).
2022-08-20 08:52:42 +01:00
auto difference = TRY ( difference_zoned_date_time ( vm , zoned_date_time . nanoseconds ( ) , other - > nanoseconds ( ) , zoned_date_time . time_zone ( ) , zoned_date_time . calendar ( ) , settings . largest_unit , * until_options ) ) ;
2022-05-07 13:32:19 +02:00
2022-06-24 00:27:29 +01:00
// 9. Let roundResult be (? RoundDuration(difference.[[Years]], difference.[[Months]], difference.[[Weeks]], difference.[[Days]], difference.[[Hours]], difference.[[Minutes]], difference.[[Seconds]], difference.[[Milliseconds]], difference.[[Microseconds]], difference.[[Nanoseconds]], settings.[[RoundingIncrement]], settings.[[SmallestUnit]], settings.[[RoundingMode]], zonedDateTime)).[[DurationRecord]].
2022-08-20 08:52:42 +01:00
auto round_result = TRY ( round_duration ( vm , difference . years , difference . months , difference . weeks , difference . days , difference . hours , difference . minutes , difference . seconds , difference . milliseconds , difference . microseconds , difference . nanoseconds , settings . rounding_increment , settings . smallest_unit , settings . rounding_mode , & zoned_date_time ) ) . duration_record ;
2022-05-07 13:32:19 +02:00
2022-06-24 00:27:29 +01:00
// 10. Let result be ? AdjustRoundedDurationDays(roundResult.[[Years]], roundResult.[[Months]], roundResult.[[Weeks]], roundResult.[[Days]], roundResult.[[Hours]], roundResult.[[Minutes]], roundResult.[[Seconds]], roundResult.[[Milliseconds]], roundResult.[[Microseconds]], roundResult.[[Nanoseconds]], settings.[[RoundingIncrement]], settings.[[SmallestUnit]], settings.[[RoundingMode]], zonedDateTime).
2022-08-20 08:52:42 +01:00
auto result = TRY ( adjust_rounded_duration_days ( vm , 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 , settings . rounding_increment , settings . smallest_unit , settings . rounding_mode , & zoned_date_time ) ) ;
2022-05-07 13:32:19 +02:00
2022-06-24 00:27:29 +01:00
// 11. 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]]).
2022-08-20 08:52:42 +01:00
return MUST ( create_temporal_duration ( vm , 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 ) ) ;
2022-05-07 13:32:19 +02:00
}
// 6.5.9 AddDurationToOrSubtractDurationFromZonedDateTime ( operation, zonedDateTime, temporalDurationLike, options ), https://tc39.es/proposal-temporal/#sec-temporal-adddurationtoOrsubtractdurationfromzoneddatetime
2022-08-20 08:52:42 +01:00
ThrowCompletionOr < ZonedDateTime * > add_duration_to_or_subtract_duration_from_zoned_date_time ( VM & vm , ArithmeticOperation operation , ZonedDateTime & zoned_date_time , Value temporal_duration_like , Value options_value )
2022-05-06 19:01:15 +02:00
{
// 1. If operation is subtract, let sign be -1. Otherwise, let sign be 1.
i8 sign = operation = = ArithmeticOperation : : Subtract ? - 1 : 1 ;
// 2. Let duration be ? ToTemporalDurationRecord(temporalDurationLike).
2022-08-20 08:52:42 +01:00
auto duration = TRY ( to_temporal_duration_record ( vm , temporal_duration_like ) ) ;
2022-05-06 19:01:15 +02:00
// 3. Set options to ? GetOptionsObject(options).
2022-08-20 08:52:42 +01:00
auto * options = TRY ( get_options_object ( vm , options_value ) ) ;
2022-05-06 19:01:15 +02:00
// 4. Let timeZone be zonedDateTime.[[TimeZone]].
auto & time_zone = zoned_date_time . time_zone ( ) ;
// 5. Let calendar be zonedDateTime.[[Calendar]].
auto & calendar = zoned_date_time . calendar ( ) ;
// 6. Let epochNanoseconds be ? AddZonedDateTime(zonedDateTime.[[Nanoseconds]], timeZone, calendar, sign × duration.[[Years]], sign × duration.[[Months]], sign × duration.[[Weeks]], sign × duration.[[Days]], sign × duration.[[Hours]], sign × duration.[[Minutes]], sign × duration.[[Seconds]], sign × duration.[[Milliseconds]], sign × duration.[[Microseconds]], sign × duration.[[Nanoseconds]], options).
2022-08-20 08:52:42 +01:00
auto * epoch_nanoseconds = TRY ( add_zoned_date_time ( vm , zoned_date_time . nanoseconds ( ) , & time_zone , calendar , sign * duration . years , sign * duration . months , sign * duration . weeks , sign * duration . days , sign * duration . hours , sign * duration . minutes , sign * duration . seconds , sign * duration . milliseconds , sign * duration . microseconds , sign * duration . nanoseconds , options ) ) ;
2022-05-06 19:01:15 +02:00
// 7. Return ! CreateTemporalZonedDateTime(epochNanoseconds, timeZone, calendar).
2022-08-20 08:52:42 +01:00
return MUST ( create_temporal_zoned_date_time ( vm , * epoch_nanoseconds , time_zone , calendar ) ) ;
2022-05-06 19:01:15 +02:00
}
2021-08-01 17:20:11 +01:00
}