2021-07-15 23:20:43 +01:00
/*
* Copyright ( c ) 2021 , Linus Groh < linusg @ serenityos . org >
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
2021-11-07 01:41:52 +00:00
# include <AK/StringBuilder.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>
# include <LibJS/Runtime/Temporal/PlainDateTime.h>
# include <LibJS/Runtime/Temporal/TimeZone.h>
2021-09-06 21:21:57 +03:00
# include <LibJS/Runtime/Temporal/ZonedDateTime.h>
2021-07-15 23:20:43 +01:00
namespace JS : : Temporal {
// 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 )
: Object ( prototype )
, 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 )
{
}
2021-07-18 21:44:05 +01:00
// 7.5.1 ToTemporalDuration ( item ), https://tc39.es/proposal-temporal/#sec-temporal-totemporalduration
2021-09-16 22:07:14 +01:00
ThrowCompletionOr < Duration * > to_temporal_duration ( GlobalObject & global_object , Value item )
2021-07-18 21:44:05 +01:00
{
2021-09-16 22:07:14 +01:00
TemporalDuration result ;
2021-07-18 21:44:05 +01:00
// 1. If Type(item) is Object, then
if ( item . is_object ( ) ) {
// a. If item has an [[InitializedTemporalDuration]] internal slot, then
if ( is < Duration > ( item . as_object ( ) ) ) {
// i. Return item.
return & static_cast < Duration & > ( item . as_object ( ) ) ;
}
// b. Let result be ? ToTemporalDurationRecord(item).
2021-09-16 22:07:14 +01:00
result = TRY ( to_temporal_duration_record ( global_object , item . as_object ( ) ) ) ;
2021-07-18 21:44:05 +01:00
}
// 2. Else,
else {
// a. Let string be ? ToString(item).
2021-10-12 17:49:01 +01:00
auto string = TRY ( item . to_string ( global_object ) ) ;
2021-07-18 21:44:05 +01:00
// b. Let result be ? ParseTemporalDurationString(string).
2021-09-16 22:07:14 +01:00
result = TRY ( parse_temporal_duration_string ( global_object , string ) ) ;
2021-07-18 21:44:05 +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]]).
2021-09-16 22:07:14 +01:00
return create_temporal_duration ( global_object , result . years , result . months , result . weeks , result . days , result . hours , result . minutes , result . seconds , result . milliseconds , result . microseconds , result . nanoseconds ) ;
2021-07-18 21:44:05 +01:00
}
// 7.5.2 ToTemporalDurationRecord ( temporalDurationLike ), https://tc39.es/proposal-temporal/#sec-temporal-totemporaldurationrecord
2021-09-16 22:07:14 +01:00
ThrowCompletionOr < TemporalDuration > to_temporal_duration_record ( GlobalObject & global_object , Object const & temporal_duration_like )
2021-07-18 21:44:05 +01:00
{
auto & vm = global_object . vm ( ) ;
// 1. Assert: Type(temporalDurationLike) is Object.
// 2. If temporalDurationLike has an [[InitializedTemporalDuration]] internal slot, then
if ( is < Duration > ( temporal_duration_like ) ) {
2021-09-09 21:51:26 +01:00
auto & duration = static_cast < Duration const & > ( temporal_duration_like ) ;
2021-07-18 21:44:05 +01:00
// a. Return the Record { [[Years]]: temporalDurationLike.[[Years]], [[Months]]: temporalDurationLike.[[Months]], [[Weeks]]: temporalDurationLike.[[Weeks]], [[Days]]: temporalDurationLike.[[Days]], [[Hours]]: temporalDurationLike.[[Hours]], [[Minutes]]: temporalDurationLike.[[Minutes]], [[Seconds]]: temporalDurationLike.[[Seconds]], [[Milliseconds]]: temporalDurationLike.[[Milliseconds]], [[Microseconds]]: temporalDurationLike.[[Microseconds]], [[Nanoseconds]]: temporalDurationLike.[[Nanoseconds]] }.
return TemporalDuration { . years = duration . years ( ) , . months = duration . months ( ) , . weeks = duration . weeks ( ) , . days = duration . days ( ) , . hours = duration . hours ( ) , . minutes = duration . minutes ( ) , . seconds = duration . seconds ( ) , . milliseconds = duration . milliseconds ( ) , . microseconds = duration . microseconds ( ) , . nanoseconds = duration . nanoseconds ( ) } ;
}
// 3. Let result be a new Record with all the internal slots given in the Internal Slot column in Table 7.
auto result = TemporalDuration { } ;
// 4. Let any be false.
auto any = false ;
// 5. For each row of Table 7, except the header row, in table order, do
for ( auto & [ internal_slot , property ] : temporal_duration_like_properties < TemporalDuration , double > ( vm ) ) {
// a. Let prop be the Property value of the current row.
// b. Let val be ? Get(temporalDurationLike, prop).
2021-10-02 23:52:27 +01:00
auto value = TRY ( temporal_duration_like . get ( property ) ) ;
2021-07-18 21:44:05 +01:00
2021-09-01 00:19:03 +01:00
// c. If val is undefined, then
if ( value . is_undefined ( ) ) {
// i. Set result's internal slot whose name is the Internal Slot value of the current row to 0.
result . * internal_slot = 0 ;
2021-07-18 21:44:05 +01:00
}
2021-09-01 00:19:03 +01:00
// d. Else,
2021-07-18 21:44:05 +01:00
else {
2021-09-01 00:19:03 +01:00
// i. Set any to true.
any = true ;
2021-07-18 21:44:05 +01:00
2021-11-17 22:20:59 +00:00
// ii. Let val be 𝔽 (? ToIntegerWithoutRounding(val)).
value = Value ( TRY ( to_integer_without_rounding ( global_object , value , ErrorType : : TemporalInvalidDurationPropertyValueNonIntegral , property . as_string ( ) , value . to_string_without_side_effects ( ) ) ) ) ;
2021-09-01 00:19:03 +01:00
2021-11-17 22:20:59 +00:00
// iii. Set result's internal slot whose name is the Internal Slot value of the current row to val.
2021-09-01 00:19:03 +01:00
result . * internal_slot = value . as_double ( ) ;
2021-07-18 21:44:05 +01:00
}
}
// 6. If any is false, then
if ( ! any ) {
// a. Throw a TypeError exception.
2021-09-16 22:07:14 +01:00
return vm . throw_completion < TypeError > ( global_object , ErrorType : : TemporalInvalidDurationLikeObject ) ;
2021-07-18 21:44:05 +01:00
}
// 7. Return result.
return result ;
}
2021-08-08 11:04:30 +01:00
// 7.5.3 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 } ) {
// a. If v < 0, return − 1.
if ( v < 0 )
return - 1 ;
// b. If v > 0, return 1.
if ( v > 0 )
return 1 ;
}
// 2. Return 0.
return 0 ;
}
2021-08-08 11:04:30 +01:00
// 7.5.4 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 } ) {
// a. If v is not finite, return false.
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 ;
}
2021-07-16 19:30:52 +01:00
// 7.5.6 ToPartialDuration ( temporalDurationLike ), https://tc39.es/proposal-temporal/#sec-temporal-topartialduration
2021-09-16 22:07:14 +01:00
ThrowCompletionOr < PartialDuration > to_partial_duration ( GlobalObject & global_object , Value temporal_duration_like )
2021-07-16 19:30:52 +01:00
{
auto & vm = global_object . vm ( ) ;
// 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.
return vm . throw_completion < TypeError > ( global_object , ErrorType : : NotAnObject , temporal_duration_like . to_string_without_side_effects ( ) ) ;
2021-07-16 19:30:52 +01:00
}
2021-08-17 20:38:29 +01:00
// 2. Let result be the Record { [[Years]]: undefined, [[Months]]: undefined, [[Weeks]]: undefined, [[Days]]: undefined, [[Hours]]: undefined, [[Minutes]]: undefined, [[Seconds]]: undefined, [[Milliseconds]]: undefined, [[Microseconds]]: undefined, [[Nanoseconds]]: undefined }.
2021-07-16 19:30:52 +01:00
auto result = PartialDuration { } ;
// 3. Let any be false.
auto any = false ;
// 4. For each row of Table 7, except the header row, in table order, do
2021-07-18 22:28:30 +01:00
for ( auto & [ internal_slot , property ] : temporal_duration_like_properties < PartialDuration , Optional < double > > ( vm ) ) {
2021-07-16 19:30:52 +01:00
// a. Let property be the Property value of the current row.
// b. Let value be ? Get(temporalDurationLike, property).
2021-10-02 23:52:27 +01:00
auto value = TRY ( temporal_duration_like . as_object ( ) . get ( property ) ) ;
2021-07-16 19:30:52 +01:00
// c. If value is not undefined, then
if ( ! value . is_undefined ( ) ) {
// i. Set any to true.
any = true ;
2021-11-17 22:20:59 +00:00
// ii. Set value to 𝔽 (? ToIntegerWithoutRounding(value)).
value = Value ( TRY ( to_integer_without_rounding ( global_object , value , ErrorType : : TemporalInvalidDurationPropertyValueNonIntegral , property . as_string ( ) , value . to_string_without_side_effects ( ) ) ) ) ;
2021-09-02 18:58:50 +01:00
2021-11-17 22:20:59 +00:00
// iii. Set result's internal slot whose name is the Internal Slot value of the current row to value.
2021-09-02 18:58:50 +01:00
result . * internal_slot = value . as_double ( ) ;
2021-07-16 19:30:52 +01:00
}
}
// 5. If any is false, then
if ( ! any ) {
// a. Throw a TypeError exception.
2021-09-16 22:07:14 +01:00
return vm . throw_completion < TypeError > ( global_object , ErrorType : : TemporalInvalidDurationLikeObject ) ;
2021-07-16 19:30:52 +01:00
}
// 6. Return result.
return result ;
}
2021-07-15 23:20:43 +01:00
// 7.5.7 CreateTemporalDuration ( years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds [ , newTarget ] ), https://tc39.es/proposal-temporal/#sec-temporal-createtemporalduration
2021-09-16 22:07:14 +01:00
ThrowCompletionOr < Duration * > create_temporal_duration ( GlobalObject & global_object , 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
{
auto & vm = global_object . vm ( ) ;
// 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 ) )
return vm . throw_completion < RangeError > ( global_object , ErrorType : : TemporalInvalidDuration ) ;
2021-07-15 23:20:43 +01:00
// 2. If newTarget is not present, set it to %Temporal.Duration%.
if ( ! new_target )
new_target = global_object . temporal_duration_constructor ( ) ;
// 3. Let object be ? OrdinaryCreateFromConstructor(newTarget, "%Temporal.Duration.prototype%", « [[InitializedTemporalDuration]], [[Years]], [[Months]], [[Weeks]], [[Days]], [[Hours]], [[Minutes]], [[Seconds]], [[Milliseconds]], [[Microseconds]], [[Nanoseconds]] »).
// 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.
2021-09-16 22:07:14 +01:00
auto * object = TRY ( ordinary_create_from_constructor < Duration > ( global_object , * new_target , & GlobalObject : : temporal_duration_prototype , years , months , weeks , days , hours , minutes , seconds , milliseconds , microseconds , nanoseconds ) ) ;
2021-07-15 23:20:43 +01:00
// 14. Return object.
return object ;
}
2021-09-03 00:10:09 +01:00
// 7.5.8 CreateNegatedTemporalDuration ( duration ), https://tc39.es/proposal-temporal/#sec-temporal-createnegatedtemporalduration
Duration * create_negated_temporal_duration ( GlobalObject & global_object , Duration const & duration )
{
// 1. Assert: Type(duration) is Object.
// 2. Assert: duration has an [[InitializedTemporalDuration]] internal slot.
// 3. Return ! CreateTemporalDuration(− duration.[[Years]], − duration.[[Months]], − duration.[[Weeks]], − duration.[[Days]], − duration.[[Hours]], − duration.[[Minutes]], − duration.[[Seconds]], − duration.[[Milliseconds]], − duration.[[Microseconds]], − duration.[[Nanoseconds]]).
2021-10-03 15:57:26 +01:00
return MUST ( create_temporal_duration ( global_object , - 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
}
2021-11-09 17:19:28 +00:00
// 7.5.9 CalculateOffsetShift ( relativeTo, y, mon, w, d, h, min, s, ms, mus, ns ), https://tc39.es/proposal-temporal/#sec-temporal-calculateoffsetshift
ThrowCompletionOr < double > calculate_offset_shift ( GlobalObject & global_object , Value relative_to_value , double years , double months , double weeks , double days , double hours , double minutes , double seconds , double milliseconds , double microseconds , double nanoseconds )
{
// 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]]).
auto * instant = MUST ( create_temporal_instant ( global_object , relative_to . nanoseconds ( ) ) ) ;
// 3. Let offsetBefore be ? GetOffsetNanosecondsFor(relativeTo.[[TimeZone]], instant).
auto offset_before = TRY ( get_offset_nanoseconds_for ( global_object , & relative_to . time_zone ( ) , * instant ) ) ;
// 4. Let after be ? AddZonedDateTime(relativeTo.[[Nanoseconds]], relativeTo.[[TimeZone]], relativeTo.[[Calendar]], y, mon, w, d, h, min, s, ms, mus, ns).
auto * after = TRY ( add_zoned_date_time ( global_object , relative_to . nanoseconds ( ) , & relative_to . time_zone ( ) , relative_to . calendar ( ) , years , months , weeks , days , hours , minutes , seconds , milliseconds , microseconds , nanoseconds ) ) ;
// 5. Let instantAfter be ! CreateTemporalInstant(after).
auto * instant_after = MUST ( create_temporal_instant ( global_object , * after ) ) ;
// 6. Let offsetAfter be ? GetOffsetNanosecondsFor(relativeTo.[[TimeZone]], instantAfter).
auto offset_after = TRY ( get_offset_nanoseconds_for ( global_object , & relative_to . time_zone ( ) , * instant_after ) ) ;
// 7. Return offsetAfter − offsetBefore.
return offset_after - offset_before ;
}
2021-09-06 21:21:57 +03:00
// 7.5.10 TotalDurationNanoseconds ( days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, offsetShift ), https://tc39.es/proposal-temporal/#sec-temporal-totaldurationnanoseconds
BigInt * total_duration_nanoseconds ( GlobalObject & global_object , double days , double hours , double minutes , double seconds , double milliseconds , double microseconds , BigInt const & nanoseconds , double offset_shift )
{
auto & vm = global_object . vm ( ) ;
// 1. Assert: offsetShift is an integer.
VERIFY ( offset_shift = = trunc ( offset_shift ) ) ;
// 2. Set nanoseconds to ℝ (nanoseconds).
auto result_nanoseconds = nanoseconds . big_integer ( ) ;
// TODO: Add a way to create SignedBigIntegers from doubles with full precision and remove this restriction
VERIFY ( AK : : is_within_range < i64 > ( days ) & & AK : : is_within_range < i64 > ( hours ) & & AK : : is_within_range < i64 > ( minutes ) & & AK : : is_within_range < i64 > ( seconds ) & & AK : : is_within_range < i64 > ( milliseconds ) & & AK : : is_within_range < i64 > ( microseconds ) ) ;
// 3. If days ≠ 0, then
if ( days ! = 0 ) {
// a. Set nanoseconds to nanoseconds − offsetShift.
result_nanoseconds = result_nanoseconds . minus ( Crypto : : SignedBigInteger : : create_from ( offset_shift ) ) ;
}
// 4. Set hours to ℝ (hours) + ℝ (days) × 24.
auto total_hours = Crypto : : SignedBigInteger : : create_from ( hours ) . plus ( Crypto : : SignedBigInteger : : create_from ( days ) . multiplied_by ( Crypto : : UnsignedBigInteger ( 24 ) ) ) ;
// 5. Set minutes to ℝ (minutes) + hours × 60.
auto total_minutes = Crypto : : SignedBigInteger : : create_from ( minutes ) . plus ( total_hours . multiplied_by ( Crypto : : UnsignedBigInteger ( 60 ) ) ) ;
// 6. Set seconds to ℝ (seconds) + minutes × 60.
auto total_seconds = Crypto : : SignedBigInteger : : create_from ( seconds ) . plus ( total_minutes . multiplied_by ( Crypto : : UnsignedBigInteger ( 60 ) ) ) ;
// 7. Set milliseconds to ℝ (milliseconds) + seconds × 1000.
auto total_milliseconds = Crypto : : SignedBigInteger : : create_from ( milliseconds ) . plus ( total_seconds . multiplied_by ( Crypto : : UnsignedBigInteger ( 1000 ) ) ) ;
// 8. Set microseconds to ℝ (microseconds) + milliseconds × 1000.
auto total_microseconds = Crypto : : SignedBigInteger : : create_from ( microseconds ) . plus ( total_milliseconds . multiplied_by ( Crypto : : UnsignedBigInteger ( 1000 ) ) ) ;
// 9. Return nanoseconds + microseconds × 1000.
return js_bigint ( vm , result_nanoseconds . plus ( total_microseconds . multiplied_by ( Crypto : : UnsignedBigInteger ( 1000 ) ) ) ) ;
}
// 7.5.11 BalanceDuration ( days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, largestUnit [ , relativeTo ] ), https://tc39.es/proposal-temporal/#sec-temporal-balanceduration
2021-09-16 22:07:14 +01:00
ThrowCompletionOr < BalancedDuration > balance_duration ( GlobalObject & global_object , double days , double hours , double minutes , double seconds , double milliseconds , double microseconds , BigInt const & nanoseconds , String const & largest_unit , Object * relative_to )
2021-09-06 21:21:57 +03:00
{
auto & vm = global_object . vm ( ) ;
2021-11-02 00:14:47 +01:00
2021-09-06 21:21:57 +03:00
// 1. If relativeTo is not present, set relativeTo to undefined.
Crypto : : SignedBigInteger total_nanoseconds ;
// 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).
2021-11-02 00:14:47 +01:00
auto * end_ns = TRY ( add_zoned_date_time ( global_object , 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 . big_integer ( ) . to_double ( ) ) ) ;
2021-09-06 21:21:57 +03:00
// b. Set nanoseconds to endNs − relativeTo.[[Nanoseconds]].
2021-11-02 00:14:47 +01: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 {
// a. Set nanoseconds to ℤ (! TotalDurationNanoseconds(days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, 0)).
total_nanoseconds = total_duration_nanoseconds ( global_object , days , hours , minutes , seconds , milliseconds , microseconds , nanoseconds , 0 ) - > big_integer ( ) ;
}
// 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).
2021-11-02 00:14:47 +01:00
auto result = TRY ( nanoseconds_to_days ( global_object , * js_bigint ( 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]].
2021-11-02 00:14:47 +01:00
total_nanoseconds = result . nanoseconds . cell ( ) - > big_integer ( ) ;
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 ;
// 7. Set nanoseconds to ℝ (nanoseconds).
double result_nanoseconds = total_nanoseconds . to_double ( ) ;
// 8. If nanoseconds < 0, let sign be − 1; else, let sign be 1.
i8 sign = total_nanoseconds . is_negative ( ) ? - 1 : 1 ;
// 9. Set nanoseconds to abs(nanoseconds).
total_nanoseconds = Crypto : : SignedBigInteger ( total_nanoseconds . unsigned_value ( ) ) ;
result_nanoseconds = fabs ( result_nanoseconds ) ;
// 10. 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 ( ) ;
}
// 11. Else if largestUnit is "minute", then
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 ( ) ;
}
// 12. Else if largestUnit is "second", then
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 ( ) ;
}
// 13. Else if largestUnit is "millisecond", then
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 ( ) ;
}
// 14. Else if largestUnit is "microsecond", then
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 ( ) ;
}
// 15. Else,
else {
// a. Assert: largestUnit is "nanosecond".
VERIFY ( largest_unit = = " nanosecond " sv ) ;
}
// 16. Return the Record { [[Days]]: 𝔽 (days), [[Hours]]: 𝔽 (hours × sign), [[Minutes]]: 𝔽 (minutes × sign), [[Seconds]]: 𝔽 (seconds × sign), [[Milliseconds]]: 𝔽 (milliseconds × sign), [[Microseconds]]: 𝔽 (microseconds × sign), [[Nanoseconds]]: 𝔽 (nanoseconds × sign) }.
return BalancedDuration { . days = days , . hours = hours * sign , . minutes = minutes * sign , . seconds = seconds * sign , . milliseconds = milliseconds * sign , . microseconds = microseconds * sign , . nanoseconds = result_nanoseconds * sign } ;
}
2021-11-09 17:19:28 +00:00
// 7.5.12 UnbalanceDurationRelative ( years, months, weeks, days, largestUnit, relativeTo ), https://tc39.es/proposal-temporal/#sec-temporal-unbalancedurationrelative
ThrowCompletionOr < UnbalancedDuration > unbalance_duration_relative ( GlobalObject & global_object , double years , double months , double weeks , double days , String const & largest_unit , Value relative_to )
{
auto & vm = global_object . vm ( ) ;
// 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 ) ) {
// a. Return the Record { [[Years]]: years, [[Months]]: months, [[Weeks]]: weeks, [[Days]]: days }.
return UnbalancedDuration { . years = years , . months = months , . weeks = weeks , . days = days } ;
}
// 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).
auto * one_year = MUST ( create_temporal_duration ( global_object , sign , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ) ) ;
// 5. Let oneMonth be ! CreateTemporalDuration(0, sign, 0, 0, 0, 0, 0, 0, 0, 0).
auto * one_month = MUST ( create_temporal_duration ( global_object , 0 , sign , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ) ) ;
// 6. Let oneWeek be ! CreateTemporalDuration(0, 0, sign, 0, 0, 0, 0, 0, 0, 0).
auto * one_week = MUST ( create_temporal_duration ( global_object , 0 , 0 , sign , 0 , 0 , 0 , 0 , 0 , 0 , 0 ) ) ;
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).
auto * relative_to_plain_date = TRY ( to_temporal_date ( global_object , relative_to ) ) ;
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.
return vm . throw_completion < RangeError > ( global_object , ErrorType : : TemporalMissingStartingPoint , " months " ) ;
}
// b. Let dateAdd be ? GetMethod(calendar, "dateAdd").
auto * date_add = TRY ( Value ( calendar ) . get_method ( global_object , vm . names . dateAdd ) ) ;
// c. Let dateUntil be ? GetMethod(calendar, "dateUntil").
auto * date_until = TRY ( Value ( calendar ) . get_method ( global_object , vm . names . dateUntil ) ) ;
// d. Repeat, while years ≠ 0,
while ( years ! = 0 ) {
// i. Let addOptions be ! OrdinaryObjectCreate(null).
auto * add_options = Object : : create ( global_object , nullptr ) ;
// ii. Let newRelativeTo be ? CalendarDateAdd(calendar, relativeTo, oneYear, addOptions, dateAdd).
auto * new_relative_to = TRY ( calendar_date_add ( global_object , * calendar , relative_to , * one_year , add_options , date_add ) ) ;
// iii. Let untilOptions be ! OrdinaryObjectCreate(null).
auto * until_options = Object : : create ( global_object , nullptr ) ;
// iv. Perform ! CreateDataPropertyOrThrow(untilOptions, "largestUnit", "month").
MUST ( until_options - > create_data_property_or_throw ( vm . names . largestUnit , js_string ( vm , " month " sv ) ) ) ;
// v. Let untilResult be ? CalendarDateUntil(calendar, relativeTo, newRelativeTo, untilOptions, dateUntil).
auto * until_result = TRY ( calendar_date_until ( global_object , * calendar , relative_to , new_relative_to , * until_options , date_until ) ) ;
// vi. Let oneYearMonths be untilResult.[[Months]].
auto one_year_months = until_result - > months ( ) ;
// vii. Set relativeTo to newRelativeTo.
relative_to = new_relative_to ;
// viii. Set years to years − sign.
years - = sign ;
// ix. Set months to months + oneYearMonths.
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.
return vm . throw_completion < RangeError > ( global_object , ErrorType : : TemporalMissingStartingPoint , " weeks " ) ;
}
// b. Repeat, while years ≠ 0,
while ( years ! = 0 ) {
// i. Let moveResult be ? MoveRelativeDate(calendar, relativeTo, oneYear).
2021-11-19 19:59:13 +00:00
auto move_result = TRY ( move_relative_date ( global_object , * calendar , verify_cast < PlainDate > ( relative_to . as_object ( ) ) , * one_year ) ) ;
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 ;
// iv. Set years to years − sign.
years - = sign ;
}
// c. Repeat, while months ≠ 0,
while ( months ! = 0 ) {
// i. Let moveResult be ? MoveRelativeDate(calendar, relativeTo, oneMonth).
2021-11-19 19:59:13 +00:00
auto move_result = TRY ( move_relative_date ( global_object , * calendar , verify_cast < PlainDate > ( relative_to . as_object ( ) ) , * one_month ) ) ;
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 ;
// iv. Set months to months − sign.
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.
return vm . throw_completion < RangeError > ( global_object , ErrorType : : TemporalMissingStartingPoint , " calendar units " ) ;
}
// ii. Repeat, while years ≠ 0,
while ( years ! = 0 ) {
// 1. Let moveResult be ? MoveRelativeDate(calendar, relativeTo, oneYear).
2021-11-19 19:59:13 +00:00
auto move_result = TRY ( move_relative_date ( global_object , * calendar , verify_cast < PlainDate > ( relative_to . as_object ( ) ) , * one_year ) ) ;
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 ;
// 4. Set years to years − sign.
years - = sign ;
}
// iii. Repeat, while months ≠ 0,
while ( months ! = 0 ) {
// 1. Let moveResult be ? MoveRelativeDate(calendar, relativeTo, oneMonth).
2021-11-19 19:59:13 +00:00
auto move_result = TRY ( move_relative_date ( global_object , * calendar , verify_cast < PlainDate > ( relative_to . as_object ( ) ) , * one_month ) ) ;
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 ;
// 4. Set months to months − sign.
months - = sign ;
}
// iv. Repeat, while weeks ≠ 0,
while ( weeks ! = 0 ) {
// 1. Let moveResult be ? MoveRelativeDate(calendar, relativeTo, oneWeek).
2021-11-19 19:59:13 +00:00
auto move_result = TRY ( move_relative_date ( global_object , * calendar , verify_cast < PlainDate > ( relative_to . as_object ( ) ) , * one_week ) ) ;
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 ;
// 4. Set weeks to weeks − sign.
weeks - = sign ;
}
}
}
// 12. Return the Record { [[Years]]: years, [[Months]]: months, [[Weeks]]: weeks, [[Days]]: days }.
return UnbalancedDuration { . years = years , . months = months , . weeks = weeks , . days = days } ;
}
2021-11-07 01:41:52 +00:00
// 7.5.16 MoveRelativeDate ( calendar, relativeTo, duration ), https://tc39.es/proposal-temporal/#sec-temporal-moverelativedate
2021-11-19 19:59:13 +00:00
ThrowCompletionOr < MoveRelativeDateResult > move_relative_date ( GlobalObject & global_object , Object & calendar , PlainDate & relative_to , Duration & duration )
2021-11-07 01:41:52 +00:00
{
// 1. Assert: Type(relativeTo) is Object.
2021-11-19 19:59:13 +00:00
// 2. Assert: relativeTo has an [[InitializedTemporalDate]] internal slot.
2021-11-07 01:41:52 +00:00
// 3. Let options be ! OrdinaryObjectCreate(null).
auto * options = Object : : create ( global_object , nullptr ) ;
2021-11-19 19:59:13 +00:00
// 4. Let newDate be ? CalendarDateAdd(calendar, relativeTo, duration, options).
auto * new_date = TRY ( calendar_date_add ( global_object , calendar , & relative_to , duration , options ) ) ;
2021-11-07 01:41:52 +00:00
2021-11-19 19:59:13 +00:00
// 5. Let days be ! DaysUntil(relativeTo, newDate).
auto days = days_until ( global_object , relative_to , * new_date ) ;
2021-11-07 01:41:52 +00:00
2021-11-19 19:59:13 +00:00
// 6. Return the Record { [[RelativeTo]]: newDate, [[Days]]: days }.
return MoveRelativeDateResult { . relative_to = make_handle ( new_date ) , . days = days } ;
2021-11-07 01:41:52 +00:00
}
// 7.5.17 MoveRelativeZonedDateTime ( zonedDateTime, years, months, weeks, days ), https://tc39.es/proposal-temporal/#sec-temporal-moverelativezoneddatetime
ThrowCompletionOr < ZonedDateTime * > move_relative_zoned_date_time ( GlobalObject & global_object , ZonedDateTime & zoned_date_time , double years , double months , double weeks , double days )
{
// 1. Let intermediateNs be ? AddZonedDateTime(zonedDateTime.[[Nanoseconds]], zonedDateTime.[[TimeZone]], zonedDateTime.[[Calendar]], years, months, weeks, days, 0, 0, 0, 0, 0, 0).
auto * intermediate_ns = TRY ( add_zoned_date_time ( global_object , zoned_date_time . nanoseconds ( ) , & zoned_date_time . time_zone ( ) , zoned_date_time . calendar ( ) , years , months , weeks , days , 0 , 0 , 0 , 0 , 0 , 0 ) ) ;
// 2. Return ! CreateTemporalZonedDateTime(intermediateNs, zonedDateTime.[[TimeZone]], zonedDateTime.[[Calendar]]).
return MUST ( create_temporal_zoned_date_time ( global_object , * intermediate_ns , zoned_date_time . time_zone ( ) , zoned_date_time . calendar ( ) ) ) ;
}
// 7.5.18 RoundDuration ( years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, increment, unit, roundingMode [ , relativeTo ] ), https://tc39.es/proposal-temporal/#sec-temporal-roundduration
ThrowCompletionOr < RoundedDuration > round_duration ( GlobalObject & global_object , 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 )
{
auto & vm = global_object . vm ( ) ;
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
// 2. Let years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, and increment each be the mathematical values of themselves.
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
2021-11-07 01:41:52 +00:00
// 3. If unit is "year", "month", or "week", and relativeTo is undefined, then
if ( unit . is_one_of ( " year " sv , " month " sv , " week " sv ) & & ! relative_to_object ) {
// a. Throw a RangeError exception.
return vm . throw_completion < RangeError > ( global_object , ErrorType : : OptionIsNotValidValue , unit , " smallestUnit " sv ) ;
}
// 4. Let zonedRelativeTo be undefined.
ZonedDateTime * zoned_relative_to = nullptr ;
// 5. If relativeTo is not undefined, then
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 ) ;
// i. Let instant be ! CreateTemporalInstant(relativeTo.[[Nanoseconds]]).
auto * instant = MUST ( create_temporal_instant ( global_object , relative_to_zoned_date_time - > nanoseconds ( ) ) ) ;
// ii. Set zonedRelativeTo to relativeTo.
zoned_relative_to = relative_to_zoned_date_time ;
2021-11-19 19:59:13 +00:00
// iii. Let plainDateTime be ? BuiltinTimeZoneGetPlainDateTimeFor(relativeTo.[[TimeZone]], instant, relativeTo.[[Calendar]]).
auto * plain_date_time = TRY ( builtin_time_zone_get_plain_date_time_for ( global_object , & relative_to_zoned_date_time - > time_zone ( ) , * instant , relative_to_zoned_date_time - > calendar ( ) ) ) ;
// iv. Set relativeTo to ! CreateTemporalDate(plainDateTime.[[ISOYear]], plainDateTime.[[ISOMonth]], plainDateTime.[[ISODay]], relativeTo.[[Calendar]]).
relative_to = TRY ( create_temporal_date ( global_object , plain_date_time - > iso_year ( ) , plain_date_time - > iso_month ( ) , plain_date_time - > iso_day ( ) , relative_to_zoned_date_time - > calendar ( ) ) ) ;
2021-11-07 01:41:52 +00:00
}
// b. Else,
else {
2021-11-19 19:59:13 +00:00
// i. Assert: relativeTo has an [[InitializedTemporalDate]] internal slot.
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 ( ) ;
}
// 6. If unit is one of "year", "month", "week", or "day", then
if ( unit . is_one_of ( " year " sv , " month " sv , " week " sv , " day " sv ) ) {
auto * nanoseconds_bigint = js_bigint ( vm , Crypto : : SignedBigInteger : : create_from ( ( i64 ) nanoseconds ) ) ;
// a. Let nanoseconds be ! TotalDurationNanoseconds(0, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, 0).
nanoseconds_bigint = total_duration_nanoseconds ( global_object , 0 , hours , minutes , seconds , milliseconds , microseconds , * nanoseconds_bigint , 0 ) ;
// 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).
intermediate = TRY ( move_relative_zoned_date_time ( global_object , * zoned_relative_to , years , months , weeks , days ) ) ;
}
// d. Let result be ? NanosecondsToDays(nanoseconds, intermediate).
auto result = TRY ( nanoseconds_to_days ( global_object , * nanoseconds_bigint , intermediate ) ) ;
// e. Set days to days + result.[[Days]] + result.[[Nanoseconds]] / result.[[DayLength]].
2021-11-13 16:53:12 +00:00
auto nanoseconds_division_result = result . nanoseconds . cell ( ) - > big_integer ( ) . divided_by ( Crypto : : UnsignedBigInteger : : create_from ( ( u64 ) result . day_length ) ) ;
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 ;
}
// 7. Else,
else {
// a. Let fractionalSeconds be nanoseconds × 10^− 9 + microseconds × 10^− 6 + milliseconds × 10^− 3 + seconds.
fractional_seconds = nanoseconds * 0.000000001 + microseconds * 0.000001 + milliseconds * 0.001 + seconds ;
}
// 8. Let remainder be undefined.
double remainder = 0 ;
// 9. If unit is "year", then
if ( unit = = " year " sv ) {
VERIFY ( relative_to ) ;
// a. Let yearsDuration be ? CreateTemporalDuration(years, 0, 0, 0, 0, 0, 0, 0, 0, 0).
auto * years_duration = TRY ( create_temporal_duration ( global_object , years , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ) ) ;
// b. Let dateAdd be ? GetMethod(calendar, "dateAdd").
auto * date_add = TRY ( Value ( calendar ) . get_method ( global_object , vm . names . dateAdd ) ) ;
// c. Let firstAddOptions be ! OrdinaryObjectCreate(null).
auto * first_add_options = Object : : create ( global_object , nullptr ) ;
// d. Let yearsLater be ? CalendarDateAdd(calendar, relativeTo, yearsDuration, firstAddOptions, dateAdd).
auto * years_later = TRY ( calendar_date_add ( global_object , * calendar , relative_to , * years_duration , first_add_options , date_add ) ) ;
// e. Let yearsMonthsWeeks be ? CreateTemporalDuration(years, months, weeks, 0, 0, 0, 0, 0, 0, 0).
auto * years_months_weeks = TRY ( create_temporal_duration ( global_object , years , months , weeks , 0 , 0 , 0 , 0 , 0 , 0 , 0 ) ) ;
// f. Let secondAddOptions be ! OrdinaryObjectCreate(null).
auto * second_add_options = Object : : create ( global_object , nullptr ) ;
// g. Let yearsMonthsWeeksLater be ? CalendarDateAdd(calendar, relativeTo, yearsMonthsWeeks, secondAddOptions, dateAdd).
auto * years_months_weeks_later = TRY ( calendar_date_add ( global_object , * calendar , relative_to , * years_months_weeks , second_add_options , date_add ) ) ;
// h. Let monthsWeeksInDays be ? DaysUntil(yearsLater, yearsMonthsWeeksLater).
auto months_weeks_in_days = days_until ( global_object , * years_later , * years_months_weeks_later ) ;
// i. Set relativeTo to yearsLater.
2021-11-19 19:59:13 +00:00
relative_to = years_later ;
2021-11-07 01:41:52 +00:00
// j. Let days be days + monthsWeeksInDays.
days + = months_weeks_in_days ;
// k. Let daysDuration be ? CreateTemporalDuration(0, 0, 0, days, 0, 0, 0, 0, 0, 0).
auto * days_duration = TRY ( create_temporal_duration ( global_object , 0 , 0 , 0 , days , 0 , 0 , 0 , 0 , 0 , 0 ) ) ;
// l. Let thirdAddOptions be ! OrdinaryObjectCreate(null).
auto * third_add_options = Object : : create ( global_object , nullptr ) ;
// m. Let daysLater be ? CalendarDateAdd(calendar, relativeTo, daysDuration, thirdAddOptions, dateAdd).
2021-11-19 19:59:13 +00:00
auto * days_later = TRY ( calendar_date_add ( global_object , * calendar , relative_to , * days_duration , third_add_options , date_add ) ) ;
2021-11-07 01:41:52 +00:00
// n. Let untilOptions be ! OrdinaryObjectCreate(null).
auto * until_options = Object : : create ( global_object , nullptr ) ;
// o. Perform ! CreateDataPropertyOrThrow(untilOptions, "largestUnit", "year").
MUST ( until_options - > create_data_property_or_throw ( vm . names . largestUnit , js_string ( vm , " year " sv ) ) ) ;
// p. Let timePassed be ? CalendarDateUntil(calendar, relativeTo, daysLater, untilOptions).
2021-11-19 19:59:13 +00:00
auto * time_passed = TRY ( calendar_date_until ( global_object , * calendar , relative_to , days_later , * until_options ) ) ;
2021-11-07 01:41:52 +00:00
// q. Let yearsPassed be timePassed.[[Years]].
auto years_passed = time_passed - > years ( ) ;
// r. Set years to years + yearsPassed.
years + = years_passed ;
// s. 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
// t. Let yearsDuration be ? CreateTemporalDuration(yearsPassed, 0, 0, 0, 0, 0, 0, 0, 0, 0).
years_duration = TRY ( create_temporal_duration ( global_object , years_passed , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ) ) ;
// u. Let fourthAddOptions be ! OrdinaryObjectCreate(null).
auto * fourth_add_options = Object : : create ( global_object , nullptr ) ;
// v. Set relativeTo to ? CalendarDateAdd(calendar, relativeTo, yearsDuration, fourthAddOptions, dateAdd).
2021-11-19 19:59:13 +00:00
relative_to = TRY ( calendar_date_add ( global_object , * calendar , relative_to , * years_duration , fourth_add_options , date_add ) ) ;
2021-11-07 01:41:52 +00:00
// w. Let daysPassed be ? DaysUntil(oldRelativeTo, relativeTo).
2021-11-19 19:59:13 +00:00
auto days_passed = days_until ( global_object , * old_relative_to , * relative_to ) ;
2021-11-07 01:41:52 +00:00
// x. Set days to days - daysPassed.
days - = days_passed ;
// y. Let sign be ! Sign(days).
auto sign = JS : : Temporal : : sign ( days ) ;
// z. If sign is 0, set sign to 1.
if ( sign = = 0 )
sign = 1 ;
// aa. Let oneYear be ? CreateTemporalDuration(sign, 0, 0, 0, 0, 0, 0, 0, 0, 0).
auto * one_year = TRY ( create_temporal_duration ( global_object , sign , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ) ) ;
2021-11-19 19:59:13 +00:00
// ab. Let moveResult be ? MoveRelativeDate(calendar, relativeTo, oneYear).
2021-11-07 01:41:52 +00:00
auto move_result = TRY ( move_relative_date ( global_object , * calendar , * relative_to , * one_year ) ) ;
2021-11-19 19:59:13 +00:00
// ac. Let oneYearDays be moveResult.[[Days]].
2021-11-07 01:41:52 +00:00
auto one_year_days = move_result . days ;
2021-11-19 19:59:13 +00:00
// ad. Let fractionalYears be years + days / abs(oneYearDays).
2021-11-07 01:41:52 +00:00
auto fractional_years = years + days / fabs ( one_year_days ) ;
2021-11-19 19:59:13 +00:00
// ae. Set years to ! RoundNumberToIncrement(fractionalYears, increment, roundingMode).
2021-11-07 01:41:52 +00:00
years = ( double ) round_number_to_increment ( fractional_years , increment , rounding_mode ) ;
2021-11-19 19:59:13 +00:00
// af. Set remainder to fractionalYears - years.
2021-11-07 01:41:52 +00:00
remainder = fractional_years - years ;
2021-11-19 19:59:13 +00:00
// ag. Set months, weeks, and days to 0.
2021-11-07 01:41:52 +00:00
months = 0 ;
weeks = 0 ;
days = 0 ;
}
// 10. Else if unit is "month", then
else if ( unit = = " month " sv ) {
VERIFY ( relative_to ) ;
// a. Let yearsMonths be ? CreateTemporalDuration(years, months, 0, 0, 0, 0, 0, 0, 0, 0).
auto * years_months = TRY ( create_temporal_duration ( global_object , years , months , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ) ) ;
// b. Let dateAdd be ? GetMethod(calendar, "dateAdd").
auto * date_add = TRY ( Value ( calendar ) . get_method ( global_object , vm . names . dateAdd ) ) ;
// c. Let firstAddOptions be ! OrdinaryObjectCreate(null).
auto * first_add_options = Object : : create ( global_object , nullptr ) ;
// d. Let yearsMonthsLater be ? CalendarDateAdd(calendar, relativeTo, yearsMonths, firstAddOptions, dateAdd).
auto * years_months_later = TRY ( calendar_date_add ( global_object , * calendar , relative_to , * years_months , first_add_options , date_add ) ) ;
// e. Let yearsMonthsWeeks be ? CreateTemporalDuration(years, months, weeks, 0, 0, 0, 0, 0, 0, 0).
auto * years_months_weeks = TRY ( create_temporal_duration ( global_object , years , months , weeks , 0 , 0 , 0 , 0 , 0 , 0 , 0 ) ) ;
// f. Let secondAddOptions be ! OrdinaryObjectCreate(null).
auto * seconds_add_options = Object : : create ( global_object , nullptr ) ;
// g. Let yearsMonthsWeeksLater be ? CalendarDateAdd(calendar, relativeTo, yearsMonthsWeeks, secondAddOptions, dateAdd).
auto * years_months_weeks_later = TRY ( calendar_date_add ( global_object , * calendar , relative_to , * years_months_weeks , seconds_add_options , date_add ) ) ;
// h. Let weeksInDays be ? DaysUntil(yearsMonthsLater, yearsMonthsWeeksLater).
auto weeks_in_days = days_until ( global_object , * years_months_later , * years_months_weeks_later ) ;
// i. 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
// j. Let days be days + weeksInDays.
days + = weeks_in_days ;
// k. Let sign be ! Sign(days).
auto sign = JS : : Temporal : : sign ( days ) ;
// l. If sign is 0, set sign to 1.
if ( sign = = 0 )
sign = 1 ;
// m. Let oneMonth be ? CreateTemporalDuration(0, sign, 0, 0, 0, 0, 0, 0, 0, 0).
auto * one_month = TRY ( create_temporal_duration ( global_object , 0 , sign , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ) ) ;
2021-11-19 19:59:13 +00:00
// n. Let moveResult be ? MoveRelativeDate(calendar, relativeTo, oneMonth).
2021-11-07 01:41:52 +00:00
auto move_result = TRY ( move_relative_date ( global_object , * calendar , * relative_to , * one_month ) ) ;
2021-11-19 19:59:13 +00:00
// o. Set relativeTo to moveResult.[[RelativeTo]].
2021-11-07 01:41:52 +00:00
relative_to = move_result . relative_to . cell ( ) ;
2021-11-19 19:59:13 +00:00
// p. Let oneMonthDays be moveResult.[[Days]].
2021-11-07 01:41:52 +00:00
auto one_month_days = move_result . days ;
2021-11-19 19:59:13 +00:00
// q. 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 ;
// ii. Set days to days − oneMonthDays.
days - = one_month_days ;
// iii. Set moveResult to ? MoveRelativeDate(calendar, relativeTo, oneMonth).
move_result = TRY ( move_relative_date ( global_object , * calendar , * relative_to , * one_month ) ) ;
// 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 ;
}
2021-11-19 19:59:13 +00:00
// r. Let fractionalMonths be months + days / abs(oneMonthDays).
2021-11-07 01:41:52 +00:00
auto fractional_months = months + days / fabs ( one_month_days ) ;
2021-11-19 19:59:13 +00:00
// s. Set months to ! RoundNumberToIncrement(fractionalMonths, increment, roundingMode).
2021-11-07 01:41:52 +00:00
months = ( double ) round_number_to_increment ( fractional_months , increment , rounding_mode ) ;
2021-11-19 19:59:13 +00:00
// t. Set remainder to fractionalMonths - months.
2021-11-07 01:41:52 +00:00
remainder = fractional_months - months ;
2021-11-19 19:59:13 +00:00
// u. Set weeks and days to 0.
2021-11-07 01:41:52 +00:00
weeks = 0 ;
days = 0 ;
}
// 11. Else if unit is "week", then
else if ( unit = = " week " sv ) {
VERIFY ( relative_to ) ;
// a. Let sign be ! Sign(days).
auto sign = JS : : Temporal : : sign ( days ) ;
// b. If sign is 0, set sign to 1.
if ( sign = = 0 )
sign = 1 ;
// c. Let oneWeek be ? CreateTemporalDuration(0, 0, sign, 0, 0, 0, 0, 0, 0, 0).
auto * one_week = TRY ( create_temporal_duration ( global_object , 0 , 0 , sign , 0 , 0 , 0 , 0 , 0 , 0 , 0 ) ) ;
// d. Let moveResult be ? MoveRelativeDate(calendar, relativeTo, oneWeek).
auto move_result = TRY ( move_relative_date ( global_object , * calendar , * relative_to , * one_week ) ) ;
// e. Set relativeTo to moveResult.[[RelativeTo]].
relative_to = move_result . relative_to . cell ( ) ;
// f. Let oneWeekDays be moveResult.[[Days]].
auto one_week_days = move_result . days ;
// g. Repeat, while abs(days) ≥ abs(oneWeekDays),
while ( fabs ( days ) > = fabs ( one_week_days ) ) {
// i. Set weeks to weeks + sign.
weeks + = sign ;
// ii. Set days to days − oneWeekDays.
days - = one_week_days ;
// iii. Set moveResult to ? MoveRelativeDate(calendar, relativeTo, oneWeek).
move_result = TRY ( move_relative_date ( global_object , * calendar , * relative_to , * one_week ) ) ;
// 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 ;
}
// h. Let fractionalWeeks be weeks + days / abs(oneWeekDays).
auto fractional_weeks = weeks + days / fabs ( one_week_days ) ;
// i. Set weeks to ! RoundNumberToIncrement(fractionalWeeks, increment, roundingMode).
weeks = ( double ) round_number_to_increment ( fractional_weeks , increment , rounding_mode ) ;
// j. Set remainder to fractionalWeeks - weeks.
remainder = fractional_weeks - weeks ;
// k. Set days to 0.
days = 0 ;
}
// 12. Else if unit is "day", then
else if ( unit = = " day " sv ) {
// a. Let fractionalDays be days.
auto fractional_days = days ;
// b. Set days to ! RoundNumberToIncrement(days, increment, roundingMode).
days = ( double ) round_number_to_increment ( days , increment , rounding_mode ) ;
// c. Set remainder to fractionalDays - days.
remainder = fractional_days - days ;
}
// 13. Else if unit is "hour", then
else if ( unit = = " hour " sv ) {
// a. Let fractionalHours be (fractionalSeconds / 60 + minutes) / 60 + hours.
auto fractional_hours = ( fractional_seconds / 60 + minutes ) / 60 + hours ;
// b. Set hours to ! RoundNumberToIncrement(fractionalHours, increment, roundingMode).
hours = ( double ) round_number_to_increment ( fractional_hours , increment , rounding_mode ) ;
// 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 ;
}
// 14. Else if unit is "minute", then
else if ( unit = = " minute " sv ) {
// a. Let fractionalMinutes be fractionalSeconds / 60 + minutes.
auto fractional_minutes = fractional_seconds / 60 + minutes ;
// b. Set minutes to ! RoundNumberToIncrement(fractionalMinutes, increment, roundingMode).
minutes = ( double ) round_number_to_increment ( fractional_minutes , increment , rounding_mode ) ;
// 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 ;
}
// 15. Else if unit is "second", then
else if ( unit = = " second " sv ) {
// a. Set seconds to ! RoundNumberToIncrement(fractionalSeconds, increment, roundingMode).
seconds = ( double ) round_number_to_increment ( fractional_seconds , increment , rounding_mode ) ;
// b. Set remainder to fractionalSeconds - seconds.
remainder = fractional_seconds - seconds ;
// c. Set milliseconds, microseconds, and nanoseconds to 0.
milliseconds = 0 ;
microseconds = 0 ;
nanoseconds = 0 ;
}
// 16. Else if unit is "millisecond", then
else if ( unit = = " millisecond " sv ) {
// a. Let fractionalMilliseconds be nanoseconds × 10^− 6 + microseconds × 10^− 3 + milliseconds.
auto fractional_milliseconds = nanoseconds * 0.000001 + microseconds * 0.001 + milliseconds ;
// b. Set milliseconds to ! RoundNumberToIncrement(fractionalMilliseconds, increment, roundingMode).
milliseconds = ( double ) round_number_to_increment ( fractional_milliseconds , increment , rounding_mode ) ;
// c. Set remainder to fractionalMilliseconds - milliseconds.
remainder = fractional_milliseconds - milliseconds ;
// d. Set microseconds and nanoseconds to 0.
microseconds = 0 ;
nanoseconds = 0 ;
}
// 17. Else if unit is "microsecond", then
else if ( unit = = " microsecond " sv ) {
// a. Let fractionalMicroseconds be nanoseconds × 10^− 3 + microseconds.
auto fractional_microseconds = nanoseconds * 0.001 + microseconds ;
// b. Set microseconds to ! RoundNumberToIncrement(fractionalMicroseconds, increment, roundingMode).
microseconds = ( double ) round_number_to_increment ( fractional_microseconds , increment , rounding_mode ) ;
// c. Set remainder to fractionalMicroseconds - microseconds.
remainder = fractional_microseconds - microseconds ;
// d. Set nanoseconds to 0.
nanoseconds = 0 ;
}
// 18. Else,
else {
// a. Assert: unit is "nanosecond".
VERIFY ( unit = = " nanosecond " sv ) ;
// b. Set remainder to nanoseconds.
remainder = nanoseconds ;
// c. Set nanoseconds to ! RoundNumberToIncrement(nanoseconds, increment, roundingMode).
nanoseconds = ( double ) round_number_to_increment ( nanoseconds , increment , rounding_mode ) ;
// d. Set remainder to remainder − nanoseconds.
remainder - = nanoseconds ;
}
// Return the Record { [[Years]]: years, [[Months]]: months, [[Weeks]]: weeks, [[Days]]: days, [[Hours]]: hours, [[Minutes]]: minutes, [[Seconds]]: seconds, [[Milliseconds]]: milliseconds, [[Microseconds]]: microseconds, [[Nanoseconds]]: nanoseconds, [[Remainder]]: remainder }.
return RoundedDuration { . years = years , . months = months , . weeks = weeks , . days = days , . hours = hours , . minutes = minutes , . seconds = seconds , . milliseconds = milliseconds , . microseconds = microseconds , . nanoseconds = nanoseconds , . remainder = remainder } ;
}
2021-11-24 17:37:27 +00:00
// 7.5.20 ToLimitedTemporalDuration ( temporalDurationLike, disallowedFields ), https://tc39.es/proposal-temporal/#sec-temporal-tolimitedtemporalduration
2021-09-16 22:07:14 +01:00
ThrowCompletionOr < TemporalDuration > to_limited_temporal_duration ( GlobalObject & global_object , Value temporal_duration_like , Vector < StringView > const & disallowed_fields )
2021-08-07 00:31:08 +01:00
{
auto & vm = global_object . vm ( ) ;
2021-09-16 22:07:14 +01:00
TemporalDuration duration ;
2021-08-07 00:31:08 +01:00
// 1. If Type(temporalDurationLike) is not Object, then
if ( ! temporal_duration_like . is_object ( ) ) {
// a. Let str be ? ToString(temporalDurationLike).
2021-10-12 17:49:01 +01:00
auto str = TRY ( temporal_duration_like . to_string ( global_object ) ) ;
2021-08-07 00:31:08 +01:00
// b. Let duration be ? ParseTemporalDurationString(str).
2021-09-16 22:07:14 +01:00
duration = TRY ( parse_temporal_duration_string ( global_object , str ) ) ;
2021-08-07 00:31:08 +01:00
}
// 2. Else,
else {
// a. Let duration be ? ToTemporalDurationRecord(temporalDurationLike).
2021-09-16 22:07:14 +01:00
duration = TRY ( to_temporal_duration_record ( global_object , temporal_duration_like . as_object ( ) ) ) ;
2021-08-07 00:31:08 +01:00
}
// 3. If ! IsValidDuration(duration.[[Years]], duration.[[Months]], duration.[[Weeks]], duration.[[Days]], duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]], duration.[[Milliseconds]], duration.[[Microseconds]], duration.[[Nanoseconds]]) is false, throw a RangeError exception.
2021-09-16 22:07:14 +01:00
if ( ! is_valid_duration ( duration . years , duration . months , duration . weeks , duration . days , duration . hours , duration . minutes , duration . seconds , duration . milliseconds , duration . microseconds , duration . nanoseconds ) )
return vm . throw_completion < RangeError > ( global_object , ErrorType : : TemporalInvalidDuration ) ;
2021-08-07 00:31:08 +01:00
// 4. For each row of Table 7, except the header row, in table order, do
for ( auto & [ internal_slot , property ] : temporal_duration_like_properties < TemporalDuration , double > ( vm ) ) {
// a. Let prop be the Property value of the current row.
// b. Let value be duration's internal slot whose name is the Internal Slot value of the current row.
2021-09-16 22:07:14 +01:00
auto value = duration . * internal_slot ;
2021-08-07 00:31:08 +01:00
// If value is not 0 and disallowedFields contains prop, then
if ( value ! = 0 & & disallowed_fields . contains_slow ( property . as_string ( ) ) ) {
// i. Throw a RangeError exception.
2021-09-16 22:07:14 +01:00
return vm . throw_completion < RangeError > ( global_object , ErrorType : : TemporalInvalidDurationPropertyValueNonZero , property . as_string ( ) , value ) ;
2021-08-07 00:31:08 +01:00
}
}
// 5. Return duration.
return duration ;
}
2021-11-07 01:41:52 +00:00
// 7.5.21 TemporalDurationToString ( years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, precision ), https://tc39.es/proposal-temporal/#sec-temporal-temporaldurationtostring
String temporal_duration_to_string ( 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 )
{
// 1. Assert: precision is not "minute".
if ( precision . has < StringView > ( ) )
VERIFY ( precision . get < StringView > ( ) ! = " minute " sv ) ;
// 2. Set seconds to the mathematical value of seconds.
// 3. Set milliseconds to the mathematical value of milliseconds.
// 4. Set microseconds to the mathematical value of microseconds.
// 5. Set nanoseconds to the mathematical value of nanoseconds.
// 6. 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 ) ;
// 7. Set microseconds to microseconds + the integral part of nanoseconds / 1000.
microseconds + = trunc ( nanoseconds / 1000 ) ;
// 8. Set nanoseconds to remainder(nanoseconds, 1000).
nanoseconds = fmod ( nanoseconds , 1000 ) ;
// 9. Set milliseconds to milliseconds + the integral part of microseconds / 1000.
milliseconds + = trunc ( microseconds / 1000 ) ;
// 10. Set microseconds to remainder(microseconds, 1000).
microseconds = fmod ( microseconds , 1000 ) ;
// 11. Set seconds to seconds + the integral part of milliseconds / 1000.
seconds + = trunc ( milliseconds / 1000 ) ;
// 12. Set milliseconds to remainder(milliseconds, 1000).
milliseconds = fmod ( milliseconds , 1000 ) ;
// 13. Let datePart be "".
StringBuilder date_part ;
// 14. If years is not 0, then
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 ' ) ;
}
// 15. If months is not 0, then
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 ' ) ;
}
// 16. If weeks is not 0, then
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 ' ) ;
}
// 17. If days is not 0, then
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 ' ) ;
}
// 18. Let timePart be "".
StringBuilder time_part ;
// 19. If hours is not 0, then
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 ' ) ;
}
// 20. If minutes is not 0, then
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 ' ) ;
}
// 21. If any of seconds, milliseconds, microseconds, and nanoseconds are not 0; or years, months, weeks, days, hours, and minutes are all 0, then
if ( ( seconds ! = 0 | | milliseconds ! = 0 | | microseconds ! = 0 | | nanoseconds ! = 0 ) | | ( years = = 0 & & months = = 0 & & weeks = = 0 & & days = = 0 & & hours = = 0 & & minutes = = 0 ) ) {
// 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 ) ;
// b. Let decimalPart be fraction formatted as a nine-digit decimal number, padded to the left with zeroes if necessary.
// 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.
auto decimal_part = String : : formatted ( " {:09} " , ( u64 ) fraction ) ;
// 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).
// NOTE: trim() would keep the left-most 0.
while ( decimal_part . ends_with ( ' 0 ' ) )
decimal_part = decimal_part . substring ( 0 , decimal_part . length ( ) - 1 ) ;
}
// d. Else if precision = 0, then
else if ( precision . get < u8 > ( ) = = 0 ) {
// i. Set decimalPart to "".
decimal_part = String : : empty ( ) ;
}
// e. Else,
else {
// i. Set decimalPart to the substring of decimalPart from 0 to precision.
decimal_part = decimal_part . substring ( 0 , precision . get < u8 > ( ) ) ;
}
// 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 ' ) ;
}
// 22. Let signPart be the code unit 0x002D (HYPHEN-MINUS) if sign < 0, and otherwise the empty String.
auto sign_part = sign < 0 ? " - " sv : " " sv ;
// 23. Let result be the string concatenation of signPart, the code unit 0x0050 (LATIN CAPITAL LETTER P) and datePart.
StringBuilder result ;
result . append ( sign_part ) ;
result . append ( ' P ' ) ;
result . append ( date_part . string_view ( ) ) ;
// 24. If timePart is not "", then
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 ( ) ) ;
}
// 25. Return result.
return result . to_string ( ) ;
}
2021-07-15 23:20:43 +01:00
}