2021-07-15 23:20:43 +01:00
/*
2023-04-13 15:41:29 +02:00
* Copyright ( c ) 2021 - 2023 , Linus Groh < linusg @ serenityos . org >
2021-11-23 23:09:01 +00:00
* Copyright ( c ) 2021 , Luke Wilde < lukew @ serenityos . org >
2021-07-15 23:20:43 +01:00
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
2021-11-07 01:41:52 +00:00
# include <AK/StringBuilder.h>
2022-11-23 13:41:50 +01:00
# include <AK/TypeCasts.h>
2021-07-15 23:20:43 +01:00
# include <LibJS/Runtime/AbstractOperations.h>
2021-09-16 22:07:14 +01:00
# include <LibJS/Runtime/Completion.h>
2021-07-15 23:20:43 +01:00
# include <LibJS/Runtime/GlobalObject.h>
2021-07-18 21:44:05 +01:00
# include <LibJS/Runtime/Object.h>
# include <LibJS/Runtime/Temporal/AbstractOperations.h>
2021-11-07 01:41:52 +00:00
# include <LibJS/Runtime/Temporal/Calendar.h>
2021-07-15 23:20:43 +01:00
# include <LibJS/Runtime/Temporal/Duration.h>
# include <LibJS/Runtime/Temporal/DurationConstructor.h>
2021-11-07 01:41:52 +00:00
# include <LibJS/Runtime/Temporal/Instant.h>
2022-03-10 00:39:21 +01:00
# include <LibJS/Runtime/Temporal/PlainDate.h>
2021-11-07 01:41:52 +00:00
# include <LibJS/Runtime/Temporal/TimeZone.h>
2021-09-06 21:21:57 +03:00
# include <LibJS/Runtime/Temporal/ZonedDateTime.h>
2023-10-06 17:54:21 +02:00
# include <LibJS/Runtime/ValueInlines.h>
2021-07-15 23:20:43 +01:00
namespace JS : : Temporal {
2023-11-19 09:45:05 +01:00
JS_DEFINE_ALLOCATOR ( Duration ) ;
2021-07-15 23:20:43 +01:00
// 7 Temporal.Duration Objects, https://tc39.es/proposal-temporal/#sec-temporal-duration-objects
Duration : : Duration ( double years , double months , double weeks , double days , double hours , double minutes , double seconds , double milliseconds , double microseconds , double nanoseconds , Object & prototype )
2022-12-14 12:17:58 +01:00
: Object ( ConstructWithPrototypeTag : : Tag , prototype )
2021-07-15 23:20:43 +01:00
, m_years ( years )
, m_months ( months )
, m_weeks ( weeks )
, m_days ( days )
, m_hours ( hours )
, m_minutes ( minutes )
, m_seconds ( seconds )
, m_milliseconds ( milliseconds )
, m_microseconds ( microseconds )
, m_nanoseconds ( nanoseconds )
{
2022-05-07 16:26:32 +02:00
auto fields = AK : : Array {
& Duration : : m_years ,
& Duration : : m_months ,
& Duration : : m_weeks ,
& Duration : : m_days ,
& Duration : : m_hours ,
& Duration : : m_minutes ,
& Duration : : m_seconds ,
& Duration : : m_milliseconds ,
& Duration : : m_microseconds ,
& Duration : : m_nanoseconds ,
} ;
// NOTE: The spec stores these fields as mathematical values. VERIFY() that we have finite,
// integral values in them, and normalize any negative zeros caused by floating point math.
// This is usually done using ℝ (𝔽 (value)) at the call site.
for ( auto const & field : fields ) {
auto & value = this - > * field ;
VERIFY ( isfinite ( value ) ) ;
// FIXME: test-js contains a small number of cases where a Temporal.Duration is constructed
// with a non-integral double. Eliminate these and VERIFY(trunc(value) == value) instead.
if ( trunc ( value ) ! = value )
value = trunc ( value ) ;
else if ( bit_cast < u64 > ( value ) = = NEGATIVE_ZERO_BITS )
value = 0 ;
}
2021-07-15 23:20:43 +01:00
}
2022-03-10 16:52:25 +01:00
// NOTE: All of these have two overloads: one that can throw, and one that can't.
// This is so that we don't have to needlessly pass a global object and then unwrap
// the ThrowCompletionOr when we already know that the duration is valid.
// 7.5.5 CreateDurationRecord ( years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds ), https://tc39.es/proposal-temporal/#sec-temporal-createdurationrecord
DurationRecord create_duration_record ( double years , double months , double weeks , double days , double hours , double minutes , double seconds , double milliseconds , double microseconds , double nanoseconds )
{
// 1. If ! IsValidDuration(years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds) is false, throw a RangeError exception.
VERIFY ( is_valid_duration ( years , months , weeks , days , hours , minutes , seconds , milliseconds , microseconds , nanoseconds ) ) ;
2022-05-07 16:26:32 +02:00
// 2. Return the Record { [[Years]]: ℝ (𝔽 (years)), [[Months]]: ℝ (𝔽 (months)), [[Weeks]]: ℝ (𝔽 (weeks)), [[Days]]: ℝ (𝔽 (days)), [[Hours]]: ℝ (𝔽 (hours)), [[Minutes]]: ℝ (𝔽 (minutes)), [[Seconds]]: ℝ (𝔽 (seconds)), [[Milliseconds]]: ℝ (𝔽 (milliseconds)), [[Microseconds]]: ℝ (𝔽 (microseconds)), [[Nanoseconds]]: ℝ (𝔽 (nanoseconds)) }.
2022-03-10 16:52:25 +01:00
return DurationRecord { . years = years , . months = months , . weeks = weeks , . days = days , . hours = hours , . minutes = minutes , . seconds = seconds , . milliseconds = milliseconds , . microseconds = microseconds , . nanoseconds = nanoseconds } ;
}
// 7.5.5 CreateDurationRecord ( years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds ), https://tc39.es/proposal-temporal/#sec-temporal-createdurationrecord
2022-08-20 08:52:42 +01:00
ThrowCompletionOr < DurationRecord > create_duration_record ( VM & vm , double years , double months , double weeks , double days , double hours , double minutes , double seconds , double milliseconds , double microseconds , double nanoseconds )
2022-03-10 16:52:25 +01:00
{
// 1. If ! IsValidDuration(years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds) is false, throw a RangeError exception.
if ( ! is_valid_duration ( years , months , weeks , days , hours , minutes , seconds , milliseconds , microseconds , nanoseconds ) )
2022-08-16 20:33:17 +01:00
return vm . throw_completion < RangeError > ( ErrorType : : TemporalInvalidDuration ) ;
2022-03-10 16:52:25 +01:00
2022-05-07 16:26:32 +02:00
// 2. Return the Record { [[Years]]: ℝ (𝔽 (years)), [[Months]]: ℝ (𝔽 (months)), [[Weeks]]: ℝ (𝔽 (weeks)), [[Days]]: ℝ (𝔽 (days)), [[Hours]]: ℝ (𝔽 (hours)), [[Minutes]]: ℝ (𝔽 (minutes)), [[Seconds]]: ℝ (𝔽 (seconds)), [[Milliseconds]]: ℝ (𝔽 (milliseconds)), [[Microseconds]]: ℝ (𝔽 (microseconds)), [[Nanoseconds]]: ℝ (𝔽 (nanoseconds)) }.
2022-03-10 16:52:25 +01:00
return DurationRecord { . years = years , . months = months , . weeks = weeks , . days = days , . hours = hours , . minutes = minutes , . seconds = seconds , . milliseconds = milliseconds , . microseconds = microseconds , . nanoseconds = nanoseconds } ;
}
// 7.5.6 CreateDateDurationRecord ( years, months, weeks, days ), https://tc39.es/proposal-temporal/#sec-temporal-createdatedurationrecord
DateDurationRecord create_date_duration_record ( double years , double months , double weeks , double days )
{
// 1. If ! IsValidDuration(years, months, weeks, days, 0, 0, 0, 0, 0, 0) is false, throw a RangeError exception.
VERIFY ( is_valid_duration ( years , months , weeks , days , 0 , 0 , 0 , 0 , 0 , 0 ) ) ;
2022-05-07 16:26:32 +02:00
// 2. Return the Record { [[Years]]: ℝ (𝔽 (years)), [[Months]]: ℝ (𝔽 (months)), [[Weeks]]: ℝ (𝔽 (weeks)), [[Days]]: ℝ (𝔽 (days)) }.
2022-03-10 16:52:25 +01:00
return DateDurationRecord { . years = years , . months = months , . weeks = weeks , . days = days } ;
}
// 7.5.6 CreateDateDurationRecord ( years, months, weeks, days ), https://tc39.es/proposal-temporal/#sec-temporal-createdatedurationrecord
2022-08-20 08:52:42 +01:00
ThrowCompletionOr < DateDurationRecord > create_date_duration_record ( VM & vm , double years , double months , double weeks , double days )
2022-03-10 16:52:25 +01:00
{
// 1. If ! IsValidDuration(years, months, weeks, days, 0, 0, 0, 0, 0, 0) is false, throw a RangeError exception.
if ( ! is_valid_duration ( years , months , weeks , days , 0 , 0 , 0 , 0 , 0 , 0 ) )
2022-08-16 20:33:17 +01:00
return vm . throw_completion < RangeError > ( ErrorType : : TemporalInvalidDuration ) ;
2022-03-10 16:52:25 +01:00
2022-05-07 16:26:32 +02:00
// 2. Return the Record { [[Years]]: ℝ (𝔽 (years)), [[Months]]: ℝ (𝔽 (months)), [[Weeks]]: ℝ (𝔽 (weeks)), [[Days]]: ℝ (𝔽 (days)) }.
2022-03-10 16:52:25 +01:00
return DateDurationRecord { . years = years , . months = months , . weeks = weeks , . days = days } ;
}
// 7.5.7 CreateTimeDurationRecord ( days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds ), https://tc39.es/proposal-temporal/#sec-temporal-createtimedurationrecord
2022-08-20 08:52:42 +01:00
ThrowCompletionOr < TimeDurationRecord > create_time_duration_record ( VM & vm , double days , double hours , double minutes , double seconds , double milliseconds , double microseconds , double nanoseconds )
2022-03-10 16:52:25 +01:00
{
// 1. If ! IsValidDuration(0, 0, 0, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds) is false, throw a RangeError exception.
if ( ! is_valid_duration ( 0 , 0 , 0 , days , hours , minutes , seconds , milliseconds , microseconds , nanoseconds ) )
2022-08-16 20:33:17 +01:00
return vm . throw_completion < RangeError > ( ErrorType : : TemporalInvalidDuration ) ;
2022-03-10 16:52:25 +01:00
2022-05-07 16:26:32 +02:00
// 2. Return the Record { [[Days]]: ℝ (𝔽 (days)), [[Hours]]: ℝ (𝔽 (hours)), [[Minutes]]: ℝ (𝔽 (minutes)), [[Seconds]]: ℝ (𝔽 (seconds)), [[Milliseconds]]: ℝ (𝔽 (milliseconds)), [[Microseconds]]: ℝ (𝔽 (microseconds)), [[Nanoseconds]]: ℝ (𝔽 (nanoseconds)) }.
2022-03-10 16:52:25 +01:00
return TimeDurationRecord { . days = days , . hours = hours , . minutes = minutes , . seconds = seconds , . milliseconds = milliseconds , . microseconds = microseconds , . nanoseconds = nanoseconds } ;
}
// 7.5.8 ToTemporalDuration ( item ), https://tc39.es/proposal-temporal/#sec-temporal-totemporalduration
2022-08-20 08:52:42 +01:00
ThrowCompletionOr < Duration * > to_temporal_duration ( VM & vm , Value item )
2021-07-18 21:44:05 +01:00
{
2022-03-10 17:43:00 +01:00
// 1. If Type(item) is Object and item has an [[InitializedTemporalDuration]] internal slot, then
if ( item . is_object ( ) & & is < Duration > ( item . as_object ( ) ) ) {
// a. Return item.
return & static_cast < Duration & > ( item . as_object ( ) ) ;
2021-07-18 21:44:05 +01:00
}
2022-03-10 17:43:00 +01:00
// 2. Let result be ? ToTemporalDurationRecord(item).
2022-08-20 08:52:42 +01:00
auto result = TRY ( to_temporal_duration_record ( vm , item ) ) ;
2021-07-18 21:44:05 +01:00
2022-03-10 17:29:12 +01:00
// 3. Return ! CreateTemporalDuration(result.[[Years]], result.[[Months]], result.[[Weeks]], result.[[Days]], result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]]).
2022-08-20 08:52:42 +01:00
return MUST ( create_temporal_duration ( vm , result . years , result . months , result . weeks , result . days , result . hours , result . minutes , result . seconds , result . milliseconds , result . microseconds , result . nanoseconds ) ) ;
2021-07-18 21:44:05 +01:00
}
2022-03-10 16:52:25 +01:00
// 7.5.9 ToTemporalDurationRecord ( temporalDurationLike ), https://tc39.es/proposal-temporal/#sec-temporal-totemporaldurationrecord
2022-08-20 08:52:42 +01:00
ThrowCompletionOr < DurationRecord > to_temporal_duration_record ( VM & vm , Value temporal_duration_like )
2021-07-18 21:44:05 +01:00
{
2022-03-10 17:43:00 +01:00
// 1. If Type(temporalDurationLike) is not Object, then
if ( ! temporal_duration_like . is_object ( ) ) {
// a. Let string be ? ToString(temporalDurationLike).
2023-01-26 14:22:47 +00:00
auto string = TRY ( temporal_duration_like . to_string ( vm ) ) ;
2022-03-10 17:43:00 +01:00
// b. Return ? ParseTemporalDurationString(string).
2022-08-20 08:52:42 +01:00
return parse_temporal_duration_string ( vm , string ) ;
2022-03-10 17:43:00 +01:00
}
// 2. If temporalDurationLike has an [[InitializedTemporalDuration]] internal slot, then
if ( is < Duration > ( temporal_duration_like . as_object ( ) ) ) {
auto & duration = static_cast < Duration const & > ( temporal_duration_like . as_object ( ) ) ;
2021-07-18 21:44:05 +01:00
2022-03-10 16:52:25 +01:00
// a. Return ! CreateDurationRecord(temporalDurationLike.[[Years]], temporalDurationLike.[[Months]], temporalDurationLike.[[Weeks]], temporalDurationLike.[[Days]], temporalDurationLike.[[Hours]], temporalDurationLike.[[Minutes]], temporalDurationLike.[[Seconds]], temporalDurationLike.[[Milliseconds]], temporalDurationLike.[[Microseconds]], temporalDurationLike.[[Nanoseconds]]).
return create_duration_record ( duration . years ( ) , duration . months ( ) , duration . weeks ( ) , duration . days ( ) , duration . hours ( ) , duration . minutes ( ) , duration . seconds ( ) , duration . milliseconds ( ) , duration . microseconds ( ) , duration . nanoseconds ( ) ) ;
2021-07-18 21:44:05 +01:00
}
2022-06-14 21:20:35 +01:00
// 3. Let result be a new Duration Record with each field set to 0.
2022-03-09 22:59:17 +01:00
auto result = DurationRecord { } ;
2021-07-18 21:44:05 +01:00
2022-06-14 21:23:01 +01:00
// 4. Let partial be ? ToTemporalPartialDurationRecord(temporalDurationLike).
2022-08-20 08:52:42 +01:00
auto partial = TRY ( to_temporal_partial_duration_record ( vm , temporal_duration_like ) ) ;
2021-07-18 21:44:05 +01:00
2022-11-13 15:24:42 -06:00
// 5. If partial.[[Years]] is not undefined, set result.[[Years]] to partial.[[Years]].
if ( partial . years . has_value ( ) )
result . years = partial . years . value ( ) ;
// 6. If partial.[[Months]] is not undefined, set result.[[Months]] to partial.[[Months]].
if ( partial . months . has_value ( ) )
result . months = partial . months . value ( ) ;
// 7. If partial.[[Weeks]] is not undefined, set result.[[Weeks]] to partial.[[Weeks]].
if ( partial . weeks . has_value ( ) )
result . weeks = partial . weeks . value ( ) ;
// 8. If partial.[[Days]] is not undefined, set result.[[Days]] to partial.[[Days]].
if ( partial . days . has_value ( ) )
result . days = partial . days . value ( ) ;
// 9. If partial.[[Hours]] is not undefined, set result.[[Hours]] to partial.[[Hours]].
if ( partial . hours . has_value ( ) )
result . hours = partial . hours . value ( ) ;
// 10. If partial.[[Minutes]] is not undefined, set result.[[Minutes]] to partial.[[Minutes]].
if ( partial . minutes . has_value ( ) )
result . minutes = partial . minutes . value ( ) ;
// 11. If partial.[[Seconds]] is not undefined, set result.[[Seconds]] to partial.[[Seconds]].
if ( partial . seconds . has_value ( ) )
result . seconds = partial . seconds . value ( ) ;
// 12. If partial.[[Milliseconds]] is not undefined, set result.[[Milliseconds]] to partial.[[Milliseconds]].
if ( partial . milliseconds . has_value ( ) )
result . milliseconds = partial . milliseconds . value ( ) ;
// 13. If partial.[[Microseconds]] is not undefined, set result.[[Microseconds]] to partial.[[Microseconds]].
if ( partial . microseconds . has_value ( ) )
result . microseconds = partial . microseconds . value ( ) ;
// 14. If partial.[[Nanoseconds]] is not undefined, set result.[[Nanoseconds]] to partial.[[Nanoseconds]].
if ( partial . nanoseconds . has_value ( ) )
result . nanoseconds = partial . nanoseconds . value ( ) ;
2021-07-18 21:44:05 +01:00
2022-11-13 15:24:42 -06:00
// 15. If ! IsValidDuration(result.[[Years]], result.[[Months]], result.[[Weeks]], result.[[Days]], result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]]) is false, then
2022-03-10 17:29:12 +01:00
if ( ! is_valid_duration ( result . years , result . months , result . weeks , result . days , result . hours , result . minutes , result . seconds , result . milliseconds , result . microseconds , result . nanoseconds ) ) {
// a. Throw a RangeError exception.
2022-08-16 20:33:17 +01:00
return vm . throw_completion < RangeError > ( ErrorType : : TemporalInvalidDuration ) ;
2022-03-10 17:29:12 +01:00
}
2022-11-13 15:24:42 -06:00
// 16. Return result.
2021-07-18 21:44:05 +01:00
return result ;
}
2022-03-10 16:52:25 +01:00
// 7.5.10 DurationSign ( years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds ), https://tc39.es/proposal-temporal/#sec-temporal-durationsign
2021-07-15 23:20:43 +01:00
i8 duration_sign ( double years , double months , double weeks , double days , double hours , double minutes , double seconds , double milliseconds , double microseconds , double nanoseconds )
{
// 1. For each value v of « years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds », do
for ( auto & v : { years , months , weeks , days , hours , minutes , seconds , milliseconds , microseconds , nanoseconds } ) {
2022-04-29 21:09:10 +02:00
// a. If v < 0, return -1.
2021-07-15 23:20:43 +01:00
if ( v < 0 )
return - 1 ;
// b. If v > 0, return 1.
if ( v > 0 )
return 1 ;
}
// 2. Return 0.
return 0 ;
}
2022-03-10 16:52:25 +01:00
// 7.5.11 IsValidDuration ( years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds ), https://tc39.es/proposal-temporal/#sec-temporal-isvalidduration
2021-07-15 23:20:43 +01:00
bool is_valid_duration ( double years , double months , double weeks , double days , double hours , double minutes , double seconds , double milliseconds , double microseconds , double nanoseconds )
{
// 1. Let sign be ! DurationSign(years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds).
auto sign = duration_sign ( years , months , weeks , days , hours , minutes , seconds , milliseconds , microseconds , nanoseconds ) ;
// 2. For each value v of « years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds », do
for ( auto & v : { years , months , weeks , days , hours , minutes , seconds , milliseconds , microseconds , nanoseconds } ) {
2022-05-07 16:26:32 +02:00
// a. If 𝔽 (v) is not finite, return false.
2021-07-15 23:20:43 +01:00
if ( ! isfinite ( v ) )
return false ;
// b. If v < 0 and sign > 0, return false.
if ( v < 0 & & sign > 0 )
return false ;
// c. If v > 0 and sign < 0, return false.
if ( v > 0 & & sign < 0 )
return false ;
}
// 3. Return true.
return true ;
}
2022-03-10 16:52:25 +01:00
// 7.5.12 DefaultTemporalLargestUnit ( years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds ), https://tc39.es/proposal-temporal/#sec-temporal-defaulttemporallargestunit
2021-11-23 23:09:01 +00:00
StringView default_temporal_largest_unit ( double years , double months , double weeks , double days , double hours , double minutes , double seconds , double milliseconds , double microseconds )
{
2023-01-06 02:35:12 +01:00
// 1. If years ≠ 0, return "year".
2021-11-23 23:09:01 +00:00
if ( years ! = 0 )
return " year " sv ;
2023-01-06 02:35:12 +01:00
// 2. If months ≠ 0, return "month".
2021-11-23 23:09:01 +00:00
if ( months ! = 0 )
return " month " sv ;
2023-01-06 02:35:12 +01:00
// 3. If weeks ≠ 0, return "week".
2021-11-23 23:09:01 +00:00
if ( weeks ! = 0 )
return " week " sv ;
2023-01-06 02:35:12 +01:00
// 4. If days ≠ 0, return "day".
2021-11-23 23:09:01 +00:00
if ( days ! = 0 )
return " day " sv ;
2023-01-06 02:35:12 +01:00
// 5. If hours ≠ 0, return "hour".
2021-11-23 23:09:01 +00:00
if ( hours ! = 0 )
return " hour " sv ;
2023-01-06 02:35:12 +01:00
// 6. If minutes ≠ 0, return "minute".
2021-11-23 23:09:01 +00:00
if ( minutes ! = 0 )
return " minute " sv ;
2023-01-06 02:35:12 +01:00
// 7. If seconds ≠ 0, return "second".
2021-11-23 23:09:01 +00:00
if ( seconds ! = 0 )
return " second " sv ;
2023-01-06 02:35:12 +01:00
// 8. If milliseconds ≠ 0, return "millisecond".
2021-11-23 23:09:01 +00:00
if ( milliseconds ! = 0 )
return " millisecond " sv ;
2023-01-06 02:35:12 +01:00
// 9. If microseconds ≠ 0, return "microsecond".
2021-11-23 23:09:01 +00:00
if ( microseconds ! = 0 )
return " microsecond " sv ;
// 10. Return "nanosecond".
return " nanosecond " sv ;
}
2022-06-14 21:23:01 +01:00
// 7.5.13 ToTemporalPartialDurationRecord ( temporalDurationLike ), https://tc39.es/proposal-temporal/#sec-temporal-totemporalpartialdurationrecord
2022-08-20 08:52:42 +01:00
ThrowCompletionOr < PartialDurationRecord > to_temporal_partial_duration_record ( VM & vm , Value temporal_duration_like )
2021-07-16 19:30:52 +01:00
{
// 1. If Type(temporalDurationLike) is not Object, then
if ( ! temporal_duration_like . is_object ( ) ) {
2021-09-16 22:07:14 +01:00
// a. Throw a TypeError exception.
2023-08-09 08:49:02 +02:00
return vm . throw_completion < TypeError > ( ErrorType : : NotAnObject , temporal_duration_like . to_string_without_side_effects ( ) ) ;
2021-07-16 19:30:52 +01:00
}
2022-03-09 22:59:17 +01:00
// 2. Let result be a new partial Duration Record with each field set to undefined.
auto result = PartialDurationRecord { } ;
2021-07-16 19:30:52 +01:00
2022-11-13 15:12:59 -06:00
// 3. Let days be ? Get(temporalDurationLike, "days").
auto days = TRY ( temporal_duration_like . as_object ( ) . get ( vm . names . days ) ) ;
2021-07-16 19:30:52 +01:00
2022-11-13 15:12:59 -06:00
// 4. If days is not undefined, set result.[[Days]] to ? ToIntegerIfIntegral(days).
if ( ! days . is_undefined ( ) )
result . days = TRY ( to_integer_if_integral ( vm , days , ErrorType : : TemporalInvalidDurationPropertyValueNonIntegral , " days " sv , days ) ) ;
2022-11-13 15:10:09 -06:00
2022-11-13 15:12:59 -06:00
// 5. Let hours be ? Get(temporalDurationLike, "hours").
auto hours = TRY ( temporal_duration_like . as_object ( ) . get ( vm . names . hours ) ) ;
2022-11-13 15:10:09 -06:00
2022-11-13 15:12:59 -06:00
// 6. If hours is not undefined, set result.[[Hours]] to ? ToIntegerIfIntegral(hours).
if ( ! hours . is_undefined ( ) )
result . hours = TRY ( to_integer_if_integral ( vm , hours , ErrorType : : TemporalInvalidDurationPropertyValueNonIntegral , " hours " sv , hours ) ) ;
2021-07-16 19:30:52 +01:00
2022-11-13 15:12:59 -06:00
// 7. Let microseconds be ? Get(temporalDurationLike, "microseconds").
auto microseconds = TRY ( temporal_duration_like . as_object ( ) . get ( vm . names . microseconds ) ) ;
2021-07-16 19:30:52 +01:00
2022-11-13 15:12:59 -06:00
// 8. If microseconds is not undefined, set result.[[Microseconds]] to ? ToIntegerIfIntegral(microseconds).
if ( ! microseconds . is_undefined ( ) )
result . microseconds = TRY ( to_integer_if_integral ( vm , microseconds , ErrorType : : TemporalInvalidDurationPropertyValueNonIntegral , " microseconds " sv , microseconds ) ) ;
2021-07-16 19:30:52 +01:00
2022-11-13 15:12:59 -06:00
// 9. Let milliseconds be ? Get(temporalDurationLike, "milliseconds").
auto milliseconds = TRY ( temporal_duration_like . as_object ( ) . get ( vm . names . milliseconds ) ) ;
2021-09-02 18:58:50 +01:00
2022-11-13 15:12:59 -06:00
// 10. If milliseconds is not undefined, set result.[[Milliseconds]] to ? ToIntegerIfIntegral(milliseconds).
if ( ! milliseconds . is_undefined ( ) )
result . milliseconds = TRY ( to_integer_if_integral ( vm , milliseconds , ErrorType : : TemporalInvalidDurationPropertyValueNonIntegral , " milliseconds " sv , milliseconds ) ) ;
2021-07-16 19:30:52 +01:00
2022-11-13 15:12:59 -06:00
// 11. Let minutes be ? Get(temporalDurationLike, "minutes").
auto minutes = TRY ( temporal_duration_like . as_object ( ) . get ( vm . names . minutes ) ) ;
// 12. If minutes is not undefined, set result.[[Minutes]] to ? ToIntegerIfIntegral(minutes).
if ( ! minutes . is_undefined ( ) )
result . minutes = TRY ( to_integer_if_integral ( vm , minutes , ErrorType : : TemporalInvalidDurationPropertyValueNonIntegral , " minutes " sv , minutes ) ) ;
// 13. Let months be ? Get(temporalDurationLike, "months").
auto months = TRY ( temporal_duration_like . as_object ( ) . get ( vm . names . months ) ) ;
// 14. If months is not undefined, set result.[[Months]] to ? ToIntegerIfIntegral(months).
if ( ! months . is_undefined ( ) )
result . months = TRY ( to_integer_if_integral ( vm , months , ErrorType : : TemporalInvalidDurationPropertyValueNonIntegral , " months " sv , months ) ) ;
// 15. Let nanoseconds be ? Get(temporalDurationLike, "nanoseconds").
auto nanoseconds = TRY ( temporal_duration_like . as_object ( ) . get ( vm . names . nanoseconds ) ) ;
// 16. If nanoseconds is not undefined, set result.[[Nanoseconds]] to ? ToIntegerIfIntegral(nanoseconds).
if ( ! nanoseconds . is_undefined ( ) )
result . nanoseconds = TRY ( to_integer_if_integral ( vm , nanoseconds , ErrorType : : TemporalInvalidDurationPropertyValueNonIntegral , " nanoseconds " sv , nanoseconds ) ) ;
// 17. Let seconds be ? Get(temporalDurationLike, "seconds").
auto seconds = TRY ( temporal_duration_like . as_object ( ) . get ( vm . names . seconds ) ) ;
// 18. If seconds is not undefined, set result.[[Seconds]] to ? ToIntegerIfIntegral(seconds).
if ( ! seconds . is_undefined ( ) )
result . seconds = TRY ( to_integer_if_integral ( vm , seconds , ErrorType : : TemporalInvalidDurationPropertyValueNonIntegral , " seconds " sv , seconds ) ) ;
// 19. Let weeks be ? Get(temporalDurationLike, "weeks").
auto weeks = TRY ( temporal_duration_like . as_object ( ) . get ( vm . names . weeks ) ) ;
// 20. If weeks is not undefined, set result.[[Weeks]] to ? ToIntegerIfIntegral(weeks).
if ( ! weeks . is_undefined ( ) )
result . weeks = TRY ( to_integer_if_integral ( vm , weeks , ErrorType : : TemporalInvalidDurationPropertyValueNonIntegral , " weeks " sv , weeks ) ) ;
// 21. Let years be ? Get(temporalDurationLike, "years").
auto years = TRY ( temporal_duration_like . as_object ( ) . get ( vm . names . years ) ) ;
// 22. If years is not undefined, set result.[[Years]] to ? ToIntegerIfIntegral(years).
if ( ! years . is_undefined ( ) )
result . years = TRY ( to_integer_if_integral ( vm , years , ErrorType : : TemporalInvalidDurationPropertyValueNonIntegral , " years " sv , years ) ) ;
// 23. If years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, and nanoseconds are all undefined, throw a TypeError exception.
if ( years . is_undefined ( ) & & months . is_undefined ( ) & & weeks . is_undefined ( ) & & days . is_undefined ( ) & & hours . is_undefined ( ) & & minutes . is_undefined ( ) & & seconds . is_undefined ( ) & & milliseconds . is_undefined ( ) & & microseconds . is_undefined ( ) & & nanoseconds . is_undefined ( ) )
2022-08-16 20:33:17 +01:00
return vm . throw_completion < TypeError > ( ErrorType : : TemporalInvalidDurationLikeObject ) ;
2021-07-16 19:30:52 +01:00
2022-11-13 15:12:59 -06:00
// 24. Return result.
2021-07-16 19:30:52 +01:00
return result ;
}
2022-03-10 16:52:25 +01:00
// 7.5.14 CreateTemporalDuration ( years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds [ , newTarget ] ), https://tc39.es/proposal-temporal/#sec-temporal-createtemporalduration
2022-08-20 08:52:42 +01:00
ThrowCompletionOr < Duration * > create_temporal_duration ( VM & vm , double years , double months , double weeks , double days , double hours , double minutes , double seconds , double milliseconds , double microseconds , double nanoseconds , FunctionObject const * new_target )
2021-07-15 23:20:43 +01:00
{
2022-08-20 08:52:42 +01:00
auto & realm = * vm . current_realm ( ) ;
2021-07-15 23:20:43 +01:00
// 1. If ! IsValidDuration(years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds) is false, throw a RangeError exception.
2021-09-16 22:07:14 +01:00
if ( ! is_valid_duration ( years , months , weeks , days , hours , minutes , seconds , milliseconds , microseconds , nanoseconds ) )
2022-08-16 20:33:17 +01:00
return vm . throw_completion < RangeError > ( ErrorType : : TemporalInvalidDuration ) ;
2021-07-15 23:20:43 +01:00
2022-03-10 17:04:46 +01:00
// 2. If newTarget is not present, set newTarget to %Temporal.Duration%.
2021-07-15 23:20:43 +01:00
if ( ! new_target )
2022-08-27 00:54:55 +01:00
new_target = realm . intrinsics ( ) . temporal_duration_constructor ( ) ;
2021-07-15 23:20:43 +01:00
// 3. Let object be ? OrdinaryCreateFromConstructor(newTarget, "%Temporal.Duration.prototype%", « [[InitializedTemporalDuration]], [[Years]], [[Months]], [[Weeks]], [[Days]], [[Hours]], [[Minutes]], [[Seconds]], [[Milliseconds]], [[Microseconds]], [[Nanoseconds]] »).
2022-05-07 16:26:32 +02:00
// 4. Set object.[[Years]] to ℝ (𝔽 (years)).
// 5. Set object.[[Months]] to ℝ (𝔽 (months)).
// 6. Set object.[[Weeks]] to ℝ (𝔽 (weeks)).
// 7. Set object.[[Days]] to ℝ (𝔽 (days)).
// 8. Set object.[[Hours]] to ℝ (𝔽 (hours)).
// 9. Set object.[[Minutes]] to ℝ (𝔽 (minutes)).
// 10. Set object.[[Seconds]] to ℝ (𝔽 (seconds)).
// 11. Set object.[[Milliseconds]] to ℝ (𝔽 (milliseconds)).
// 12. Set object.[[Microseconds]] to ℝ (𝔽 (microseconds)).
// 13. Set object.[[Nanoseconds]] to ℝ (𝔽 (nanoseconds)).
2022-12-14 18:34:32 +00:00
auto object = TRY ( ordinary_create_from_constructor < Duration > ( vm , * new_target , & Intrinsics : : temporal_duration_prototype , years , months , weeks , days , hours , minutes , seconds , milliseconds , microseconds , nanoseconds ) ) ;
2021-07-15 23:20:43 +01:00
// 14. Return object.
2022-12-14 18:34:32 +00:00
return object . ptr ( ) ;
2021-07-15 23:20:43 +01:00
}
2022-03-10 16:52:25 +01:00
// 7.5.15 CreateNegatedTemporalDuration ( duration ), https://tc39.es/proposal-temporal/#sec-temporal-createnegatedtemporalduration
2022-08-20 08:52:42 +01:00
Duration * create_negated_temporal_duration ( VM & vm , Duration const & duration )
2021-09-03 00:10:09 +01:00
{
2022-04-29 21:09:10 +02:00
// 1. Return ! CreateTemporalDuration(-duration.[[Years]], -duration.[[Months]], -duration.[[Weeks]], -duration.[[Days]], -duration.[[Hours]], -duration.[[Minutes]], -duration.[[Seconds]], -duration.[[Milliseconds]], -duration.[[Microseconds]], -duration.[[Nanoseconds]]).
2022-08-20 08:52:42 +01:00
return MUST ( create_temporal_duration ( vm , - duration . years ( ) , - duration . months ( ) , - duration . weeks ( ) , - duration . days ( ) , - duration . hours ( ) , - duration . minutes ( ) , - duration . seconds ( ) , - duration . milliseconds ( ) , - duration . microseconds ( ) , - duration . nanoseconds ( ) ) ) ;
2021-09-03 00:10:09 +01:00
}
2022-06-14 23:53:49 +01:00
// 7.5.16 CalculateOffsetShift ( relativeTo, y, mon, w, d ), https://tc39.es/proposal-temporal/#sec-temporal-calculateoffsetshift
2022-08-20 08:52:42 +01:00
ThrowCompletionOr < double > calculate_offset_shift ( VM & vm , Value relative_to_value , double years , double months , double weeks , double days )
2021-11-09 17:19:28 +00:00
{
// 1. If Type(relativeTo) is not Object or relativeTo does not have an [[InitializedTemporalZonedDateTime]] internal slot, return 0.
if ( ! relative_to_value . is_object ( ) | | ! is < ZonedDateTime > ( relative_to_value . as_object ( ) ) )
return 0.0 ;
auto & relative_to = static_cast < ZonedDateTime & > ( relative_to_value . as_object ( ) ) ;
// 2. Let instant be ! CreateTemporalInstant(relativeTo.[[Nanoseconds]]).
2022-08-20 08:52:42 +01:00
auto * instant = MUST ( create_temporal_instant ( vm , relative_to . nanoseconds ( ) ) ) ;
2021-11-09 17:19:28 +00:00
// 3. Let offsetBefore be ? GetOffsetNanosecondsFor(relativeTo.[[TimeZone]], instant).
2022-08-20 08:52:42 +01:00
auto offset_before = TRY ( get_offset_nanoseconds_for ( vm , & relative_to . time_zone ( ) , * instant ) ) ;
2021-11-09 17:19:28 +00:00
2022-06-14 23:53:49 +01:00
// 4. Let after be ? AddZonedDateTime(relativeTo.[[Nanoseconds]], relativeTo.[[TimeZone]], relativeTo.[[Calendar]], y, mon, w, d, 0, 0, 0, 0, 0, 0).
2022-08-20 08:52:42 +01:00
auto * after = TRY ( add_zoned_date_time ( vm , relative_to . nanoseconds ( ) , & relative_to . time_zone ( ) , relative_to . calendar ( ) , years , months , weeks , days , 0 , 0 , 0 , 0 , 0 , 0 ) ) ;
2021-11-09 17:19:28 +00:00
// 5. Let instantAfter be ! CreateTemporalInstant(after).
2022-08-20 08:52:42 +01:00
auto * instant_after = MUST ( create_temporal_instant ( vm , * after ) ) ;
2021-11-09 17:19:28 +00:00
// 6. Let offsetAfter be ? GetOffsetNanosecondsFor(relativeTo.[[TimeZone]], instantAfter).
2022-08-20 08:52:42 +01:00
auto offset_after = TRY ( get_offset_nanoseconds_for ( vm , & relative_to . time_zone ( ) , * instant_after ) ) ;
2021-11-09 17:19:28 +00:00
2022-04-29 21:09:10 +02:00
// 7. Return offsetAfter - offsetBefore.
2021-11-09 17:19:28 +00:00
return offset_after - offset_before ;
}
2022-03-10 16:52:25 +01:00
// 7.5.17 TotalDurationNanoseconds ( days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, offsetShift ), https://tc39.es/proposal-temporal/#sec-temporal-totaldurationnanoseconds
2022-03-18 22:53:36 +00:00
Crypto : : SignedBigInteger total_duration_nanoseconds ( double days , double hours , double minutes , double seconds , double milliseconds , double microseconds , Crypto : : SignedBigInteger const & nanoseconds , double offset_shift )
2021-09-06 21:21:57 +03:00
{
VERIFY ( offset_shift = = trunc ( offset_shift ) ) ;
2022-03-18 22:53:36 +00:00
auto result_nanoseconds = nanoseconds ;
2021-09-06 21:21:57 +03:00
2022-05-07 16:26:32 +02:00
// 1. If days ≠ 0, then
2021-09-06 21:21:57 +03:00
if ( days ! = 0 ) {
2022-04-29 21:09:10 +02:00
// a. Set nanoseconds to nanoseconds - offsetShift.
2022-08-26 00:49:50 +02:00
result_nanoseconds = result_nanoseconds . minus ( Crypto : : SignedBigInteger { offset_shift } ) ;
2021-09-06 21:21:57 +03:00
}
2022-05-07 16:26:32 +02:00
// 2. Set hours to hours + days × 24.
2022-08-26 00:49:50 +02:00
auto total_hours = Crypto : : SignedBigInteger { hours } . plus ( Crypto : : SignedBigInteger { days } . multiplied_by ( Crypto : : UnsignedBigInteger ( 24 ) ) ) ;
2022-05-07 16:26:32 +02:00
// 3. Set minutes to minutes + hours × 60.
2022-08-26 00:49:50 +02:00
auto total_minutes = Crypto : : SignedBigInteger { minutes } . plus ( total_hours . multiplied_by ( Crypto : : UnsignedBigInteger ( 60 ) ) ) ;
2022-05-07 16:26:32 +02:00
// 4. Set seconds to seconds + minutes × 60.
2022-08-26 00:49:50 +02:00
auto total_seconds = Crypto : : SignedBigInteger { seconds } . plus ( total_minutes . multiplied_by ( Crypto : : UnsignedBigInteger ( 60 ) ) ) ;
2022-05-07 16:26:32 +02:00
// 5. Set milliseconds to milliseconds + seconds × 1000.
2022-08-26 00:49:50 +02:00
auto total_milliseconds = Crypto : : SignedBigInteger { milliseconds } . plus ( total_seconds . multiplied_by ( Crypto : : UnsignedBigInteger ( 1000 ) ) ) ;
2022-05-07 16:26:32 +02:00
// 6. Set microseconds to microseconds + milliseconds × 1000.
2022-08-26 00:49:50 +02:00
auto total_microseconds = Crypto : : SignedBigInteger { microseconds } . plus ( total_milliseconds . multiplied_by ( Crypto : : UnsignedBigInteger ( 1000 ) ) ) ;
2022-05-07 16:26:32 +02:00
// 7. Return nanoseconds + microseconds × 1000.
2022-03-18 22:53:36 +00:00
return result_nanoseconds . plus ( total_microseconds . multiplied_by ( Crypto : : UnsignedBigInteger ( 1000 ) ) ) ;
2021-09-06 21:21:57 +03:00
}
2022-03-10 16:52:25 +01:00
// 7.5.18 BalanceDuration ( days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, largestUnit [ , relativeTo ] ), https://tc39.es/proposal-temporal/#sec-temporal-balanceduration
2023-01-26 14:43:42 +00:00
ThrowCompletionOr < TimeDurationRecord > balance_duration ( VM & vm , double days , double hours , double minutes , double seconds , double milliseconds , double microseconds , Crypto : : SignedBigInteger const & nanoseconds , StringView largest_unit , Object * relative_to )
2021-09-06 21:21:57 +03:00
{
// 1. If relativeTo is not present, set relativeTo to undefined.
2022-08-26 00:49:50 +02:00
// NOTE: If any of the inputs is not finite this will mean that we have infinities,
// so the duration will never be valid. Also
if ( ! isfinite ( days ) | | ! isfinite ( hours ) | | ! isfinite ( minutes ) | | ! isfinite ( seconds ) | | ! isfinite ( milliseconds ) | | ! isfinite ( microseconds ) )
return vm . throw_completion < RangeError > ( ErrorType : : TemporalInvalidDuration ) ;
2022-03-18 23:20:14 +00:00
Crypto : : SignedBigInteger total_nanoseconds ;
2021-09-06 21:21:57 +03:00
// 2. If Type(relativeTo) is Object and relativeTo has an [[InitializedTemporalZonedDateTime]] internal slot, then
if ( relative_to & & is < ZonedDateTime > ( * relative_to ) ) {
2021-11-02 00:14:47 +01:00
auto & relative_to_zoned_date_time = static_cast < ZonedDateTime & > ( * relative_to ) ;
2021-09-06 21:21:57 +03:00
// a. Let endNs be ? AddZonedDateTime(relativeTo.[[Nanoseconds]], relativeTo.[[TimeZone]], relativeTo.[[Calendar]], 0, 0, 0, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds).
2022-08-20 08:52:42 +01:00
auto * end_ns = TRY ( add_zoned_date_time ( vm , relative_to_zoned_date_time . nanoseconds ( ) , & relative_to_zoned_date_time . time_zone ( ) , relative_to_zoned_date_time . calendar ( ) , 0 , 0 , 0 , days , hours , minutes , seconds , milliseconds , microseconds , nanoseconds . to_double ( ) ) ) ;
2021-11-02 00:14:47 +01:00
2022-04-29 21:09:10 +02:00
// b. Set nanoseconds to ℝ (endNs - relativeTo.[[Nanoseconds]]).
2022-03-18 23:20:14 +00:00
total_nanoseconds = end_ns - > big_integer ( ) . minus ( relative_to_zoned_date_time . nanoseconds ( ) . big_integer ( ) ) ;
2021-09-06 21:21:57 +03:00
}
// 3. Else,
else {
2022-03-18 23:20:14 +00:00
// a. Set nanoseconds to ! TotalDurationNanoseconds(days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, 0).
total_nanoseconds = total_duration_nanoseconds ( days , hours , minutes , seconds , milliseconds , microseconds , nanoseconds , 0 ) ;
2021-09-06 21:21:57 +03:00
}
// 4. If largestUnit is one of "year", "month", "week", or "day", then
if ( largest_unit . is_one_of ( " year " sv , " month " sv , " week " sv , " day " sv ) ) {
// a. Let result be ? NanosecondsToDays(nanoseconds, relativeTo).
2022-08-20 08:52:42 +01:00
auto result = TRY ( nanoseconds_to_days ( vm , total_nanoseconds , relative_to ? : js_undefined ( ) ) ) ;
2021-09-06 21:21:57 +03:00
// b. Set days to result.[[Days]].
2021-11-02 00:14:47 +01:00
days = result . days ;
2021-09-06 21:21:57 +03:00
// c. Set nanoseconds to result.[[Nanoseconds]].
2022-03-18 23:20:14 +00:00
total_nanoseconds = move ( result . nanoseconds ) ;
2021-09-06 21:21:57 +03:00
}
// 5. Else,
else {
// a. Set days to 0.
days = 0 ;
}
// 6. Set hours, minutes, seconds, milliseconds, and microseconds to 0.
hours = 0 ;
minutes = 0 ;
seconds = 0 ;
milliseconds = 0 ;
microseconds = 0 ;
2022-04-29 21:09:10 +02:00
// 7. If nanoseconds < 0, let sign be -1; else, let sign be 1.
2021-09-06 21:21:57 +03:00
i8 sign = total_nanoseconds . is_negative ( ) ? - 1 : 1 ;
2022-03-18 23:20:14 +00:00
// 8. Set nanoseconds to abs(nanoseconds).
2021-09-06 21:21:57 +03:00
total_nanoseconds = Crypto : : SignedBigInteger ( total_nanoseconds . unsigned_value ( ) ) ;
2022-03-18 22:53:36 +00:00
auto result_nanoseconds = total_nanoseconds . to_double ( ) ;
2021-09-06 21:21:57 +03:00
2022-03-18 23:20:14 +00:00
// 9. If largestUnit is "year", "month", "week", "day", or "hour", then
2021-11-13 14:21:08 +00:00
if ( largest_unit . is_one_of ( " year " sv , " month " sv , " week " sv , " day " sv , " hour " sv ) ) {
2021-09-06 21:21:57 +03:00
// a. Set microseconds to floor(nanoseconds / 1000).
auto nanoseconds_division_result = total_nanoseconds . divided_by ( Crypto : : UnsignedBigInteger ( 1000 ) ) ;
// b. Set nanoseconds to nanoseconds modulo 1000.
result_nanoseconds = nanoseconds_division_result . remainder . to_double ( ) ;
// c. Set milliseconds to floor(microseconds / 1000).
auto microseconds_division_result = nanoseconds_division_result . quotient . divided_by ( Crypto : : UnsignedBigInteger ( 1000 ) ) ;
// d. Set microseconds to microseconds modulo 1000.
microseconds = microseconds_division_result . remainder . to_double ( ) ;
// e. Set seconds to floor(milliseconds / 1000).
auto milliseconds_division_result = microseconds_division_result . quotient . divided_by ( Crypto : : UnsignedBigInteger ( 1000 ) ) ;
// f. Set milliseconds to milliseconds modulo 1000.
milliseconds = milliseconds_division_result . remainder . to_double ( ) ;
// g. Set minutes to floor(seconds / 60).
auto seconds_division_result = milliseconds_division_result . quotient . divided_by ( Crypto : : UnsignedBigInteger ( 60 ) ) ;
// h. Set seconds to seconds modulo 60.
seconds = seconds_division_result . remainder . to_double ( ) ;
// i. Set hours to floor(minutes / 60).
2021-11-13 13:24:24 +00:00
auto minutes_division_result = seconds_division_result . quotient . divided_by ( Crypto : : UnsignedBigInteger ( 60 ) ) ;
2021-09-06 21:21:57 +03:00
hours = minutes_division_result . quotient . to_double ( ) ;
// j. Set minutes to minutes modulo 60.
minutes = minutes_division_result . remainder . to_double ( ) ;
}
2022-03-18 23:20:14 +00:00
// 10. Else if largestUnit is "minute", then
2021-09-06 21:21:57 +03:00
else if ( largest_unit = = " minute " sv ) {
// a. Set microseconds to floor(nanoseconds / 1000).
auto nanoseconds_division_result = total_nanoseconds . divided_by ( Crypto : : UnsignedBigInteger ( 1000 ) ) ;
// b. Set nanoseconds to nanoseconds modulo 1000.
result_nanoseconds = nanoseconds_division_result . remainder . to_double ( ) ;
// c. Set milliseconds to floor(microseconds / 1000).
auto microseconds_division_result = nanoseconds_division_result . quotient . divided_by ( Crypto : : UnsignedBigInteger ( 1000 ) ) ;
// d. Set microseconds to microseconds modulo 1000.
microseconds = microseconds_division_result . remainder . to_double ( ) ;
// e. Set seconds to floor(milliseconds / 1000).
auto milliseconds_division_result = microseconds_division_result . quotient . divided_by ( Crypto : : UnsignedBigInteger ( 1000 ) ) ;
// f. Set milliseconds to milliseconds modulo 1000.
milliseconds = milliseconds_division_result . remainder . to_double ( ) ;
// g. Set minutes to floor(seconds / 60).
auto seconds_division_result = milliseconds_division_result . quotient . divided_by ( Crypto : : UnsignedBigInteger ( 60 ) ) ;
minutes = seconds_division_result . quotient . to_double ( ) ;
// h. Set seconds to seconds modulo 60.
seconds = seconds_division_result . remainder . to_double ( ) ;
}
2022-03-18 23:20:14 +00:00
// 11. Else if largestUnit is "second", then
2021-09-06 21:21:57 +03:00
else if ( largest_unit = = " second " sv ) {
// a. Set microseconds to floor(nanoseconds / 1000).
auto nanoseconds_division_result = total_nanoseconds . divided_by ( Crypto : : UnsignedBigInteger ( 1000 ) ) ;
// b. Set nanoseconds to nanoseconds modulo 1000.
result_nanoseconds = nanoseconds_division_result . remainder . to_double ( ) ;
// c. Set milliseconds to floor(microseconds / 1000).
auto microseconds_division_result = nanoseconds_division_result . quotient . divided_by ( Crypto : : UnsignedBigInteger ( 1000 ) ) ;
// d. Set microseconds to microseconds modulo 1000.
microseconds = microseconds_division_result . remainder . to_double ( ) ;
// e. Set seconds to floor(milliseconds / 1000).
auto milliseconds_division_result = microseconds_division_result . quotient . divided_by ( Crypto : : UnsignedBigInteger ( 1000 ) ) ;
seconds = milliseconds_division_result . quotient . to_double ( ) ;
// f. Set milliseconds to milliseconds modulo 1000.
milliseconds = milliseconds_division_result . remainder . to_double ( ) ;
}
2022-03-18 23:20:14 +00:00
// 12. Else if largestUnit is "millisecond", then
2021-09-06 21:21:57 +03:00
else if ( largest_unit = = " millisecond " sv ) {
// a. Set microseconds to floor(nanoseconds / 1000).
auto nanoseconds_division_result = total_nanoseconds . divided_by ( Crypto : : UnsignedBigInteger ( 1000 ) ) ;
// b. Set nanoseconds to nanoseconds modulo 1000.
result_nanoseconds = nanoseconds_division_result . remainder . to_double ( ) ;
// c. Set milliseconds to floor(microseconds / 1000).
auto microseconds_division_result = nanoseconds_division_result . quotient . divided_by ( Crypto : : UnsignedBigInteger ( 1000 ) ) ;
milliseconds = microseconds_division_result . quotient . to_double ( ) ;
// d. Set microseconds to microseconds modulo 1000.
microseconds = microseconds_division_result . remainder . to_double ( ) ;
}
2022-03-18 23:20:14 +00:00
// 13. Else if largestUnit is "microsecond", then
2021-09-06 21:21:57 +03:00
else if ( largest_unit = = " microsecond " sv ) {
// a. Set microseconds to floor(nanoseconds / 1000).
auto nanoseconds_division_result = total_nanoseconds . divided_by ( Crypto : : UnsignedBigInteger ( 1000 ) ) ;
microseconds = nanoseconds_division_result . quotient . to_double ( ) ;
// b. Set nanoseconds to nanoseconds modulo 1000.
result_nanoseconds = nanoseconds_division_result . remainder . to_double ( ) ;
}
2022-03-18 23:20:14 +00:00
// 14. Else,
2021-09-06 21:21:57 +03:00
else {
// a. Assert: largestUnit is "nanosecond".
VERIFY ( largest_unit = = " nanosecond " sv ) ;
}
2022-05-07 16:26:32 +02:00
// 15. Return ? CreateTimeDurationRecord(days, hours × sign, minutes × sign, seconds × sign, milliseconds × sign, microseconds × sign, nanoseconds × sign).
2022-08-20 08:52:42 +01:00
return create_time_duration_record ( vm , days , hours * sign , minutes * sign , seconds * sign , milliseconds * sign , microseconds * sign , result_nanoseconds * sign ) ;
2021-09-06 21:21:57 +03:00
}
2022-03-10 16:52:25 +01:00
// 7.5.19 UnbalanceDurationRelative ( years, months, weeks, days, largestUnit, relativeTo ), https://tc39.es/proposal-temporal/#sec-temporal-unbalancedurationrelative
2023-01-26 15:38:21 +00:00
ThrowCompletionOr < DateDurationRecord > unbalance_duration_relative ( VM & vm , double years , double months , double weeks , double days , StringView largest_unit , Value relative_to )
2021-11-09 17:19:28 +00:00
{
2022-08-20 08:52:42 +01:00
auto & realm = * vm . current_realm ( ) ;
2021-11-09 17:19:28 +00:00
// 1. If largestUnit is "year", or years, months, weeks, and days are all 0, then
if ( largest_unit = = " year " sv | | ( years = = 0 & & months = = 0 & & weeks = = 0 & & days = = 0 ) ) {
2022-03-10 16:52:25 +01:00
// a. Return ! CreateDateDurationRecord(years, months, weeks, days).
return create_date_duration_record ( years , months , weeks , days ) ;
2021-11-09 17:19:28 +00:00
}
// 2. Let sign be ! DurationSign(years, months, weeks, days, 0, 0, 0, 0, 0, 0).
auto sign = duration_sign ( years , months , weeks , days , 0 , 0 , 0 , 0 , 0 , 0 ) ;
// 3. Assert: sign ≠ 0.
VERIFY ( sign ! = 0 ) ;
// 4. Let oneYear be ! CreateTemporalDuration(sign, 0, 0, 0, 0, 0, 0, 0, 0, 0).
2022-08-20 08:52:42 +01:00
auto * one_year = MUST ( create_temporal_duration ( vm , sign , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ) ) ;
2021-11-09 17:19:28 +00:00
// 5. Let oneMonth be ! CreateTemporalDuration(0, sign, 0, 0, 0, 0, 0, 0, 0, 0).
2022-08-20 08:52:42 +01:00
auto * one_month = MUST ( create_temporal_duration ( vm , 0 , sign , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ) ) ;
2021-11-09 17:19:28 +00:00
// 6. Let oneWeek be ! CreateTemporalDuration(0, 0, sign, 0, 0, 0, 0, 0, 0, 0).
2022-08-20 08:52:42 +01:00
auto * one_week = MUST ( create_temporal_duration ( vm , 0 , 0 , sign , 0 , 0 , 0 , 0 , 0 , 0 , 0 ) ) ;
2021-11-09 17:19:28 +00:00
Object * calendar ;
// 7. If relativeTo is not undefined, then
if ( ! relative_to . is_undefined ( ) ) {
2021-11-19 19:59:13 +00:00
// a. Set relativeTo to ? ToTemporalDate(relativeTo).
2022-08-20 08:52:42 +01:00
auto * relative_to_plain_date = TRY ( to_temporal_date ( vm , relative_to ) ) ;
2021-11-19 19:59:13 +00:00
relative_to = relative_to_plain_date ;
2021-11-09 17:19:28 +00:00
// b. Let calendar be relativeTo.[[Calendar]].
2021-11-19 19:59:13 +00:00
calendar = & relative_to_plain_date - > calendar ( ) ;
2021-11-09 17:19:28 +00:00
}
// 8. Else,
else {
// a. Let calendar be undefined.
calendar = nullptr ;
}
// 9. If largestUnit is "month", then
if ( largest_unit = = " month " sv ) {
// a. If calendar is undefined, then
if ( ! calendar ) {
// i. Throw a RangeError exception.
2022-08-16 20:33:17 +01:00
return vm . throw_completion < RangeError > ( ErrorType : : TemporalMissingStartingPoint , " months " ) ;
2021-11-09 17:19:28 +00:00
}
// b. Let dateAdd be ? GetMethod(calendar, "dateAdd").
2023-04-13 15:41:29 +02:00
auto date_add = TRY ( Value ( calendar ) . get_method ( vm , vm . names . dateAdd ) ) ;
2021-11-09 17:19:28 +00:00
// c. Let dateUntil be ? GetMethod(calendar, "dateUntil").
2023-04-13 15:41:29 +02:00
auto date_until = TRY ( Value ( calendar ) . get_method ( vm , vm . names . dateUntil ) ) ;
2021-11-09 17:19:28 +00:00
// d. Repeat, while years ≠ 0,
while ( years ! = 0 ) {
2022-04-06 23:56:57 +01:00
// i. Let newRelativeTo be ? CalendarDateAdd(calendar, relativeTo, oneYear, undefined, dateAdd).
2022-08-20 08:52:42 +01:00
auto * new_relative_to = TRY ( calendar_date_add ( vm , * calendar , relative_to , * one_year , nullptr , date_add ) ) ;
2021-11-09 17:19:28 +00:00
2022-04-06 23:56:57 +01:00
// ii. Let untilOptions be OrdinaryObjectCreate(null).
2022-12-13 20:49:50 +00:00
auto until_options = Object : : create ( realm , nullptr ) ;
2021-11-09 17:19:28 +00:00
2022-04-06 23:56:57 +01:00
// iii. Perform ! CreateDataPropertyOrThrow(untilOptions, "largestUnit", "month").
2023-08-08 18:25:57 +02:00
MUST ( until_options - > create_data_property_or_throw ( vm . names . largestUnit , PrimitiveString : : create ( vm , " month " _string ) ) ) ;
2021-11-09 17:19:28 +00:00
2022-04-06 23:56:57 +01:00
// iv. Let untilResult be ? CalendarDateUntil(calendar, relativeTo, newRelativeTo, untilOptions, dateUntil).
2022-08-20 08:52:42 +01:00
auto * until_result = TRY ( calendar_date_until ( vm , * calendar , relative_to , new_relative_to , * until_options , date_until ) ) ;
2021-11-09 17:19:28 +00:00
2022-04-06 23:56:57 +01:00
// v. Let oneYearMonths be untilResult.[[Months]].
2021-11-09 17:19:28 +00:00
auto one_year_months = until_result - > months ( ) ;
2022-04-06 23:56:57 +01:00
// vi. Set relativeTo to newRelativeTo.
2021-11-09 17:19:28 +00:00
relative_to = new_relative_to ;
2022-04-29 21:09:10 +02:00
// vii. Set years to years - sign.
2021-11-09 17:19:28 +00:00
years - = sign ;
2022-04-06 23:56:57 +01:00
// viii. Set months to months + oneYearMonths.
2021-11-09 17:19:28 +00:00
months + = one_year_months ;
}
}
// 10. Else if largestUnit is "week", then
else if ( largest_unit = = " week " sv ) {
// a. If calendar is undefined, then
if ( ! calendar ) {
// i. Throw a RangeError exception.
2022-08-16 20:33:17 +01:00
return vm . throw_completion < RangeError > ( ErrorType : : TemporalMissingStartingPoint , " weeks " ) ;
2021-11-09 17:19:28 +00:00
}
2022-10-16 11:46:39 +03:00
// b. Let dateAdd be ? GetMethod(calendar, "dateAdd").
2023-04-13 15:41:29 +02:00
auto date_add = TRY ( Value ( calendar ) . get_method ( vm , vm . names . dateAdd ) ) ;
2022-10-16 11:46:39 +03:00
// c. Repeat, while years ≠ 0,
2021-11-09 17:19:28 +00:00
while ( years ! = 0 ) {
2022-10-16 11:46:39 +03:00
// i. Let moveResult be ? MoveRelativeDate(calendar, relativeTo, oneYear, dateAdd).
auto move_result = TRY ( move_relative_date ( vm , * calendar , verify_cast < PlainDate > ( relative_to . as_object ( ) ) , * one_year , date_add ) ) ;
2021-11-09 17:19:28 +00:00
// ii. Set relativeTo to moveResult.[[RelativeTo]].
relative_to = move_result . relative_to . cell ( ) ;
// iii. Set days to days + moveResult.[[Days]].
days + = move_result . days ;
2022-04-29 21:09:10 +02:00
// iv. Set years to years - sign.
2021-11-09 17:19:28 +00:00
years - = sign ;
}
2022-10-16 11:46:39 +03:00
// d. Repeat, while months ≠ 0,
2021-11-09 17:19:28 +00:00
while ( months ! = 0 ) {
2022-10-16 11:46:39 +03:00
// i. Let moveResult be ? MoveRelativeDate(calendar, relativeTo, oneMonth, dateAdd).
auto move_result = TRY ( move_relative_date ( vm , * calendar , verify_cast < PlainDate > ( relative_to . as_object ( ) ) , * one_month , date_add ) ) ;
2021-11-09 17:19:28 +00:00
// ii. Set relativeTo to moveResult.[[RelativeTo]].
relative_to = move_result . relative_to . cell ( ) ;
// iii. Set days to days + moveResult.[[Days]].
days + = move_result . days ;
2022-04-29 21:09:10 +02:00
// iv. Set months to months - sign.
2021-11-09 17:19:28 +00:00
months - = sign ;
}
}
// 11. Else,
else {
// a. If any of years, months, and weeks are not zero, then
if ( years ! = 0 | | months ! = 0 | | weeks ! = 0 ) {
// i. If calendar is undefined, then
if ( ! calendar ) {
// i. Throw a RangeError exception.
2022-08-16 20:33:17 +01:00
return vm . throw_completion < RangeError > ( ErrorType : : TemporalMissingStartingPoint , " calendar units " ) ;
2021-11-09 17:19:28 +00:00
}
2022-10-16 11:46:39 +03:00
// ii. Let dateAdd be ? GetMethod(calendar, "dateAdd").
2023-04-13 15:41:29 +02:00
auto date_add = TRY ( Value ( calendar ) . get_method ( vm , vm . names . dateAdd ) ) ;
2022-10-16 11:46:39 +03:00
// iii. Repeat, while years ≠ 0,
2021-11-09 17:19:28 +00:00
while ( years ! = 0 ) {
2022-10-16 11:46:39 +03:00
// 1. Let moveResult be ? MoveRelativeDate(calendar, relativeTo, oneYear, dateAdd).
auto move_result = TRY ( move_relative_date ( vm , * calendar , verify_cast < PlainDate > ( relative_to . as_object ( ) ) , * one_year , date_add ) ) ;
2021-11-09 17:19:28 +00:00
// 2. Set relativeTo to moveResult.[[RelativeTo]].
relative_to = move_result . relative_to . cell ( ) ;
// 3. Set days to days + moveResult.[[Days]].
days + = move_result . days ;
2022-04-29 21:09:10 +02:00
// 4. Set years to years - sign.
2021-11-09 17:19:28 +00:00
years - = sign ;
}
2022-10-16 11:46:39 +03:00
// iv. Repeat, while months ≠ 0,
2021-11-09 17:19:28 +00:00
while ( months ! = 0 ) {
2022-10-16 11:46:39 +03:00
// 1. Let moveResult be ? MoveRelativeDate(calendar, relativeTo, oneMonth, dateAdd).
auto move_result = TRY ( move_relative_date ( vm , * calendar , verify_cast < PlainDate > ( relative_to . as_object ( ) ) , * one_month , date_add ) ) ;
2021-11-09 17:19:28 +00:00
// 2. Set relativeTo to moveResult.[[RelativeTo]].
relative_to = move_result . relative_to . cell ( ) ;
// 3. Set days to days +moveResult.[[Days]].
days + = move_result . days ;
2022-04-29 21:09:10 +02:00
// 4. Set months to months - sign.
2021-11-09 17:19:28 +00:00
months - = sign ;
}
2022-10-16 11:46:39 +03:00
// v. Repeat, while weeks ≠ 0,
2021-11-09 17:19:28 +00:00
while ( weeks ! = 0 ) {
2022-10-16 11:46:39 +03:00
// 1. Let moveResult be ? MoveRelativeDate(calendar, relativeTo, oneWeek, dateAdd).
auto move_result = TRY ( move_relative_date ( vm , * calendar , verify_cast < PlainDate > ( relative_to . as_object ( ) ) , * one_week , date_add ) ) ;
2021-11-09 17:19:28 +00:00
// 2. Set relativeTo to moveResult.[[RelativeTo]].
relative_to = move_result . relative_to . cell ( ) ;
// 3. Set days to days + moveResult.[[Days]].
days + = move_result . days ;
2022-04-29 21:09:10 +02:00
// 4. Set weeks to weeks - sign.
2021-11-09 17:19:28 +00:00
weeks - = sign ;
}
}
}
2022-06-15 00:33:21 +01:00
// 12. Return ? CreateDateDurationRecord(years, months, weeks, days).
2022-08-20 08:52:42 +01:00
return create_date_duration_record ( vm , years , months , weeks , days ) ;
2021-11-09 17:19:28 +00:00
}
2022-03-10 16:52:25 +01:00
// 7.5.20 BalanceDurationRelative ( years, months, weeks, days, largestUnit, relativeTo ), https://tc39.es/proposal-temporal/#sec-temporal-balancedurationrelative
2023-01-26 15:38:21 +00:00
ThrowCompletionOr < DateDurationRecord > balance_duration_relative ( VM & vm , double years , double months , double weeks , double days , StringView largest_unit , Value relative_to_value )
2021-11-23 23:14:19 +00:00
{
2022-08-20 08:52:42 +01:00
auto & realm = * vm . current_realm ( ) ;
2021-11-23 23:14:19 +00:00
// 1. If largestUnit is not one of "year", "month", or "week", or years, months, weeks, and days are all 0, then
if ( ! largest_unit . is_one_of ( " year " sv , " month " sv , " week " sv ) | | ( years = = 0 & & months = = 0 & & weeks = = 0 & & days = = 0 ) ) {
2022-03-10 16:52:25 +01:00
// a. Return ! CreateDateDurationRecord(years, months, weeks, days).
return create_date_duration_record ( years , months , weeks , days ) ;
2021-11-23 23:14:19 +00:00
}
2022-05-16 20:40:17 +01:00
// 2. If relativeTo is undefined, then
if ( relative_to_value . is_undefined ( ) ) {
// a. Throw a RangeError exception.
2022-08-16 20:33:17 +01:00
return vm . throw_completion < RangeError > ( ErrorType : : TemporalMissingStartingPoint , " calendar units " ) ;
2022-05-16 20:40:17 +01:00
}
2022-03-10 17:11:21 +01:00
// 3. Let sign be ! DurationSign(years, months, weeks, days, 0, 0, 0, 0, 0, 0).
2021-11-23 23:14:19 +00:00
auto sign = duration_sign ( years , months , weeks , days , 0 , 0 , 0 , 0 , 0 , 0 ) ;
2022-03-10 17:11:21 +01:00
// 4. Assert: sign ≠ 0.
2021-11-23 23:14:19 +00:00
VERIFY ( sign ! = 0 ) ;
2022-03-10 17:11:21 +01:00
// 5. Let oneYear be ! CreateTemporalDuration(sign, 0, 0, 0, 0, 0, 0, 0, 0, 0).
2022-08-20 08:52:42 +01:00
auto * one_year = MUST ( create_temporal_duration ( vm , sign , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ) ) ;
2021-11-23 23:14:19 +00:00
2022-03-10 17:11:21 +01:00
// 6. Let oneMonth be ! CreateTemporalDuration(0, sign, 0, 0, 0, 0, 0, 0, 0, 0).
2022-08-20 08:52:42 +01:00
auto * one_month = MUST ( create_temporal_duration ( vm , 0 , sign , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ) ) ;
2021-11-23 23:14:19 +00:00
2022-03-10 17:11:21 +01:00
// 7. Let oneWeek be ! CreateTemporalDuration(0, 0, sign, 0, 0, 0, 0, 0, 0, 0).
2022-08-20 08:52:42 +01:00
auto * one_week = MUST ( create_temporal_duration ( vm , 0 , 0 , sign , 0 , 0 , 0 , 0 , 0 , 0 , 0 ) ) ;
2021-11-23 23:14:19 +00:00
2022-03-10 17:11:21 +01:00
// 8. Set relativeTo to ? ToTemporalDate(relativeTo).
2022-08-20 08:52:42 +01:00
auto * relative_to = TRY ( to_temporal_date ( vm , relative_to_value ) ) ;
2021-11-23 23:14:19 +00:00
2022-03-10 17:11:21 +01:00
// 9. Let calendar be relativeTo.[[Calendar]].
2021-11-23 23:14:19 +00:00
auto & calendar = relative_to - > calendar ( ) ;
2022-03-10 17:11:21 +01:00
// 10. If largestUnit is "year", then
2021-11-23 23:14:19 +00:00
if ( largest_unit = = " year " sv ) {
2022-10-16 11:46:39 +03:00
// a. Let dateAdd be ? GetMethod(calendar, "dateAdd").
2023-04-13 15:41:29 +02:00
auto date_add = TRY ( Value ( & calendar ) . get_method ( vm , vm . names . dateAdd ) ) ;
2022-10-16 11:46:39 +03:00
// b. Let moveResult be ? MoveRelativeDate(calendar, relativeTo, oneYear, dateAdd).
auto move_result = TRY ( move_relative_date ( vm , calendar , * relative_to , * one_year , date_add ) ) ;
2021-11-23 23:14:19 +00:00
2022-10-16 11:46:39 +03:00
// c. Let newRelativeTo be moveResult.[[RelativeTo]].
2022-08-03 22:34:07 +01:00
auto * new_relative_to = move_result . relative_to . cell ( ) ;
2021-11-23 23:14:19 +00:00
2022-10-16 11:46:39 +03:00
// d. Let oneYearDays be moveResult.[[Days]].
2021-11-23 23:14:19 +00:00
auto one_year_days = move_result . days ;
2022-10-16 11:46:39 +03:00
// e. Repeat, while abs(days) ≥ abs(oneYearDays),
2021-11-23 23:14:19 +00:00
while ( fabs ( days ) > = fabs ( one_year_days ) ) {
2022-04-29 21:09:10 +02:00
// i. Set days to days - oneYearDays.
2021-11-23 23:14:19 +00:00
days - = one_year_days ;
// ii. Set years to years + sign.
years + = sign ;
2022-08-03 22:34:07 +01:00
// iii. Set relativeTo to newRelativeTo.
relative_to = new_relative_to ;
2022-10-16 11:46:39 +03:00
// iv. Set moveResult to ? MoveRelativeDate(calendar, relativeTo, oneYear, dateAdd).
move_result = TRY ( move_relative_date ( vm , calendar , * relative_to , * one_year , date_add ) ) ;
2021-11-23 23:14:19 +00:00
2022-08-03 22:34:07 +01:00
// v. Set newRelativeTo to moveResult.[[RelativeTo]].
new_relative_to = move_result . relative_to . cell ( ) ;
2021-11-23 23:14:19 +00:00
2022-08-03 22:34:07 +01:00
// vi. Set oneYearDays to moveResult.[[Days]].
2021-11-23 23:14:19 +00:00
one_year_days = move_result . days ;
}
2022-10-16 11:46:39 +03:00
// f. Set moveResult to ? MoveRelativeDate(calendar, relativeTo, oneMonth, dateAdd).
move_result = TRY ( move_relative_date ( vm , calendar , * relative_to , * one_month , date_add ) ) ;
2021-11-23 23:14:19 +00:00
2022-10-16 11:46:39 +03:00
// g. Set newRelativeTo to moveResult.[[RelativeTo]].
2022-08-03 22:34:07 +01:00
new_relative_to = move_result . relative_to . cell ( ) ;
2021-11-23 23:14:19 +00:00
2022-10-16 11:46:39 +03:00
// h. Let oneMonthDays be moveResult.[[Days]].
2021-11-23 23:14:19 +00:00
auto one_month_days = move_result . days ;
2022-10-16 11:46:39 +03:00
// i. Repeat, while abs(days) ≥ abs(oneMonthDays),
2021-11-23 23:14:19 +00:00
while ( fabs ( days ) > = fabs ( one_month_days ) ) {
2022-04-29 21:09:10 +02:00
// i. Set days to days - oneMonthDays.
2021-11-23 23:14:19 +00:00
days - = one_month_days ;
// ii. Set months to months + sign.
months + = sign ;
2022-08-03 22:34:07 +01:00
// iii. Set relativeTo to newRelativeTo.
relative_to = new_relative_to ;
2022-10-16 11:46:39 +03:00
// iv. Set moveResult to ? MoveRelativeDate(calendar, relativeTo, oneMonth, dateAdd).
move_result = TRY ( move_relative_date ( vm , calendar , * relative_to , * one_month , date_add ) ) ;
2021-11-23 23:14:19 +00:00
2022-08-03 22:34:07 +01:00
// v. Set newRelativeTo to moveResult.[[RelativeTo]].
new_relative_to = move_result . relative_to . cell ( ) ;
2021-11-23 23:14:19 +00:00
2022-08-03 22:34:07 +01:00
// vi. Set oneMonthDays to moveResult.[[Days]].
2021-11-23 23:14:19 +00:00
one_month_days = move_result . days ;
}
2022-08-03 22:34:07 +01:00
// j. Set newRelativeTo to ? CalendarDateAdd(calendar, relativeTo, oneYear, undefined, dateAdd).
2022-08-20 08:52:42 +01:00
new_relative_to = TRY ( calendar_date_add ( vm , calendar , relative_to , * one_year , nullptr , date_add ) ) ;
2021-11-23 23:14:19 +00:00
2022-04-06 23:56:57 +01:00
// k. Let dateUntil be ? GetMethod(calendar, "dateUntil").
2023-04-13 15:41:29 +02:00
auto date_until = TRY ( Value ( & calendar ) . get_method ( vm , vm . names . dateUntil ) ) ;
2021-11-23 23:14:19 +00:00
2022-04-06 23:56:57 +01:00
// l. Let untilOptions be OrdinaryObjectCreate(null).
2022-12-13 20:49:50 +00:00
auto until_options = Object : : create ( realm , nullptr ) ;
2021-11-23 23:14:19 +00:00
2022-04-06 23:56:57 +01:00
// m. Perform ! CreateDataPropertyOrThrow(untilOptions, "largestUnit", "month").
2023-08-08 18:25:57 +02:00
MUST ( until_options - > create_data_property_or_throw ( vm . names . largestUnit , PrimitiveString : : create ( vm , " month " _string ) ) ) ;
2021-11-23 23:14:19 +00:00
2022-04-06 23:56:57 +01:00
// n. Let untilResult be ? CalendarDateUntil(calendar, relativeTo, newRelativeTo, untilOptions, dateUntil).
2022-08-20 08:52:42 +01:00
auto * until_result = TRY ( calendar_date_until ( vm , calendar , relative_to , new_relative_to , * until_options , date_until ) ) ;
2021-11-23 23:14:19 +00:00
2022-04-06 23:56:57 +01:00
// o. Let oneYearMonths be untilResult.[[Months]].
2021-11-23 23:14:19 +00:00
auto one_year_months = until_result - > months ( ) ;
2022-04-06 23:56:57 +01:00
// p. Repeat, while abs(months) ≥ abs(oneYearMonths),
2021-11-23 23:14:19 +00:00
while ( fabs ( months ) > = fabs ( one_year_months ) ) {
2022-04-29 21:09:10 +02:00
// i. Set months to months - oneYearMonths.
2021-11-23 23:14:19 +00:00
months - = one_year_months ;
// ii. Set years to years + sign.
years + = sign ;
// iii. Set relativeTo to newRelativeTo.
relative_to = new_relative_to ;
2022-04-06 23:56:57 +01:00
// iv. Set newRelativeTo to ? CalendarDateAdd(calendar, relativeTo, oneYear, undefined, dateAdd).
2022-08-20 08:52:42 +01:00
new_relative_to = TRY ( calendar_date_add ( vm , calendar , relative_to , * one_year , nullptr , date_add ) ) ;
2021-11-23 23:14:19 +00:00
2022-04-06 23:56:57 +01:00
// v. Set untilOptions to OrdinaryObjectCreate(null).
2022-08-16 00:20:49 +01:00
until_options = Object : : create ( realm , nullptr ) ;
2021-11-23 23:14:19 +00:00
2022-04-06 23:56:57 +01:00
// vi. Perform ! CreateDataPropertyOrThrow(untilOptions, "largestUnit", "month").
2023-08-08 18:25:57 +02:00
MUST ( until_options - > create_data_property_or_throw ( vm . names . largestUnit , PrimitiveString : : create ( vm , " month " _string ) ) ) ;
2021-11-23 23:14:19 +00:00
2022-04-06 23:56:57 +01:00
// vii. Set untilResult to ? CalendarDateUntil(calendar, relativeTo, newRelativeTo, untilOptions, dateUntil).
2022-08-20 08:52:42 +01:00
until_result = TRY ( calendar_date_until ( vm , calendar , relative_to , new_relative_to , * until_options , date_until ) ) ;
2021-11-23 23:14:19 +00:00
2022-04-06 23:56:57 +01:00
// viii. Set oneYearMonths to untilResult.[[Months]].
2021-11-23 23:14:19 +00:00
one_year_months = until_result - > months ( ) ;
}
}
2022-03-10 17:11:21 +01:00
// 11. Else if largestUnit is "month", then
2021-11-23 23:14:19 +00:00
else if ( largest_unit = = " month " sv ) {
2022-10-16 11:46:39 +03:00
// a. Let dateAdd be ? GetMethod(calendar, "dateAdd").
2023-04-13 15:41:29 +02:00
auto date_add = TRY ( Value ( & calendar ) . get_method ( vm , vm . names . dateAdd ) ) ;
2022-10-16 11:46:39 +03:00
// b. Let moveResult be ? MoveRelativeDate(calendar, relativeTo, oneMonth, dateAdd).
auto move_result = TRY ( move_relative_date ( vm , calendar , * relative_to , * one_month , date_add ) ) ;
2021-11-23 23:14:19 +00:00
2022-10-16 11:46:39 +03:00
// c. Let newRelativeTo be moveResult.[[RelativeTo]].
2022-08-03 22:34:07 +01:00
auto * new_relative_to = move_result . relative_to . cell ( ) ;
2021-11-23 23:14:19 +00:00
2022-10-16 11:46:39 +03:00
// d. Let oneMonthDays be moveResult.[[Days]].
2021-11-23 23:14:19 +00:00
auto one_month_days = move_result . days ;
2022-10-16 11:46:39 +03:00
// e. Repeat, while abs(days) ≥ abs(oneMonthDays),
2021-11-23 23:14:19 +00:00
while ( fabs ( days ) > = fabs ( one_month_days ) ) {
2022-04-29 21:09:10 +02:00
// i. Set days to days - oneMonthDays.
2021-11-23 23:14:19 +00:00
days - = one_month_days ;
// ii. Set months to months + sign.
months + = sign ;
2022-08-03 22:34:07 +01:00
// iii. Set relativeTo to newRelativeTo.
relative_to = new_relative_to ;
2022-10-16 11:46:39 +03:00
// iv. Set moveResult to ? MoveRelativeDate(calendar, relativeTo, oneMonth, dateAdd).
move_result = TRY ( move_relative_date ( vm , calendar , * relative_to , * one_month , date_add ) ) ;
2021-11-23 23:14:19 +00:00
2022-08-03 22:34:07 +01:00
// v. Set newRelativeTo to moveResult.[[RelativeTo]].
new_relative_to = move_result . relative_to . cell ( ) ;
2021-11-23 23:14:19 +00:00
2022-08-03 22:34:07 +01:00
// vi. Set oneMonthDays to moveResult.[[Days]].
2021-11-23 23:14:19 +00:00
one_month_days = move_result . days ;
}
}
2022-03-10 17:11:21 +01:00
// 12. Else,
2021-11-23 23:14:19 +00:00
else {
// a. Assert: largestUnit is "week".
VERIFY ( largest_unit = = " week " sv ) ;
2022-10-16 11:46:39 +03:00
// b. Let dateAdd be ? GetMethod(calendar, "dateAdd").
2023-04-13 15:41:29 +02:00
auto date_add = TRY ( Value ( & calendar ) . get_method ( vm , vm . names . dateAdd ) ) ;
2022-10-16 11:46:39 +03:00
// c. Let moveResult be ? MoveRelativeDate(calendar, relativeTo, oneWeek, dateAdd).
auto move_result = TRY ( move_relative_date ( vm , calendar , * relative_to , * one_week , date_add ) ) ;
2021-11-23 23:14:19 +00:00
2022-10-16 11:46:39 +03:00
// d. Let newRelativeTo be moveResult.[[RelativeTo]].
2022-08-03 22:34:07 +01:00
auto * new_relative_to = move_result . relative_to . cell ( ) ;
2021-11-23 23:14:19 +00:00
2022-10-16 11:46:39 +03:00
// e. Let oneWeekDays be moveResult.[[Days]].
2021-11-23 23:14:19 +00:00
auto one_week_days = move_result . days ;
2022-10-16 11:46:39 +03:00
// f. Repeat, while abs(days) ≥ abs(oneWeekDays),
2021-11-23 23:14:19 +00:00
while ( fabs ( days ) > = fabs ( one_week_days ) ) {
2022-04-29 21:09:10 +02:00
// i. Set days to days - oneWeekDays.
2021-11-23 23:14:19 +00:00
days - = one_week_days ;
// ii. Set weeks to weeks + sign.
weeks + = sign ;
2022-08-03 22:34:07 +01:00
// iii. Set relativeTo to newRelativeTo.
relative_to = new_relative_to ;
2022-10-16 11:46:39 +03:00
// iv. Set moveResult to ? MoveRelativeDate(calendar, relativeTo, oneWeek, dateAdd).
move_result = TRY ( move_relative_date ( vm , calendar , * relative_to , * one_week , date_add ) ) ;
2021-11-23 23:14:19 +00:00
2022-08-03 22:34:07 +01:00
// v. Set newRelativeTo to moveResult.[[RelativeTo]].
new_relative_to = move_result . relative_to . cell ( ) ;
2021-11-23 23:14:19 +00:00
2022-08-03 22:34:07 +01:00
// vi. Set oneWeekDays to moveResult.[[Days]].
2021-11-23 23:14:19 +00:00
one_week_days = move_result . days ;
}
}
2022-03-10 17:11:21 +01:00
// 13. Return ! CreateDateDurationRecord(years, months, weeks, days).
2022-03-10 16:52:25 +01:00
return create_date_duration_record ( years , months , weeks , days ) ;
2021-11-23 23:14:19 +00:00
}
2022-03-10 16:52:25 +01:00
// 7.5.21 AddDuration ( y1, mon1, w1, d1, h1, min1, s1, ms1, mus1, ns1, y2, mon2, w2, d2, h2, min2, s2, ms2, mus2, ns2, relativeTo ), https://tc39.es/proposal-temporal/#sec-temporal-addduration
2022-08-20 08:52:42 +01:00
ThrowCompletionOr < DurationRecord > add_duration ( VM & vm , double years1 , double months1 , double weeks1 , double days1 , double hours1 , double minutes1 , double seconds1 , double milliseconds1 , double microseconds1 , double nanoseconds1 , double years2 , double months2 , double weeks2 , double days2 , double hours2 , double minutes2 , double seconds2 , double milliseconds2 , double microseconds2 , double nanoseconds2 , Value relative_to_value )
2021-11-23 23:10:53 +00:00
{
2022-08-20 08:52:42 +01:00
auto & realm = * vm . current_realm ( ) ;
2021-11-23 23:10:53 +00:00
2022-03-09 23:51:53 +01:00
VERIFY ( all_of ( AK : : Array { years1 , months1 , weeks1 , days1 , hours1 , minutes1 , seconds1 , milliseconds1 , microseconds1 , nanoseconds1 , years2 , months2 , weeks2 , days2 , hours2 , minutes2 , seconds2 , milliseconds2 , microseconds2 , nanoseconds2 } , [ ] ( auto value ) { return value = = trunc ( value ) ; } ) ) ;
2021-11-23 23:10:53 +00:00
2022-03-09 23:51:53 +01:00
// 1. Let largestUnit1 be ! DefaultTemporalLargestUnit(y1, mon1, w1, d1, h1, min1, s1, ms1, mus1).
2021-11-23 23:10:53 +00:00
auto largest_unit1 = default_temporal_largest_unit ( years1 , months1 , weeks1 , days1 , hours1 , minutes1 , seconds1 , milliseconds1 , microseconds1 ) ;
2022-03-09 23:51:53 +01:00
// 2. Let largestUnit2 be ! DefaultTemporalLargestUnit(y2, mon2, w2, d2, h2, min2, s2, ms2, mus2).
2021-11-23 23:10:53 +00:00
auto largest_unit2 = default_temporal_largest_unit ( years2 , months2 , weeks2 , days2 , hours2 , minutes2 , seconds2 , milliseconds2 , microseconds2 ) ;
2022-03-09 23:51:53 +01:00
// 3. Let largestUnit be ! LargerOfTwoTemporalUnits(largestUnit1, largestUnit2).
2021-11-23 23:10:53 +00:00
auto largest_unit = larger_of_two_temporal_units ( largest_unit1 , largest_unit2 ) ;
2022-03-09 23:51:53 +01:00
// 4. If relativeTo is undefined, then
2021-11-23 23:10:53 +00:00
if ( relative_to_value . is_undefined ( ) ) {
// a. If largestUnit is one of "year", "month", or "week", then
if ( largest_unit . is_one_of ( " year " sv , " month " sv , " week " sv ) ) {
// i. Throw a RangeError exception.
2022-08-16 20:33:17 +01:00
return vm . throw_completion < RangeError > ( ErrorType : : TemporalMissingStartingPoint , " year, month or week " ) ;
2021-11-23 23:10:53 +00:00
}
2022-05-07 16:26:32 +02:00
// b. Let result be ? BalanceDuration(d1 + d2, h1 + h2, min1 + min2, s1 + s2, ms1 + ms2, mus1 + mus2, ns1 + ns2, largestUnit).
2022-10-13 23:39:37 +02:00
// NOTE: Nanoseconds is the only one that can overflow the safe integer range of a double
// so we have to check for that case.
Crypto : : SignedBigInteger sum_of_nano_seconds ;
if ( fabs ( nanoseconds1 + nanoseconds2 ) > = MAX_ARRAY_LIKE_INDEX )
sum_of_nano_seconds = Crypto : : SignedBigInteger { nanoseconds1 } . plus ( Crypto : : SignedBigInteger { nanoseconds2 } ) ;
else
sum_of_nano_seconds = Crypto : : SignedBigInteger { nanoseconds1 + nanoseconds2 } ;
auto result = TRY ( balance_duration ( vm , days1 + days2 , hours1 + hours2 , minutes1 + minutes2 , seconds1 + seconds2 , milliseconds1 + milliseconds2 , microseconds1 + microseconds2 , sum_of_nano_seconds , largest_unit ) ) ;
2021-11-23 23:10:53 +00:00
2022-05-07 16:26:32 +02:00
// c. Return ! CreateDurationRecord(0, 0, 0, result.[[Days]], result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]]).
2022-08-20 08:52:42 +01:00
return MUST ( create_duration_record ( vm , 0 , 0 , 0 , result . days , result . hours , result . minutes , result . seconds , result . milliseconds , result . microseconds , result . nanoseconds ) ) ;
2021-11-23 23:10:53 +00:00
}
2022-03-10 16:52:25 +01:00
// 5. If relativeTo has an [[InitializedTemporalDate]] internal slot, then
if ( is < PlainDate > ( relative_to_value . as_object ( ) ) ) {
2021-11-23 23:10:53 +00:00
auto & relative_to = static_cast < PlainDate & > ( relative_to_value . as_object ( ) ) ;
// a. Let calendar be relativeTo.[[Calendar]].
auto & calendar = relative_to . calendar ( ) ;
2022-03-10 18:32:23 +01:00
// b. Let dateDuration1 be ! CreateTemporalDuration(y1, mon1, w1, d1, 0, 0, 0, 0, 0, 0).
2022-08-20 08:52:42 +01:00
auto * date_duration1 = MUST ( create_temporal_duration ( vm , years1 , months1 , weeks1 , days1 , 0 , 0 , 0 , 0 , 0 , 0 ) ) ;
2021-11-23 23:10:53 +00:00
2022-03-10 18:32:23 +01:00
// c. Let dateDuration2 be ! CreateTemporalDuration(y2, mon2, w2, d2, 0, 0, 0, 0, 0, 0).
2022-08-20 08:52:42 +01:00
auto * date_duration2 = MUST ( create_temporal_duration ( vm , years2 , months2 , weeks2 , days2 , 0 , 0 , 0 , 0 , 0 , 0 ) ) ;
2021-11-23 23:10:53 +00:00
// d. Let dateAdd be ? GetMethod(calendar, "dateAdd").
2023-04-13 15:41:29 +02:00
auto date_add = TRY ( Value ( & calendar ) . get_method ( vm , vm . names . dateAdd ) ) ;
2021-11-23 23:10:53 +00:00
2022-04-06 23:56:57 +01:00
// e. Let intermediate be ? CalendarDateAdd(calendar, relativeTo, dateDuration1, undefined, dateAdd).
2022-08-20 08:52:42 +01:00
auto * intermediate = TRY ( calendar_date_add ( vm , calendar , & relative_to , * date_duration1 , nullptr , date_add ) ) ;
2021-11-23 23:10:53 +00:00
2022-04-06 23:56:57 +01:00
// f. Let end be ? CalendarDateAdd(calendar, intermediate, dateDuration2, undefined, dateAdd).
2022-08-20 08:52:42 +01:00
auto * end = TRY ( calendar_date_add ( vm , calendar , intermediate , * date_duration2 , nullptr , date_add ) ) ;
2021-11-23 23:10:53 +00:00
2022-04-06 23:56:57 +01:00
// g. Let dateLargestUnit be ! LargerOfTwoTemporalUnits("day", largestUnit).
2021-11-23 23:10:53 +00:00
auto date_largest_unit = larger_of_two_temporal_units ( " day " sv , largest_unit ) ;
2022-04-06 23:56:57 +01:00
// h. Let differenceOptions be OrdinaryObjectCreate(null).
2022-12-13 20:49:50 +00:00
auto difference_options = Object : : create ( realm , nullptr ) ;
2021-11-23 23:10:53 +00:00
2022-04-06 23:56:57 +01:00
// i. Perform ! CreateDataPropertyOrThrow(differenceOptions, "largestUnit", dateLargestUnit).
2023-08-08 18:25:57 +02:00
MUST ( difference_options - > create_data_property_or_throw ( vm . names . largestUnit , PrimitiveString : : create ( vm , date_largest_unit ) ) ) ;
2021-11-23 23:10:53 +00:00
2022-04-06 23:56:57 +01:00
// j. Let dateDifference be ? CalendarDateUntil(calendar, relativeTo, end, differenceOptions).
2022-08-20 08:52:42 +01:00
auto * date_difference = TRY ( calendar_date_until ( vm , calendar , & relative_to , end , * difference_options ) ) ;
2021-11-23 23:10:53 +00:00
2022-05-07 16:26:32 +02:00
// k. Let result be ? BalanceDuration(dateDifference.[[Days]], h1 + h2, min1 + min2, s1 + s2, ms1 + ms2, mus1 + mus2, ns1 + ns2, largestUnit).
2022-10-13 23:39:37 +02:00
// NOTE: Nanoseconds is the only one that can overflow the safe integer range of a double
// so we have to check for that case.
Crypto : : SignedBigInteger sum_of_nano_seconds ;
if ( fabs ( nanoseconds1 + nanoseconds2 ) > = MAX_ARRAY_LIKE_INDEX )
sum_of_nano_seconds = Crypto : : SignedBigInteger { nanoseconds1 } . plus ( Crypto : : SignedBigInteger { nanoseconds2 } ) ;
else
sum_of_nano_seconds = Crypto : : SignedBigInteger { nanoseconds1 + nanoseconds2 } ;
auto result = TRY ( balance_duration ( vm , date_difference - > days ( ) , hours1 + hours2 , minutes1 + minutes2 , seconds1 + seconds2 , milliseconds1 + milliseconds2 , microseconds1 + microseconds2 , sum_of_nano_seconds , largest_unit ) ) ;
2021-11-23 23:10:53 +00:00
2022-04-06 23:56:57 +01:00
// l. Return ? CreateDurationRecord(dateDifference.[[Years]], dateDifference.[[Months]], dateDifference.[[Weeks]], result.[[Days]], result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]]).
2022-08-20 08:52:42 +01:00
return MUST ( create_duration_record ( vm , date_difference - > years ( ) , date_difference - > months ( ) , date_difference - > weeks ( ) , result . days , result . hours , result . minutes , result . seconds , result . milliseconds , result . microseconds , result . nanoseconds ) ) ;
2021-11-23 23:10:53 +00:00
}
2022-03-10 16:52:25 +01:00
// 6. Assert: relativeTo has an [[InitializedTemporalZonedDateTime]] internal slot.
auto & relative_to = verify_cast < ZonedDateTime > ( relative_to_value . as_object ( ) ) ;
2021-11-23 23:10:53 +00:00
2022-03-10 16:52:25 +01:00
// 7. Let timeZone be relativeTo.[[TimeZone]].
auto & time_zone = relative_to . time_zone ( ) ;
2021-11-23 23:10:53 +00:00
2022-03-10 16:52:25 +01:00
// 8. Let calendar be relativeTo.[[Calendar]].
auto & calendar = relative_to . calendar ( ) ;
2021-11-23 23:10:53 +00:00
2022-03-10 16:52:25 +01:00
// 9. Let intermediateNs be ? AddZonedDateTime(relativeTo.[[Nanoseconds]], timeZone, calendar, y1, mon1, w1, d1, h1, min1, s1, ms1, mus1, ns1).
2022-08-20 08:52:42 +01:00
auto * intermediate_ns = TRY ( add_zoned_date_time ( vm , relative_to . nanoseconds ( ) , & time_zone , calendar , years1 , months1 , weeks1 , days1 , hours1 , minutes1 , seconds1 , milliseconds1 , microseconds1 , nanoseconds1 ) ) ;
2021-11-23 23:10:53 +00:00
2022-03-10 16:52:25 +01:00
// 10. Let endNs be ? AddZonedDateTime(intermediateNs, timeZone, calendar, y2, mon2, w2, d2, h2, min2, s2, ms2, mus2, ns2).
2022-08-20 08:52:42 +01:00
auto * end_ns = TRY ( add_zoned_date_time ( vm , * intermediate_ns , & time_zone , calendar , years2 , months2 , weeks2 , days2 , hours2 , minutes2 , seconds2 , milliseconds2 , microseconds2 , nanoseconds2 ) ) ;
2021-11-23 23:10:53 +00:00
2022-03-10 16:52:25 +01:00
// 11. If largestUnit is not one of "year", "month", "week", or "day", then
if ( ! largest_unit . is_one_of ( " year " sv , " month " sv , " week " sv , " day " sv ) ) {
// a. Let diffNs be ! DifferenceInstant(relativeTo.[[Nanoseconds]], endNs, 1, "nanosecond", "halfExpand").
2022-08-20 08:52:42 +01:00
auto * diff_ns = difference_instant ( vm , relative_to . nanoseconds ( ) , * end_ns , 1 , " nanosecond " sv , " halfExpand " sv ) ;
2021-11-23 23:10:53 +00: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(diffNs) ≤ 2 × nsMaxInstant.
// c. Let result be ! BalanceDuration(0, 0, 0, 0, 0, 0, diffNs, largestUnit).
2022-08-20 08:52:42 +01:00
auto result = MUST ( balance_duration ( vm , 0 , 0 , 0 , 0 , 0 , 0 , diff_ns - > big_integer ( ) , largest_unit ) ) ;
2021-11-23 23:10:53 +00:00
2022-05-09 20:27:20 +02:00
// d. Return ? CreateDurationRecord(0, 0, 0, 0, result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]]).
2022-08-20 08:52:42 +01:00
return create_duration_record ( vm , 0 , 0 , 0 , 0 , result . hours , result . minutes , result . seconds , result . milliseconds , result . microseconds , result . nanoseconds ) ;
2021-11-23 23:10:53 +00:00
}
2022-06-15 00:28:03 +01:00
// 12. Return ? DifferenceZonedDateTime(relativeTo.[[Nanoseconds]], endNs, timeZone, calendar, largestUnit, OrdinaryObjectCreate(null)).
2022-08-20 08:52:42 +01:00
return difference_zoned_date_time ( vm , relative_to . nanoseconds ( ) , * end_ns , time_zone , calendar , largest_unit , * Object : : create ( realm , nullptr ) ) ;
2021-11-23 23:10:53 +00:00
}
2022-10-16 11:48:58 +03:00
// 7.5.23 MoveRelativeDate ( calendar, relativeTo, duration, dateAdd ), https://tc39.es/proposal-temporal/#sec-temporal-moverelativedate
2022-10-16 11:33:42 +03:00
ThrowCompletionOr < MoveRelativeDateResult > move_relative_date ( VM & vm , Object & calendar , PlainDate & relative_to , Duration & duration , FunctionObject * date_add )
2021-11-07 01:41:52 +00:00
{
2022-10-16 11:48:58 +03:00
// 1. Let newDate be ? CalendarDateAdd(calendar, relativeTo, duration, undefined, dateAdd)
2022-10-16 11:33:42 +03:00
auto * new_date = TRY ( calendar_date_add ( vm , calendar , & relative_to , duration , nullptr , date_add ) ) ;
2022-10-16 11:48:58 +03:00
// 2. Let days be DaysUntil(relativeTo, newDate).
2022-05-06 21:03:49 +02:00
auto days = days_until ( relative_to , * new_date ) ;
2021-11-07 01:41:52 +00:00
2022-10-16 11:48:58 +03:00
// 3. Return the Record { [[RelativeTo]]: newDate, [[Days]]: days }.
2021-11-19 19:59:13 +00:00
return MoveRelativeDateResult { . relative_to = make_handle ( new_date ) , . days = days } ;
2021-11-07 01:41:52 +00:00
}
2022-03-10 16:52:25 +01:00
// 7.5.24 MoveRelativeZonedDateTime ( zonedDateTime, years, months, weeks, days ), https://tc39.es/proposal-temporal/#sec-temporal-moverelativezoneddatetime
2022-08-20 08:52:42 +01:00
ThrowCompletionOr < ZonedDateTime * > move_relative_zoned_date_time ( VM & vm , ZonedDateTime & zoned_date_time , double years , double months , double weeks , double days )
2021-11-07 01:41:52 +00:00
{
// 1. Let intermediateNs be ? AddZonedDateTime(zonedDateTime.[[Nanoseconds]], zonedDateTime.[[TimeZone]], zonedDateTime.[[Calendar]], years, months, weeks, days, 0, 0, 0, 0, 0, 0).
2022-08-20 08:52:42 +01:00
auto * intermediate_ns = TRY ( add_zoned_date_time ( vm , zoned_date_time . nanoseconds ( ) , & zoned_date_time . time_zone ( ) , zoned_date_time . calendar ( ) , years , months , weeks , days , 0 , 0 , 0 , 0 , 0 , 0 ) ) ;
2021-11-07 01:41:52 +00:00
// 2. Return ! CreateTemporalZonedDateTime(intermediateNs, zonedDateTime.[[TimeZone]], zonedDateTime.[[Calendar]]).
2022-08-20 08:52:42 +01:00
return MUST ( create_temporal_zoned_date_time ( vm , * intermediate_ns , zoned_date_time . time_zone ( ) , zoned_date_time . calendar ( ) ) ) ;
2021-11-07 01:41:52 +00:00
}
2022-03-10 16:52:25 +01:00
// 7.5.25 RoundDuration ( years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, increment, unit, roundingMode [ , relativeTo ] ), https://tc39.es/proposal-temporal/#sec-temporal-roundduration
2022-08-20 08:52:42 +01:00
ThrowCompletionOr < RoundedDuration > round_duration ( VM & vm , double years , double months , double weeks , double days , double hours , double minutes , double seconds , double milliseconds , double microseconds , double nanoseconds , u32 increment , StringView unit , StringView rounding_mode , Object * relative_to_object )
2021-11-07 01:41:52 +00:00
{
2022-08-20 08:52:42 +01:00
auto & realm = * vm . current_realm ( ) ;
2021-11-07 01:41:52 +00:00
Object * calendar = nullptr ;
double fractional_seconds = 0 ;
// 1. If relativeTo is not present, set relativeTo to undefined.
2021-11-19 19:59:13 +00:00
// NOTE: `relative_to_object` and `relative_to` in the various code paths below are all the same as far as the
// spec is concerned, but the latter is more strictly typed for convenience.
PlainDate * relative_to = nullptr ;
2021-11-07 01:41:52 +00:00
2021-11-13 17:38:00 +00:00
// FIXME: assuming "smallestUnit" as the option name here leads to confusing error messages in some cases:
// > new Temporal.Duration().total({ unit: "month" })
// Uncaught exception: [RangeError] month is not a valid value for option smallestUnit
2022-05-07 16:26:32 +02:00
// 2. If unit is "year", "month", or "week", and relativeTo is undefined, then
2021-11-07 01:41:52 +00:00
if ( unit . is_one_of ( " year " sv , " month " sv , " week " sv ) & & ! relative_to_object ) {
// a. Throw a RangeError exception.
2022-08-16 20:33:17 +01:00
return vm . throw_completion < RangeError > ( ErrorType : : OptionIsNotValidValue , unit , " smallestUnit " sv ) ;
2021-11-07 01:41:52 +00:00
}
2022-05-07 16:26:32 +02:00
// 3. Let zonedRelativeTo be undefined.
2021-11-07 01:41:52 +00:00
ZonedDateTime * zoned_relative_to = nullptr ;
2022-05-07 16:26:32 +02:00
// 4. If relativeTo is not undefined, then
2021-11-07 01:41:52 +00:00
if ( relative_to_object ) {
// a. If relativeTo has an [[InitializedTemporalZonedDateTime]] internal slot, then
if ( is < ZonedDateTime > ( relative_to_object ) ) {
auto * relative_to_zoned_date_time = static_cast < ZonedDateTime * > ( relative_to_object ) ;
2022-03-10 00:39:21 +01:00
// i. Set zonedRelativeTo to relativeTo.
2021-11-07 01:41:52 +00:00
zoned_relative_to = relative_to_zoned_date_time ;
2022-03-10 00:39:21 +01:00
// ii. Set relativeTo to ? ToTemporalDate(relativeTo).
2022-08-20 08:52:42 +01:00
relative_to = TRY ( to_temporal_date ( vm , relative_to_object ) ) ;
2021-11-07 01:41:52 +00:00
}
// b. Else,
else {
2022-03-10 00:39:21 +01:00
// i. Assert: relativeTo has an [[InitializedTemporalDate]] internal slot.
2021-11-19 19:59:13 +00:00
VERIFY ( is < PlainDate > ( relative_to_object ) ) ;
2021-11-07 01:41:52 +00:00
2021-11-19 19:59:13 +00:00
relative_to = static_cast < PlainDate * > ( relative_to_object ) ;
2021-11-07 01:41:52 +00:00
}
// c. Let calendar be relativeTo.[[Calendar]].
calendar = & relative_to - > calendar ( ) ;
}
2022-05-07 16:26:32 +02:00
// 5. Else,
2022-03-10 00:39:21 +01:00
// a. NOTE: calendar will not be used below.
2021-11-07 01:41:52 +00:00
2022-05-07 16:26:32 +02:00
// 6. If unit is one of "year", "month", "week", or "day", then
2021-11-07 01:41:52 +00:00
if ( unit . is_one_of ( " year " sv , " month " sv , " week " sv , " day " sv ) ) {
// a. Let nanoseconds be ! TotalDurationNanoseconds(0, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, 0).
2022-08-26 00:49:50 +02:00
auto nanoseconds_bigint = total_duration_nanoseconds ( 0 , hours , minutes , seconds , milliseconds , microseconds , Crypto : : SignedBigInteger { nanoseconds } , 0 ) ;
2021-11-07 01:41:52 +00:00
// b. Let intermediate be undefined.
ZonedDateTime * intermediate = nullptr ;
// c. If zonedRelativeTo is not undefined, then
if ( zoned_relative_to ) {
// i. Let intermediate be ? MoveRelativeZonedDateTime(zonedRelativeTo, years, months, weeks, days).
2022-08-20 08:52:42 +01:00
intermediate = TRY ( move_relative_zoned_date_time ( vm , * zoned_relative_to , years , months , weeks , days ) ) ;
2021-11-07 01:41:52 +00:00
}
// d. Let result be ? NanosecondsToDays(nanoseconds, intermediate).
2022-08-20 08:52:42 +01:00
auto result = TRY ( nanoseconds_to_days ( vm , nanoseconds_bigint , intermediate ) ) ;
2021-11-07 01:41:52 +00:00
// e. Set days to days + result.[[Days]] + result.[[Nanoseconds]] / result.[[DayLength]].
2022-08-26 00:49:50 +02:00
auto nanoseconds_division_result = result . nanoseconds . divided_by ( Crypto : : UnsignedBigInteger { result . day_length } ) ;
2021-11-13 16:53:12 +00:00
days + = result . days + nanoseconds_division_result . quotient . to_double ( ) + nanoseconds_division_result . remainder . to_double ( ) / result . day_length ;
2021-11-07 01:41:52 +00:00
// f. Set hours, minutes, seconds, milliseconds, microseconds, and nanoseconds to 0.
hours = 0 ;
minutes = 0 ;
seconds = 0 ;
milliseconds = 0 ;
microseconds = 0 ;
nanoseconds = 0 ;
}
2022-05-07 16:26:32 +02:00
// 7. Else,
2021-11-07 01:41:52 +00:00
else {
2022-04-29 21:09:10 +02:00
// a. Let fractionalSeconds be nanoseconds × 10^-9 + microseconds × 10^-6 + milliseconds × 10^-3 + seconds.
2021-11-07 01:41:52 +00:00
fractional_seconds = nanoseconds * 0.000000001 + microseconds * 0.000001 + milliseconds * 0.001 + seconds ;
}
2022-05-07 16:26:32 +02:00
// 8. Let remainder be undefined.
2021-11-07 01:41:52 +00:00
double remainder = 0 ;
2022-05-07 16:26:32 +02:00
// 9. If unit is "year", then
2021-11-07 01:41:52 +00:00
if ( unit = = " year " sv ) {
VERIFY ( relative_to ) ;
2022-03-10 18:32:23 +01:00
// a. Let yearsDuration be ! CreateTemporalDuration(years, 0, 0, 0, 0, 0, 0, 0, 0, 0).
2022-08-20 08:52:42 +01:00
auto * years_duration = MUST ( create_temporal_duration ( vm , years , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ) ) ;
2021-11-07 01:41:52 +00:00
// b. Let dateAdd be ? GetMethod(calendar, "dateAdd").
2023-04-13 15:41:29 +02:00
auto date_add = TRY ( Value ( calendar ) . get_method ( vm , vm . names . dateAdd ) ) ;
2021-11-07 01:41:52 +00:00
2022-04-06 23:56:57 +01:00
// c. Let yearsLater be ? CalendarDateAdd(calendar, relativeTo, yearsDuration, undefined, dateAdd).
2022-08-20 08:52:42 +01:00
auto * years_later = TRY ( calendar_date_add ( vm , * calendar , relative_to , * years_duration , nullptr , date_add ) ) ;
2021-11-07 01:41:52 +00:00
2022-04-06 23:56:57 +01:00
// d. Let yearsMonthsWeeks be ! CreateTemporalDuration(years, months, weeks, 0, 0, 0, 0, 0, 0, 0).
2022-08-20 08:52:42 +01:00
auto * years_months_weeks = MUST ( create_temporal_duration ( vm , years , months , weeks , 0 , 0 , 0 , 0 , 0 , 0 , 0 ) ) ;
2021-11-07 01:41:52 +00:00
2022-04-06 23:56:57 +01:00
// e. Let yearsMonthsWeeksLater be ? CalendarDateAdd(calendar, relativeTo, yearsMonthsWeeks, undefined, dateAdd).
2022-08-20 08:52:42 +01:00
auto * years_months_weeks_later = TRY ( calendar_date_add ( vm , * calendar , relative_to , * years_months_weeks , nullptr , date_add ) ) ;
2021-11-07 01:41:52 +00:00
2022-05-06 21:03:49 +02:00
// f. Let monthsWeeksInDays be DaysUntil(yearsLater, yearsMonthsWeeksLater).
auto months_weeks_in_days = days_until ( * years_later , * years_months_weeks_later ) ;
2021-11-07 01:41:52 +00:00
2022-04-06 23:56:57 +01:00
// g. Set relativeTo to yearsLater.
2021-11-19 19:59:13 +00:00
relative_to = years_later ;
2021-11-07 01:41:52 +00:00
2022-04-06 23:56:57 +01:00
// h. Let days be days + monthsWeeksInDays.
2021-11-07 01:41:52 +00:00
days + = months_weeks_in_days ;
2023-01-13 04:40:01 +01:00
// i. Let wholeDaysDuration be ? CreateTemporalDuration(0, 0, 0, truncate(days), 0, 0, 0, 0, 0, 0).
auto * whole_days_duration = TRY ( create_temporal_duration ( vm , 0 , 0 , 0 , trunc ( days ) , 0 , 0 , 0 , 0 , 0 , 0 ) ) ;
2021-11-07 01:41:52 +00:00
2023-01-13 04:40:01 +01:00
// j. Let wholeDaysLater be ? CalendarDateAdd(calendar, relativeTo, wholeDaysDuration, undefined, dateAdd).
auto * whole_days_later = TRY ( calendar_date_add ( vm , * calendar , relative_to , * whole_days_duration , nullptr , date_add ) ) ;
2021-11-07 01:41:52 +00:00
2022-11-11 14:10:02 +00:00
// k. Let untilOptions be OrdinaryObjectCreate(null).
2022-12-13 20:49:50 +00:00
auto until_options = Object : : create ( realm , nullptr ) ;
2021-11-07 01:41:52 +00:00
2022-11-11 14:10:02 +00:00
// l. Perform ! CreateDataPropertyOrThrow(untilOptions, "largestUnit", "year").
2023-08-08 18:25:57 +02:00
MUST ( until_options - > create_data_property_or_throw ( vm . names . largestUnit , PrimitiveString : : create ( vm , " year " _string ) ) ) ;
2021-11-07 01:41:52 +00:00
2023-01-13 04:40:01 +01:00
// m. Let timePassed be ? CalendarDateUntil(calendar, relativeTo, wholeDaysLater, untilOptions).
auto * time_passed = TRY ( calendar_date_until ( vm , * calendar , relative_to , whole_days_later , * until_options ) ) ;
2021-11-07 01:41:52 +00:00
2022-11-11 14:10:02 +00:00
// n. Let yearsPassed be timePassed.[[Years]].
2021-11-07 01:41:52 +00:00
auto years_passed = time_passed - > years ( ) ;
2022-11-11 14:10:02 +00:00
// o. Set years to years + yearsPassed.
2021-11-07 01:41:52 +00:00
years + = years_passed ;
2022-11-11 14:10:02 +00:00
// p. Let oldRelativeTo be relativeTo.
2021-11-19 19:59:13 +00:00
auto * old_relative_to = relative_to ;
2021-11-07 01:41:52 +00:00
2022-11-11 14:10:02 +00:00
// q. Let yearsDuration be ! CreateTemporalDuration(yearsPassed, 0, 0, 0, 0, 0, 0, 0, 0, 0).
2022-08-20 08:52:42 +01:00
years_duration = MUST ( create_temporal_duration ( vm , years_passed , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ) ) ;
2021-11-07 01:41:52 +00:00
2022-11-11 14:10:02 +00:00
// r. Set relativeTo to ? CalendarDateAdd(calendar, relativeTo, yearsDuration, undefined, dateAdd).
2022-08-20 08:52:42 +01:00
relative_to = TRY ( calendar_date_add ( vm , * calendar , relative_to , * years_duration , nullptr , date_add ) ) ;
2021-11-07 01:41:52 +00:00
2022-11-11 14:10:02 +00:00
// s. Let daysPassed be DaysUntil(oldRelativeTo, relativeTo).
2022-05-06 21:03:49 +02:00
auto days_passed = days_until ( * old_relative_to , * relative_to ) ;
2021-11-07 01:41:52 +00:00
2022-11-11 14:10:02 +00:00
// t. Set days to days - daysPassed.
2021-11-07 01:41:52 +00:00
days - = days_passed ;
2022-11-11 14:10:02 +00:00
// u. If days < 0, let sign be -1; else, let sign be 1.
2022-03-10 17:56:58 +01:00
auto sign = days < 0 ? - 1 : 1 ;
2021-11-07 01:41:52 +00:00
2022-11-11 14:10:02 +00:00
// v. Let oneYear be ! CreateTemporalDuration(sign, 0, 0, 0, 0, 0, 0, 0, 0, 0).
2022-08-20 08:52:42 +01:00
auto * one_year = MUST ( create_temporal_duration ( vm , sign , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ) ) ;
2021-11-07 01:41:52 +00:00
2022-11-11 14:10:02 +00:00
// w. Let moveResult be ? MoveRelativeDate(calendar, relativeTo, oneYear, dateAdd).
2022-10-16 11:33:42 +03:00
auto move_result = TRY ( move_relative_date ( vm , * calendar , * relative_to , * one_year , date_add ) ) ;
2021-11-07 01:41:52 +00:00
2022-11-11 14:10:02 +00:00
// x. Let oneYearDays be moveResult.[[Days]].
2021-11-07 01:41:52 +00:00
auto one_year_days = move_result . days ;
2022-11-11 14:10:02 +00:00
// y. Let fractionalYears be years + days / abs(oneYearDays).
2021-11-07 01:41:52 +00:00
auto fractional_years = years + days / fabs ( one_year_days ) ;
2022-11-11 14:10:02 +00:00
// z. Set years to RoundNumberToIncrement(fractionalYears, increment, roundingMode).
2022-05-24 18:26:13 +01:00
years = round_number_to_increment ( fractional_years , increment , rounding_mode ) ;
2021-11-07 01:41:52 +00:00
2022-11-11 14:10:02 +00:00
// aa. Set remainder to fractionalYears - years.
2021-11-07 01:41:52 +00:00
remainder = fractional_years - years ;
2022-11-11 14:10:02 +00:00
// ab. Set months, weeks, and days to 0.
2021-11-07 01:41:52 +00:00
months = 0 ;
weeks = 0 ;
days = 0 ;
}
2022-05-07 16:26:32 +02:00
// 10. Else if unit is "month", then
2021-11-07 01:41:52 +00:00
else if ( unit = = " month " sv ) {
VERIFY ( relative_to ) ;
2022-03-10 18:32:23 +01:00
// a. Let yearsMonths be ! CreateTemporalDuration(years, months, 0, 0, 0, 0, 0, 0, 0, 0).
2022-08-20 08:52:42 +01:00
auto * years_months = MUST ( create_temporal_duration ( vm , years , months , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ) ) ;
2021-11-07 01:41:52 +00:00
// b. Let dateAdd be ? GetMethod(calendar, "dateAdd").
2023-04-13 15:41:29 +02:00
auto date_add = TRY ( Value ( calendar ) . get_method ( vm , vm . names . dateAdd ) ) ;
2021-11-07 01:41:52 +00:00
2022-04-06 23:56:57 +01:00
// c. Let yearsMonthsLater be ? CalendarDateAdd(calendar, relativeTo, yearsMonths, undefined, dateAdd).
2022-08-20 08:52:42 +01:00
auto * years_months_later = TRY ( calendar_date_add ( vm , * calendar , relative_to , * years_months , nullptr , date_add ) ) ;
2021-11-07 01:41:52 +00:00
2022-04-06 23:56:57 +01:00
// d. Let yearsMonthsWeeks be ! CreateTemporalDuration(years, months, weeks, 0, 0, 0, 0, 0, 0, 0).
2022-08-20 08:52:42 +01:00
auto * years_months_weeks = MUST ( create_temporal_duration ( vm , years , months , weeks , 0 , 0 , 0 , 0 , 0 , 0 , 0 ) ) ;
2021-11-07 01:41:52 +00:00
2022-04-06 23:56:57 +01:00
// e. Let yearsMonthsWeeksLater be ? CalendarDateAdd(calendar, relativeTo, yearsMonthsWeeks, undefined, dateAdd).
2022-08-20 08:52:42 +01:00
auto * years_months_weeks_later = TRY ( calendar_date_add ( vm , * calendar , relative_to , * years_months_weeks , nullptr , date_add ) ) ;
2021-11-07 01:41:52 +00:00
2022-05-06 21:03:49 +02:00
// f. Let weeksInDays be DaysUntil(yearsMonthsLater, yearsMonthsWeeksLater).
auto weeks_in_days = days_until ( * years_months_later , * years_months_weeks_later ) ;
2021-11-07 01:41:52 +00:00
2022-04-06 23:56:57 +01:00
// g. Set relativeTo to yearsMonthsLater.
2021-11-19 19:59:13 +00:00
relative_to = years_months_later ;
2021-11-07 01:41:52 +00:00
2022-04-06 23:56:57 +01:00
// h. Let days be days + weeksInDays.
2021-11-07 01:41:52 +00:00
days + = weeks_in_days ;
2022-04-29 21:09:10 +02:00
// i. If days < 0, let sign be -1; else, let sign be 1.
2022-03-10 17:56:58 +01:00
auto sign = days < 0 ? - 1 : 1 ;
2021-11-07 01:41:52 +00:00
2022-04-06 23:56:57 +01:00
// j. Let oneMonth be ! CreateTemporalDuration(0, sign, 0, 0, 0, 0, 0, 0, 0, 0).
2022-08-20 08:52:42 +01:00
auto * one_month = MUST ( create_temporal_duration ( vm , 0 , sign , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ) ) ;
2021-11-07 01:41:52 +00:00
2022-10-16 11:33:42 +03:00
// k. Let moveResult be ? MoveRelativeDate(calendar, relativeTo, oneMonth, dateAdd).
auto move_result = TRY ( move_relative_date ( vm , * calendar , * relative_to , * one_month , date_add ) ) ;
2021-11-07 01:41:52 +00:00
2022-04-06 23:56:57 +01:00
// l. Set relativeTo to moveResult.[[RelativeTo]].
2021-11-07 01:41:52 +00:00
relative_to = move_result . relative_to . cell ( ) ;
2022-04-06 23:56:57 +01:00
// m. Let oneMonthDays be moveResult.[[Days]].
2021-11-07 01:41:52 +00:00
auto one_month_days = move_result . days ;
2022-04-06 23:56:57 +01:00
// n. Repeat, while abs(days) ≥ abs(oneMonthDays),
2021-11-07 01:41:52 +00:00
while ( fabs ( days ) > = fabs ( one_month_days ) ) {
// i. Set months to months + sign.
months + = sign ;
2022-04-29 21:09:10 +02:00
// ii. Set days to days - oneMonthDays.
2021-11-07 01:41:52 +00:00
days - = one_month_days ;
2022-10-16 11:33:42 +03:00
// iii. Set moveResult to ? MoveRelativeDate(calendar, relativeTo, oneMonth, dateAdd).
move_result = TRY ( move_relative_date ( vm , * calendar , * relative_to , * one_month , date_add ) ) ;
2021-11-07 01:41:52 +00:00
// iv. Set relativeTo to moveResult.[[RelativeTo]].
relative_to = move_result . relative_to . cell ( ) ;
// v. Set oneMonthDays to moveResult.[[Days]].
one_month_days = move_result . days ;
}
2022-04-06 23:56:57 +01:00
// o. Let fractionalMonths be months + days / abs(oneMonthDays).
2021-11-07 01:41:52 +00:00
auto fractional_months = months + days / fabs ( one_month_days ) ;
2022-05-24 18:26:13 +01:00
// p. Set months to RoundNumberToIncrement(fractionalMonths, increment, roundingMode).
months = round_number_to_increment ( fractional_months , increment , rounding_mode ) ;
2021-11-07 01:41:52 +00:00
2022-04-06 23:56:57 +01:00
// q. Set remainder to fractionalMonths - months.
2021-11-07 01:41:52 +00:00
remainder = fractional_months - months ;
2022-04-06 23:56:57 +01:00
// r. Set weeks and days to 0.
2021-11-07 01:41:52 +00:00
weeks = 0 ;
days = 0 ;
}
2022-05-07 16:26:32 +02:00
// 11. Else if unit is "week", then
2021-11-07 01:41:52 +00:00
else if ( unit = = " week " sv ) {
VERIFY ( relative_to ) ;
2022-04-29 21:09:10 +02:00
// a. If days < 0, let sign be -1; else, let sign be 1.
2022-03-10 17:56:58 +01:00
auto sign = days < 0 ? - 1 : 1 ;
2021-11-07 01:41:52 +00:00
2022-03-10 18:13:35 +01:00
// b. Let oneWeek be ! CreateTemporalDuration(0, 0, sign, 0, 0, 0, 0, 0, 0, 0).
2022-08-20 08:52:42 +01:00
auto * one_week = MUST ( create_temporal_duration ( vm , 0 , 0 , sign , 0 , 0 , 0 , 0 , 0 , 0 , 0 ) ) ;
2021-11-07 01:41:52 +00:00
2022-10-16 11:46:39 +03:00
// c. Let dateAdd be ? GetMethod(calendar, "dateAdd").
2023-04-13 15:41:29 +02:00
auto date_add = TRY ( Value ( calendar ) . get_method ( vm , vm . names . dateAdd ) ) ;
2022-10-16 11:46:39 +03:00
// d. Let moveResult be ? MoveRelativeDate(calendar, relativeTo, oneWeek, dateAdd).
auto move_result = TRY ( move_relative_date ( vm , * calendar , * relative_to , * one_week , date_add ) ) ;
2021-11-07 01:41:52 +00:00
2022-10-16 11:46:39 +03:00
// e. Set relativeTo to moveResult.[[RelativeTo]].
2021-11-07 01:41:52 +00:00
relative_to = move_result . relative_to . cell ( ) ;
2022-10-16 11:46:39 +03:00
// f. Let oneWeekDays be moveResult.[[Days]].
2021-11-07 01:41:52 +00:00
auto one_week_days = move_result . days ;
2022-10-16 11:46:39 +03:00
// g. Repeat, while abs(days) ≥ abs(oneWeekDays),
2021-11-07 01:41:52 +00:00
while ( fabs ( days ) > = fabs ( one_week_days ) ) {
// i. Set weeks to weeks + sign.
weeks + = sign ;
2022-04-29 21:09:10 +02:00
// ii. Set days to days - oneWeekDays.
2021-11-07 01:41:52 +00:00
days - = one_week_days ;
2022-10-16 11:46:39 +03:00
// iii. Set moveResult to ? MoveRelativeDate(calendar, relativeTo, oneWeek, dateAdd).
move_result = TRY ( move_relative_date ( vm , * calendar , * relative_to , * one_week , date_add ) ) ;
2021-11-07 01:41:52 +00:00
// iv. Set relativeTo to moveResult.[[RelativeTo]].
relative_to = move_result . relative_to . cell ( ) ;
// v. Set oneWeekDays to moveResult.[[Days]].
one_week_days = move_result . days ;
}
2022-10-16 11:46:39 +03:00
// h. Let fractionalWeeks be weeks + days / abs(oneWeekDays).
2021-11-07 01:41:52 +00:00
auto fractional_weeks = weeks + days / fabs ( one_week_days ) ;
2022-10-16 11:46:39 +03:00
// i. Set weeks to RoundNumberToIncrement(fractionalWeeks, increment, roundingMode).
2022-05-24 18:26:13 +01:00
weeks = round_number_to_increment ( fractional_weeks , increment , rounding_mode ) ;
2021-11-07 01:41:52 +00:00
2022-10-16 11:46:39 +03:00
// j. Set remainder to fractionalWeeks - weeks.
2021-11-07 01:41:52 +00:00
remainder = fractional_weeks - weeks ;
2022-10-16 11:46:39 +03:00
// k. Set days to 0.
2021-11-07 01:41:52 +00:00
days = 0 ;
}
2022-05-07 16:26:32 +02:00
// 12. Else if unit is "day", then
2021-11-07 01:41:52 +00:00
else if ( unit = = " day " sv ) {
// a. Let fractionalDays be days.
auto fractional_days = days ;
2022-05-24 18:26:13 +01:00
// b. Set days to RoundNumberToIncrement(days, increment, roundingMode).
days = round_number_to_increment ( days , increment , rounding_mode ) ;
2021-11-07 01:41:52 +00:00
// c. Set remainder to fractionalDays - days.
remainder = fractional_days - days ;
}
2022-05-07 16:26:32 +02:00
// 13. Else if unit is "hour", then
2021-11-07 01:41:52 +00:00
else if ( unit = = " hour " sv ) {
// a. Let fractionalHours be (fractionalSeconds / 60 + minutes) / 60 + hours.
auto fractional_hours = ( fractional_seconds / 60 + minutes ) / 60 + hours ;
2022-05-24 18:26:13 +01:00
// b. Set hours to RoundNumberToIncrement(fractionalHours, increment, roundingMode).
hours = round_number_to_increment ( fractional_hours , increment , rounding_mode ) ;
2021-11-07 01:41:52 +00:00
// c. Set remainder to fractionalHours - hours.
2021-11-13 13:29:07 +00:00
remainder = fractional_hours - hours ;
2021-11-07 01:41:52 +00:00
// d. Set minutes, seconds, milliseconds, microseconds, and nanoseconds to 0.
minutes = 0 ;
seconds = 0 ;
milliseconds = 0 ;
microseconds = 0 ;
nanoseconds = 0 ;
}
2022-05-07 16:26:32 +02:00
// 14. Else if unit is "minute", then
2021-11-07 01:41:52 +00:00
else if ( unit = = " minute " sv ) {
// a. Let fractionalMinutes be fractionalSeconds / 60 + minutes.
auto fractional_minutes = fractional_seconds / 60 + minutes ;
2022-05-24 18:26:13 +01:00
// b. Set minutes to RoundNumberToIncrement(fractionalMinutes, increment, roundingMode).
minutes = round_number_to_increment ( fractional_minutes , increment , rounding_mode ) ;
2021-11-07 01:41:52 +00:00
// c. Set remainder to fractionalMinutes - minutes.
remainder = fractional_minutes - minutes ;
// d. Set seconds, milliseconds, microseconds, and nanoseconds to 0.
seconds = 0 ;
milliseconds = 0 ;
microseconds = 0 ;
nanoseconds = 0 ;
}
2022-05-07 16:26:32 +02:00
// 15. Else if unit is "second", then
2021-11-07 01:41:52 +00:00
else if ( unit = = " second " sv ) {
2022-05-24 18:26:13 +01:00
// a. Set seconds to RoundNumberToIncrement(fractionalSeconds, increment, roundingMode).
seconds = round_number_to_increment ( fractional_seconds , increment , rounding_mode ) ;
2021-11-07 01:41:52 +00:00
// b. Set remainder to fractionalSeconds - seconds.
remainder = fractional_seconds - seconds ;
// c. Set milliseconds, microseconds, and nanoseconds to 0.
milliseconds = 0 ;
microseconds = 0 ;
nanoseconds = 0 ;
}
2022-05-07 16:26:32 +02:00
// 16. Else if unit is "millisecond", then
2021-11-07 01:41:52 +00:00
else if ( unit = = " millisecond " sv ) {
2022-04-29 21:09:10 +02:00
// a. Let fractionalMilliseconds be nanoseconds × 10^-6 + microseconds × 10^-3 + milliseconds.
2021-11-07 01:41:52 +00:00
auto fractional_milliseconds = nanoseconds * 0.000001 + microseconds * 0.001 + milliseconds ;
2022-05-24 18:26:13 +01:00
// b. Set milliseconds to RoundNumberToIncrement(fractionalMilliseconds, increment, roundingMode).
milliseconds = round_number_to_increment ( fractional_milliseconds , increment , rounding_mode ) ;
2021-11-07 01:41:52 +00:00
// c. Set remainder to fractionalMilliseconds - milliseconds.
remainder = fractional_milliseconds - milliseconds ;
// d. Set microseconds and nanoseconds to 0.
microseconds = 0 ;
nanoseconds = 0 ;
}
2022-05-07 16:26:32 +02:00
// 17. Else if unit is "microsecond", then
2021-11-07 01:41:52 +00:00
else if ( unit = = " microsecond " sv ) {
2022-04-29 21:09:10 +02:00
// a. Let fractionalMicroseconds be nanoseconds × 10^-3 + microseconds.
2021-11-07 01:41:52 +00:00
auto fractional_microseconds = nanoseconds * 0.001 + microseconds ;
2022-05-24 18:26:13 +01:00
// b. Set microseconds to RoundNumberToIncrement(fractionalMicroseconds, increment, roundingMode).
microseconds = round_number_to_increment ( fractional_microseconds , increment , rounding_mode ) ;
2021-11-07 01:41:52 +00:00
// c. Set remainder to fractionalMicroseconds - microseconds.
remainder = fractional_microseconds - microseconds ;
// d. Set nanoseconds to 0.
nanoseconds = 0 ;
}
2022-05-07 16:26:32 +02:00
// 18. Else,
2021-11-07 01:41:52 +00:00
else {
// a. Assert: unit is "nanosecond".
VERIFY ( unit = = " nanosecond " sv ) ;
// b. Set remainder to nanoseconds.
remainder = nanoseconds ;
2022-05-24 18:26:13 +01:00
// c. Set nanoseconds to RoundNumberToIncrement(nanoseconds, increment, roundingMode).
nanoseconds = round_number_to_increment ( nanoseconds , increment , rounding_mode ) ;
2021-11-07 01:41:52 +00:00
2022-04-29 21:09:10 +02:00
// d. Set remainder to remainder - nanoseconds.
2021-11-07 01:41:52 +00:00
remainder - = nanoseconds ;
}
2022-11-11 14:10:02 +00:00
// 19. Let duration be ? CreateDurationRecord(years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds).
2022-08-20 08:52:42 +01:00
auto duration = TRY ( create_duration_record ( vm , years , months , weeks , days , hours , minutes , seconds , milliseconds , microseconds , nanoseconds ) ) ;
2022-03-09 23:20:36 +01:00
2022-11-11 14:10:02 +00:00
// 20. Return the Record { [[DurationRecord]]: duration, [[Remainder]]: remainder }.
2022-03-09 23:20:36 +01:00
return RoundedDuration { . duration_record = duration , . remainder = remainder } ;
2021-11-07 01:41:52 +00:00
}
2022-06-18 13:58:08 +01:00
// 7.5.26 AdjustRoundedDurationDays ( years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, increment, unit, roundingMode, relativeTo ), https://tc39.es/proposal-temporal/#sec-temporal-adjustroundeddurationdays
2022-08-20 08:52:42 +01:00
ThrowCompletionOr < DurationRecord > adjust_rounded_duration_days ( VM & vm , double years , double months , double weeks , double days , double hours , double minutes , double seconds , double milliseconds , double microseconds , double nanoseconds , u32 increment , StringView unit , StringView rounding_mode , Object * relative_to_object )
2021-11-23 23:42:16 +00:00
{
2022-06-18 13:30:10 +01:00
// 1. If Type(relativeTo) is not Object; or relativeTo does not have an [[InitializedTemporalZonedDateTime]] internal slot; or unit is one of "year", "month", "week", or "day"; or unit is "nanosecond" and increment is 1, then
2021-11-23 23:42:16 +00:00
if ( relative_to_object = = nullptr | | ! is < ZonedDateTime > ( relative_to_object ) | | unit . is_one_of ( " year " sv , " month " sv , " week " sv , " day " sv ) | | ( unit = = " nanosecond " sv & & increment = = 1 ) ) {
2022-03-10 16:52:25 +01:00
// a. Return ! CreateDurationRecord(years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds).
return create_duration_record ( years , months , weeks , days , hours , minutes , seconds , milliseconds , microseconds , nanoseconds ) ;
2021-11-23 23:42:16 +00:00
}
auto & relative_to = static_cast < ZonedDateTime & > ( * relative_to_object ) ;
// 2. Let timeRemainderNs be ! TotalDurationNanoseconds(0, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, 0).
2022-08-26 00:49:50 +02:00
auto time_remainder_ns = total_duration_nanoseconds ( 0 , hours , minutes , seconds , milliseconds , microseconds , Crypto : : SignedBigInteger { nanoseconds } , 0 ) ;
2021-11-23 23:42:16 +00:00
2022-03-10 17:56:58 +01:00
i32 direction ;
// 3. If timeRemainderNs = 0, let direction be 0.
2022-07-16 12:43:23 -04:00
if ( time_remainder_ns . is_zero ( ) )
2022-03-10 17:56:58 +01:00
direction = 0 ;
2022-04-29 21:09:10 +02:00
// 4. Else if timeRemainderNs < 0, let direction be -1.
2022-03-10 17:56:58 +01:00
else if ( time_remainder_ns . is_negative ( ) )
direction = - 1 ;
// 5. Else, let direction be 1.
else
direction = 1 ;
2021-11-23 23:42:16 +00:00
2022-03-10 17:56:58 +01:00
// 6. Let dayStart be ? AddZonedDateTime(relativeTo.[[Nanoseconds]], relativeTo.[[TimeZone]], relativeTo.[[Calendar]], years, months, weeks, days, 0, 0, 0, 0, 0, 0).
2022-08-20 08:52:42 +01:00
auto * day_start = TRY ( add_zoned_date_time ( vm , relative_to . nanoseconds ( ) , & relative_to . time_zone ( ) , relative_to . calendar ( ) , years , months , weeks , days , 0 , 0 , 0 , 0 , 0 , 0 ) ) ;
2021-11-23 23:42:16 +00:00
2022-03-10 17:56:58 +01:00
// 7. Let dayEnd be ? AddZonedDateTime(dayStart, relativeTo.[[TimeZone]], relativeTo.[[Calendar]], 0, 0, 0, direction, 0, 0, 0, 0, 0, 0).
2022-08-20 08:52:42 +01:00
auto * day_end = TRY ( add_zoned_date_time ( vm , * day_start , & relative_to . time_zone ( ) , relative_to . calendar ( ) , 0 , 0 , 0 , direction , 0 , 0 , 0 , 0 , 0 , 0 ) ) ;
2021-11-23 23:42:16 +00:00
2022-04-29 21:09:10 +02:00
// 8. Let dayLengthNs be ℝ (dayEnd - dayStart).
2021-11-23 23:42:16 +00:00
auto day_length_ns = day_end - > big_integer ( ) . minus ( day_start - > big_integer ( ) ) ;
2022-04-29 21:09:10 +02:00
// 9. If (timeRemainderNs - dayLengthNs) × direction < 0, then
2022-03-10 17:56:58 +01:00
if ( time_remainder_ns . minus ( day_length_ns ) . multiplied_by ( Crypto : : SignedBigInteger { direction } ) . is_negative ( ) ) {
2022-03-10 16:52:25 +01:00
// a. Return ! CreateDurationRecord(years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds).
return create_duration_record ( years , months , weeks , days , hours , minutes , seconds , milliseconds , microseconds , nanoseconds ) ;
2021-11-23 23:42:16 +00:00
}
2022-04-29 21:09:10 +02:00
// 10. Set timeRemainderNs to ! RoundTemporalInstant(ℤ (timeRemainderNs - dayLengthNs), increment, unit, roundingMode).
2022-12-06 22:03:52 +00:00
time_remainder_ns = round_temporal_instant ( vm , BigInt : : create ( vm , time_remainder_ns . minus ( day_length_ns ) ) , increment , unit , rounding_mode ) - > big_integer ( ) ;
2021-11-23 23:42:16 +00:00
2022-03-10 17:56:58 +01:00
// 11. Let adjustedDateDuration be ? AddDuration(years, months, weeks, days, 0, 0, 0, 0, 0, 0, 0, 0, 0, direction, 0, 0, 0, 0, 0, 0, relativeTo).
2022-08-20 08:52:42 +01:00
auto adjusted_date_duration = TRY ( add_duration ( vm , years , months , weeks , days , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , direction , 0 , 0 , 0 , 0 , 0 , 0 , & relative_to ) ) ;
2021-11-23 23:42:16 +00:00
2022-03-10 17:56:58 +01:00
// 12. Let adjustedTimeDuration be ? BalanceDuration(0, 0, 0, 0, 0, 0, timeRemainderNs, "hour").
2022-08-20 08:52:42 +01:00
auto adjusted_time_duration = TRY ( balance_duration ( vm , 0 , 0 , 0 , 0 , 0 , 0 , time_remainder_ns , " hour " sv ) ) ;
2021-11-23 23:42:16 +00:00
2022-03-10 17:56:58 +01:00
// 13. Return ! CreateDurationRecord(adjustedDateDuration.[[Years]], adjustedDateDuration.[[Months]], adjustedDateDuration.[[Weeks]], adjustedDateDuration.[[Days]], adjustedTimeDuration.[[Hours]], adjustedTimeDuration.[[Minutes]], adjustedTimeDuration.[[Seconds]], adjustedTimeDuration.[[Milliseconds]], adjustedTimeDuration.[[Microseconds]], adjustedTimeDuration.[[Nanoseconds]]).
2022-03-10 16:52:25 +01:00
return create_duration_record ( adjusted_date_duration . years , adjusted_date_duration . months , adjusted_date_duration . weeks , adjusted_date_duration . days , adjusted_time_duration . hours , adjusted_time_duration . minutes , adjusted_time_duration . seconds , adjusted_time_duration . milliseconds , adjusted_time_duration . microseconds , adjusted_time_duration . nanoseconds ) ;
2021-11-23 23:42:16 +00:00
}
2022-06-09 20:56:03 +01:00
// 7.5.27 TemporalDurationToString ( years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, precision ), https://tc39.es/proposal-temporal/#sec-temporal-temporaldurationtostring
2023-01-26 15:40:58 +00:00
ThrowCompletionOr < String > temporal_duration_to_string ( VM & vm , double years , double months , double weeks , double days , double hours , double minutes , double seconds , double milliseconds , double microseconds , double nanoseconds , Variant < StringView , u8 > const & precision )
2021-11-07 01:41:52 +00:00
{
if ( precision . has < StringView > ( ) )
2022-03-09 23:51:53 +01:00
VERIFY ( precision . get < StringView > ( ) = = " auto " sv ) ;
2021-11-07 01:41:52 +00:00
2022-05-07 16:26:32 +02:00
// 1. Let sign be ! DurationSign(years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds).
2021-11-07 01:41:52 +00:00
auto sign = duration_sign ( years , months , weeks , days , hours , minutes , seconds , milliseconds , microseconds , nanoseconds ) ;
2022-10-14 09:29:16 -04:00
// 2. Set microseconds to microseconds + truncate(nanoseconds / 1000).
2021-11-07 01:41:52 +00:00
microseconds + = trunc ( nanoseconds / 1000 ) ;
2022-05-07 16:26:32 +02:00
// 3. Set nanoseconds to remainder(nanoseconds, 1000).
2021-11-07 01:41:52 +00:00
nanoseconds = fmod ( nanoseconds , 1000 ) ;
2022-10-14 09:29:16 -04:00
// 4. Set milliseconds to milliseconds + truncate(microseconds / 1000).
2021-11-07 01:41:52 +00:00
milliseconds + = trunc ( microseconds / 1000 ) ;
2022-05-07 16:26:32 +02:00
// 5. Set microseconds to remainder(microseconds, 1000).
2021-11-07 01:41:52 +00:00
microseconds = fmod ( microseconds , 1000 ) ;
2022-10-14 09:29:16 -04:00
// 6. Set seconds to seconds + truncate(milliseconds / 1000).
2021-11-07 01:41:52 +00:00
seconds + = trunc ( milliseconds / 1000 ) ;
2022-05-07 16:26:32 +02:00
// 7. Set milliseconds to remainder(milliseconds, 1000).
2021-11-07 01:41:52 +00:00
milliseconds = fmod ( milliseconds , 1000 ) ;
2022-05-07 16:26:32 +02:00
// 8. Let datePart be "".
2021-11-07 01:41:52 +00:00
StringBuilder date_part ;
2022-05-07 16:26:32 +02:00
// 9. If years is not 0, then
2021-11-07 01:41:52 +00:00
if ( years ! = 0 ) {
// a. Set datePart to the string concatenation of abs(years) formatted as a decimal number and the code unit 0x0059 (LATIN CAPITAL LETTER Y).
date_part . appendff ( " {} " , fabs ( years ) ) ;
date_part . append ( ' Y ' ) ;
}
2022-05-07 16:26:32 +02:00
// 10. If months is not 0, then
2021-11-07 01:41:52 +00:00
if ( months ! = 0 ) {
// a. Set datePart to the string concatenation of datePart, abs(months) formatted as a decimal number, and the code unit 0x004D (LATIN CAPITAL LETTER M).
date_part . appendff ( " {} " , fabs ( months ) ) ;
date_part . append ( ' M ' ) ;
}
2022-05-07 16:26:32 +02:00
// 11. If weeks is not 0, then
2021-11-07 01:41:52 +00:00
if ( weeks ! = 0 ) {
// a. Set datePart to the string concatenation of datePart, abs(weeks) formatted as a decimal number, and the code unit 0x0057 (LATIN CAPITAL LETTER W).
date_part . appendff ( " {} " , fabs ( weeks ) ) ;
date_part . append ( ' W ' ) ;
}
2022-05-07 16:26:32 +02:00
// 12. If days is not 0, then
2021-11-07 01:41:52 +00:00
if ( days ! = 0 ) {
// a. Set datePart to the string concatenation of datePart, abs(days) formatted as a decimal number, and the code unit 0x0044 (LATIN CAPITAL LETTER D).
date_part . appendff ( " {} " , fabs ( days ) ) ;
date_part . append ( ' D ' ) ;
}
2022-05-07 16:26:32 +02:00
// 13. Let timePart be "".
2021-11-07 01:41:52 +00:00
StringBuilder time_part ;
2022-05-07 16:26:32 +02:00
// 14. If hours is not 0, then
2021-11-07 01:41:52 +00:00
if ( hours ! = 0 ) {
// a. Set timePart to the string concatenation of abs(hours) formatted as a decimal number and the code unit 0x0048 (LATIN CAPITAL LETTER H).
time_part . appendff ( " {} " , fabs ( hours ) ) ;
time_part . append ( ' H ' ) ;
}
2022-05-07 16:26:32 +02:00
// 15. If minutes is not 0, then
2021-11-07 01:41:52 +00:00
if ( minutes ! = 0 ) {
// a. Set timePart to the string concatenation of timePart, abs(minutes) formatted as a decimal number, and the code unit 0x004D (LATIN CAPITAL LETTER M).
time_part . appendff ( " {} " , fabs ( minutes ) ) ;
time_part . append ( ' M ' ) ;
}
2022-05-07 16:26:32 +02:00
// 16. If any of seconds, milliseconds, microseconds, and nanoseconds are not 0; or years, months, weeks, days, hours, and minutes are all 0; or precision is not "auto"; then
2021-12-18 23:20:00 +00:00
if ( ( seconds ! = 0 | | milliseconds ! = 0 | | microseconds ! = 0 | | nanoseconds ! = 0 ) | | ( years = = 0 & & months = = 0 & & weeks = = 0 & & days = = 0 & & hours = = 0 & & minutes = = 0 ) | | ( ! precision . has < StringView > ( ) | | precision . get < StringView > ( ) ! = " auto " sv ) ) {
2021-11-07 01:41:52 +00:00
// a. Let fraction be abs(milliseconds) × 10^6 + abs(microseconds) × 10^3 + abs(nanoseconds).
auto fraction = fabs ( milliseconds ) * 1'000'000 + fabs ( microseconds ) * 1'000 + fabs ( nanoseconds ) ;
2022-04-12 22:20:33 +01:00
// b. Let decimalPart be ToZeroPaddedDecimalString(fraction, 9).
2021-11-07 01:41:52 +00:00
// NOTE: padding with zeros leads to weird results when applied to a double. Not sure if that's a bug in AK/Format.h or if I'm doing this wrong.
2023-01-26 15:40:58 +00:00
auto decimal_part_string = TRY_OR_THROW_OOM ( vm , String : : formatted ( " {:09} " , ( u64 ) fraction ) ) ;
StringView decimal_part ;
2021-11-07 01:41:52 +00:00
// c. If precision is "auto", then
if ( precision . has < StringView > ( ) & & precision . get < StringView > ( ) = = " auto " sv ) {
// i. Set decimalPart to the longest possible substring of decimalPart starting at position 0 and not ending with the code unit 0x0030 (DIGIT ZERO).
2023-01-26 15:40:58 +00:00
decimal_part = decimal_part_string . bytes_as_string_view ( ) . trim ( " 0 " sv , TrimMode : : Right ) ;
2021-11-07 01:41:52 +00:00
}
// d. Else if precision = 0, then
else if ( precision . get < u8 > ( ) = = 0 ) {
// i. Set decimalPart to "".
2023-01-26 15:40:58 +00:00
decimal_part = " " sv ;
2021-11-07 01:41:52 +00:00
}
// e. Else,
else {
// i. Set decimalPart to the substring of decimalPart from 0 to precision.
2023-01-26 15:40:58 +00:00
decimal_part = decimal_part_string . bytes_as_string_view ( ) . substring_view ( 0 , precision . get < u8 > ( ) ) ;
2021-11-07 01:41:52 +00:00
}
// f. Let secondsPart be abs(seconds) formatted as a decimal number.
StringBuilder seconds_part ;
seconds_part . appendff ( " {} " , fabs ( seconds ) ) ;
// g. If decimalPart is not "", then
if ( ! decimal_part . is_empty ( ) ) {
// i. Set secondsPart to the string-concatenation of secondsPart, the code unit 0x002E (FULL STOP), and decimalPart.
seconds_part . append ( ' . ' ) ;
seconds_part . append ( decimal_part ) ;
}
// h. Set timePart to the string concatenation of timePart, secondsPart, and the code unit 0x0053 (LATIN CAPITAL LETTER S).
time_part . append ( seconds_part . string_view ( ) ) ;
time_part . append ( ' S ' ) ;
}
2022-05-07 16:26:32 +02:00
// 17. Let signPart be the code unit 0x002D (HYPHEN-MINUS) if sign < 0, and otherwise the empty String.
2021-11-07 01:41:52 +00:00
auto sign_part = sign < 0 ? " - " sv : " " sv ;
2022-05-07 16:26:32 +02:00
// 18. Let result be the string concatenation of signPart, the code unit 0x0050 (LATIN CAPITAL LETTER P) and datePart.
2021-11-07 01:41:52 +00:00
StringBuilder result ;
result . append ( sign_part ) ;
result . append ( ' P ' ) ;
result . append ( date_part . string_view ( ) ) ;
2022-05-07 16:26:32 +02:00
// 19. If timePart is not "", then
2021-11-07 01:41:52 +00:00
if ( ! time_part . is_empty ( ) ) {
// a. Set result to the string concatenation of result, the code unit 0x0054 (LATIN CAPITAL LETTER T), and timePart.
result . append ( ' T ' ) ;
result . append ( time_part . string_view ( ) ) ;
}
2022-05-07 16:26:32 +02:00
// 20. Return result.
2023-01-26 15:40:58 +00:00
return TRY_OR_THROW_OOM ( vm , result . to_string ( ) ) ;
2021-11-07 01:41:52 +00:00
}
2022-06-09 20:56:03 +01:00
// 7.5.28 AddDurationToOrSubtractDurationFromDuration ( operation, duration, other, options ), https://tc39.es/proposal-temporal/#sec-temporal-adddurationtoorsubtractdurationfromduration
2022-08-20 08:52:42 +01:00
ThrowCompletionOr < Duration * > add_duration_to_or_subtract_duration_from_duration ( VM & vm , ArithmeticOperation operation , Duration const & duration , Value other_value , 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. Set other to ? ToTemporalDurationRecord(other).
2022-08-20 08:52:42 +01:00
auto other = TRY ( to_temporal_duration_record ( vm , other_value ) ) ;
2022-05-06 19:01:15 +02:00
// 3. Set options to ? GetOptionsObject(options).
2022-08-20 08:52:42 +01:00
auto const * options = TRY ( get_options_object ( vm , options_value ) ) ;
2022-05-06 19:01:15 +02:00
// 4. Let relativeTo be ? ToRelativeTemporalObject(options).
2022-08-20 08:52:42 +01:00
auto relative_to = TRY ( to_relative_temporal_object ( vm , * options ) ) ;
2022-05-06 19:01:15 +02:00
// 5. Let result be ? AddDuration(duration.[[Years]], duration.[[Months]], duration.[[Weeks]], duration.[[Days]], duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]], duration.[[Milliseconds]], duration.[[Microseconds]], duration.[[Nanoseconds]], sign × other.[[Years]], sign × other.[[Months]], sign × other.[[Weeks]], sign × other.[[Days]], sign × other.[[Hours]], sign × other.[[Minutes]], sign × other.[[Seconds]], sign × other.[[Milliseconds]], sign × other.[[Microseconds]], sign × other.[[Nanoseconds]], relativeTo).
2022-08-20 08:52:42 +01:00
auto result = TRY ( add_duration ( vm , duration . years ( ) , duration . months ( ) , duration . weeks ( ) , duration . days ( ) , duration . hours ( ) , duration . minutes ( ) , duration . seconds ( ) , duration . milliseconds ( ) , duration . microseconds ( ) , duration . nanoseconds ( ) , sign * other . years , sign * other . months , sign * other . weeks , sign * other . days , sign * other . hours , sign * other . minutes , sign * other . seconds , sign * other . milliseconds , sign * other . microseconds , sign * other . nanoseconds , relative_to ) ) ;
2022-05-06 19:01:15 +02:00
// 6. Return ! CreateTemporalDuration(result.[[Years]], result.[[Months]], result.[[Weeks]], result.[[Days]], result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]]).
2022-08-20 08:52:42 +01:00
return MUST ( create_temporal_duration ( vm , result . years , result . months , result . weeks , result . days , result . hours , result . minutes , result . seconds , result . milliseconds , result . microseconds , result . nanoseconds ) ) ;
2022-05-06 19:01:15 +02:00
}
2021-07-15 23:20:43 +01:00
}