mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-06-14 22:31:56 +00:00
It was a bit of a semantic mistake too use this alias too eagerly. Namely, it should not be used to refer to epoch nanoseconds. We now only use the TimeDuration alias where the spec refers to a value as a time duration.
684 lines
30 KiB
C++
684 lines
30 KiB
C++
/*
|
||
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
|
||
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
|
||
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
|
||
*
|
||
* SPDX-License-Identifier: BSD-2-Clause
|
||
*/
|
||
|
||
#include <AK/Assertions.h>
|
||
#include <LibJS/Runtime/AbstractOperations.h>
|
||
#include <LibJS/Runtime/Temporal/Duration.h>
|
||
#include <LibJS/Runtime/Temporal/Instant.h>
|
||
#include <LibJS/Runtime/Temporal/PlainDateTime.h>
|
||
#include <LibJS/Runtime/Temporal/PlainTime.h>
|
||
#include <LibJS/Runtime/Temporal/PlainTimeConstructor.h>
|
||
#include <LibJS/Runtime/Temporal/TimeZone.h>
|
||
#include <LibJS/Runtime/Temporal/ZonedDateTime.h>
|
||
#include <math.h>
|
||
|
||
namespace JS::Temporal {
|
||
|
||
GC_DEFINE_ALLOCATOR(PlainTime);
|
||
|
||
// 4 Temporal.PlainTime Objects, https://tc39.es/proposal-temporal/#sec-temporal-plaintime-objects
|
||
PlainTime::PlainTime(Time const& time, Object& prototype)
|
||
: Object(ConstructWithPrototypeTag::Tag, prototype)
|
||
, m_time(time)
|
||
{
|
||
}
|
||
|
||
// 4.5.2 CreateTimeRecord ( hour, minute, second, millisecond, microsecond, nanosecond [ , deltaDays ] ), https://tc39.es/proposal-temporal/#sec-temporal-createtimerecord
|
||
Time create_time_record(double hour, double minute, double second, double millisecond, double microsecond, double nanosecond, double delta_days)
|
||
{
|
||
// 1. If deltaDays is not present, set deltaDays to 0.
|
||
// 2. Assert: IsValidTime(hour, minute, second, millisecond, microsecond, nanosecond).
|
||
VERIFY(is_valid_time(hour, minute, second, millisecond, microsecond, nanosecond));
|
||
|
||
// 3. Return Time Record { [[Days]]: deltaDays, [[Hour]]: hour, [[Minute]]: minute, [[Second]]: second, [[Millisecond]]: millisecond, [[Microsecond]]: microsecond, [[Nanosecond]]: nanosecond }.
|
||
return {
|
||
.days = delta_days,
|
||
.hour = static_cast<u8>(hour),
|
||
.minute = static_cast<u8>(minute),
|
||
.second = static_cast<u8>(second),
|
||
.millisecond = static_cast<u16>(millisecond),
|
||
.microsecond = static_cast<u16>(microsecond),
|
||
.nanosecond = static_cast<u16>(nanosecond),
|
||
};
|
||
}
|
||
|
||
// 4.5.3 MidnightTimeRecord ( ), https://tc39.es/proposal-temporal/#sec-temporal-midnighttimerecord
|
||
Time midnight_time_record()
|
||
{
|
||
// 1. Return Time Record { [[Days]]: 0, [[Hour]]: 0, [[Minute]]: 0, [[Second]]: 0, [[Millisecond]]: 0, [[Microsecond]]: 0, [[Nanosecond]]: 0 }.
|
||
return { .days = 0, .hour = 0, .minute = 0, .second = 0, .millisecond = 0, .microsecond = 0, .nanosecond = 0 };
|
||
}
|
||
|
||
// 4.5.4 NoonTimeRecord ( ), https://tc39.es/proposal-temporal/#sec-temporal-noontimerecord
|
||
Time noon_time_record()
|
||
{
|
||
// 1. Return Time Record { [[Days]]: 0, [[Hour]]: 12, [[Minute]]: 0, [[Second]]: 0, [[Millisecond]]: 0, [[Microsecond]]: 0, [[Nanosecond]]: 0 }.
|
||
return { .days = 0, .hour = 12, .minute = 0, .second = 0, .millisecond = 0, .microsecond = 0, .nanosecond = 0 };
|
||
}
|
||
|
||
// 4.5.5 DifferenceTime ( time1, time2 ), https://tc39.es/proposal-temporal/#sec-temporal-differencetime
|
||
TimeDuration difference_time(Time const& time1, Time const& time2)
|
||
{
|
||
// 1. Let hours be time2.[[Hour]] - time1.[[Hour]].
|
||
auto hours = static_cast<double>(time2.hour) - static_cast<double>(time1.hour);
|
||
|
||
// 2. Let minutes be time2.[[Minute]] - time1.[[Minute]].
|
||
auto minutes = static_cast<double>(time2.minute) - static_cast<double>(time1.minute);
|
||
|
||
// 3. Let seconds be time2.[[Second]] - time1.[[Second]].
|
||
auto seconds = static_cast<double>(time2.second) - static_cast<double>(time1.second);
|
||
|
||
// 4. Let milliseconds be time2.[[Millisecond]] - time1.[[Millisecond]].
|
||
auto milliseconds = static_cast<double>(time2.millisecond) - static_cast<double>(time1.millisecond);
|
||
|
||
// 5. Let microseconds be time2.[[Microsecond]] - time1.[[Microsecond]].
|
||
auto microseconds = static_cast<double>(time2.microsecond) - static_cast<double>(time1.microsecond);
|
||
|
||
// 6. Let nanoseconds be time2.[[Nanosecond]] - time1.[[Nanosecond]].
|
||
auto nanoseconds = static_cast<double>(time2.nanosecond) - static_cast<double>(time1.nanosecond);
|
||
|
||
// 7. Let timeDuration be TimeDurationFromComponents(hours, minutes, seconds, milliseconds, microseconds, nanoseconds).
|
||
auto time_duration = time_duration_from_components(hours, minutes, seconds, milliseconds, microseconds, nanoseconds);
|
||
|
||
// 8. Assert: abs(timeDuration) < nsPerDay.
|
||
VERIFY(time_duration.unsigned_value() < NANOSECONDS_PER_DAY);
|
||
|
||
// 9. Return timeDuration.
|
||
return time_duration;
|
||
}
|
||
|
||
// 4.5.6 ToTemporalTime ( item [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal-totemporaltime
|
||
ThrowCompletionOr<GC::Ref<PlainTime>> to_temporal_time(VM& vm, Value item, Value options)
|
||
{
|
||
// 1. If options is not present, set options to undefined.
|
||
|
||
Time time;
|
||
|
||
// 2. If item is an Object, then
|
||
if (item.is_object()) {
|
||
auto const& object = item.as_object();
|
||
|
||
// a. If item has an [[InitializedTemporalTime]] internal slot, then
|
||
if (is<PlainTime>(object)) {
|
||
auto const& plain_time = static_cast<PlainTime const&>(object);
|
||
|
||
// i. Let resolvedOptions be ? GetOptionsObject(options).
|
||
auto resolved_options = TRY(get_options_object(vm, options));
|
||
|
||
// ii. Perform ? GetTemporalOverflowOption(resolvedOptions).
|
||
TRY(get_temporal_overflow_option(vm, resolved_options));
|
||
|
||
// iii. Return ! CreateTemporalTime(item.[[Time]]).
|
||
return MUST(create_temporal_time(vm, plain_time.time()));
|
||
}
|
||
|
||
// b. If item has an [[InitializedTemporalDateTime]] internal slot, then
|
||
if (is<PlainDateTime>(object)) {
|
||
auto const& plain_date_time = static_cast<PlainDateTime const&>(object);
|
||
|
||
// i. Let resolvedOptions be ? GetOptionsObject(options).
|
||
auto resolved_options = TRY(get_options_object(vm, options));
|
||
|
||
// ii. Perform ? GetTemporalOverflowOption(resolvedOptions).
|
||
TRY(get_temporal_overflow_option(vm, resolved_options));
|
||
|
||
// iii. Return ! CreateTemporalTime(item.[[ISODateTime]].[[Time]]).
|
||
return MUST(create_temporal_time(vm, plain_date_time.iso_date_time().time));
|
||
}
|
||
|
||
// c. If item has an [[InitializedTemporalZonedDateTime]] internal slot, then
|
||
if (is<ZonedDateTime>(object)) {
|
||
auto const& zoned_date_time = static_cast<ZonedDateTime const&>(object);
|
||
|
||
// i. Let isoDateTime be GetISODateTimeFor(item.[[TimeZone]], item.[[EpochNanoseconds]]).
|
||
auto iso_date_time = get_iso_date_time_for(zoned_date_time.time_zone(), zoned_date_time.epoch_nanoseconds()->big_integer());
|
||
|
||
// ii. Let resolvedOptions be ? GetOptionsObject(options).
|
||
auto resolved_options = TRY(get_options_object(vm, options));
|
||
|
||
// iii. Perform ? GetTemporalOverflowOption(resolvedOptions).
|
||
TRY(get_temporal_overflow_option(vm, resolved_options));
|
||
|
||
// iv. Return ! CreateTemporalTime(isoDateTime.[[Time]]).
|
||
return MUST(create_temporal_time(vm, iso_date_time.time));
|
||
}
|
||
|
||
// d. Let result be ? ToTemporalTimeRecord(item).
|
||
auto result = TRY(to_temporal_time_record(vm, object));
|
||
|
||
// e. Let resolvedOptions be ? GetOptionsObject(options).
|
||
auto resolved_options = TRY(get_options_object(vm, options));
|
||
|
||
// f. Let overflow be ? GetTemporalOverflowOption(resolvedOptions).
|
||
auto overflow = TRY(get_temporal_overflow_option(vm, resolved_options));
|
||
|
||
// g. Set result to ? RegulateTime(result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]], overflow).
|
||
time = TRY(regulate_time(vm, *result.hour, *result.minute, *result.second, *result.millisecond, *result.microsecond, *result.nanosecond, overflow));
|
||
}
|
||
// 3. Else,
|
||
else {
|
||
// a. If item is not a String, throw a TypeError exception.
|
||
if (!item.is_string())
|
||
return vm.throw_completion<TypeError>(ErrorType::TemporalInvalidPlainTime);
|
||
|
||
// b. Let parseResult be ? ParseISODateTime(item, « TemporalTimeString »).
|
||
auto parse_result = TRY(parse_iso_date_time(vm, item.as_string().utf8_string_view(), { { Production::TemporalTimeString } }));
|
||
|
||
// c. Assert: parseResult.[[Time]] is not START-OF-DAY.
|
||
VERIFY(!parse_result.time.has<ParsedISODateTime::StartOfDay>());
|
||
|
||
// d. Set result to parseResult.[[Time]].
|
||
time = parse_result.time.get<Time>();
|
||
|
||
// e. NOTE: A successful parse using TemporalTimeString guarantees absence of ambiguity with respect to any
|
||
// ISO 8601 date-only, year-month, or month-day representation.
|
||
|
||
// f. Let resolvedOptions be ? GetOptionsObject(options).
|
||
auto resolved_options = TRY(get_options_object(vm, options));
|
||
|
||
// g. Perform ? GetTemporalOverflowOption(resolvedOptions).
|
||
TRY(get_temporal_overflow_option(vm, resolved_options));
|
||
}
|
||
|
||
// 4. Return ! CreateTemporalTime(result).
|
||
return MUST(create_temporal_time(vm, time));
|
||
}
|
||
|
||
// 4.5.7 ToTimeRecordOrMidnight ( item ), https://tc39.es/proposal-temporal/#sec-temporal-totimerecordormidnight
|
||
ThrowCompletionOr<Time> to_time_record_or_midnight(VM& vm, Value item)
|
||
{
|
||
// 1. If item is undefined, return MidnightTimeRecord().
|
||
if (item.is_undefined())
|
||
return midnight_time_record();
|
||
|
||
// 2. Let plainTime be ? ToTemporalTime(item).
|
||
auto plain_time = TRY(to_temporal_time(vm, item));
|
||
|
||
// 3. Return plainTime.[[Time]].
|
||
return plain_time->time();
|
||
}
|
||
|
||
// 4.5.8 RegulateTime ( hour, minute, second, millisecond, microsecond, nanosecond, overflow ), https://tc39.es/proposal-temporal/#sec-temporal-regulatetime
|
||
ThrowCompletionOr<Time> regulate_time(VM& vm, double hour, double minute, double second, double millisecond, double microsecond, double nanosecond, Overflow overflow)
|
||
{
|
||
switch (overflow) {
|
||
// 1. If overflow is CONSTRAIN, then
|
||
case Overflow::Constrain:
|
||
// a. Set hour to the result of clamping hour between 0 and 23.
|
||
hour = clamp(hour, 0, 23);
|
||
|
||
// b. Set minute to the result of clamping minute between 0 and 59.
|
||
minute = clamp(minute, 0, 59);
|
||
|
||
// c. Set second to the result of clamping second between 0 and 59.
|
||
second = clamp(second, 0, 59);
|
||
|
||
// d. Set millisecond to the result of clamping millisecond between 0 and 999.
|
||
millisecond = clamp(millisecond, 0, 999);
|
||
|
||
// e. Set microsecond to the result of clamping microsecond between 0 and 999.
|
||
microsecond = clamp(microsecond, 0, 999);
|
||
|
||
// f. Set nanosecond to the result of clamping nanosecond between 0 and 999.
|
||
nanosecond = clamp(nanosecond, 0, 999);
|
||
|
||
break;
|
||
|
||
// 2. Else,
|
||
case Overflow::Reject:
|
||
// a. Assert: overflow is REJECT.
|
||
// b. If IsValidTime(hour, minute, second, millisecond, microsecond, nanosecond) is false, throw a RangeError exception.
|
||
if (!is_valid_time(hour, minute, second, millisecond, microsecond, nanosecond))
|
||
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidPlainTime);
|
||
|
||
break;
|
||
}
|
||
|
||
// 3. Return CreateTimeRecord(hour, minute, second, millisecond, microsecond,nanosecond).
|
||
return create_time_record(hour, minute, second, millisecond, microsecond, nanosecond);
|
||
}
|
||
|
||
// 4.5.9 IsValidTime ( hour, minute, second, millisecond, microsecond, nanosecond ), https://tc39.es/proposal-temporal/#sec-temporal-isvalidtime
|
||
bool is_valid_time(double hour, double minute, double second, double millisecond, double microsecond, double nanosecond)
|
||
{
|
||
// 1. If hour < 0 or hour > 23, then
|
||
if (hour < 0 || hour > 23) {
|
||
// a. Return false.
|
||
return false;
|
||
}
|
||
|
||
// 2. If minute < 0 or minute > 59, then
|
||
if (minute < 0 || minute > 59) {
|
||
// a. Return false.
|
||
return false;
|
||
}
|
||
|
||
// 3. If second < 0 or second > 59, then
|
||
if (second < 0 || second > 59) {
|
||
// a. Return false.
|
||
return false;
|
||
}
|
||
|
||
// 4. If millisecond < 0 or millisecond > 999, then
|
||
if (millisecond < 0 || millisecond > 999) {
|
||
// a. Return false.
|
||
return false;
|
||
}
|
||
|
||
// 5. If microsecond < 0 or microsecond > 999, then
|
||
if (microsecond < 0 || microsecond > 999) {
|
||
// a. Return false.
|
||
return false;
|
||
}
|
||
|
||
// 6. If nanosecond < 0 or nanosecond > 999, then
|
||
if (nanosecond < 0 || nanosecond > 999) {
|
||
// a. Return false.
|
||
return false;
|
||
}
|
||
|
||
// 7. Return true.
|
||
return true;
|
||
}
|
||
|
||
// 4.5.10 BalanceTime ( hour, minute, second, millisecond, microsecond, nanosecond ), https://tc39.es/proposal-temporal/#sec-temporal-balancetime
|
||
Time balance_time(double hour, double minute, double second, double millisecond, double microsecond, double nanosecond)
|
||
{
|
||
// 1. Set microsecond to microsecond + floor(nanosecond / 1000).
|
||
microsecond += floor(nanosecond / 1000.0);
|
||
|
||
// 2. Set nanosecond to nanosecond modulo 1000.
|
||
nanosecond = modulo(nanosecond, 1000.0);
|
||
|
||
// 3. Set millisecond to millisecond + floor(microsecond / 1000).
|
||
millisecond += floor(microsecond / 1000.0);
|
||
|
||
// 4. Set microsecond to microsecond modulo 1000.
|
||
microsecond = modulo(microsecond, 1000.0);
|
||
|
||
// 5. Set second to second + floor(millisecond / 1000).
|
||
second += floor(millisecond / 1000.0);
|
||
|
||
// 6. Set millisecond to millisecond modulo 1000.
|
||
millisecond = modulo(millisecond, 1000.0);
|
||
|
||
// 7. Set minute to minute + floor(second / 60).
|
||
minute += floor(second / 60.0);
|
||
|
||
// 8. Set second to second modulo 60.
|
||
second = modulo(second, 60.0);
|
||
|
||
// 9. Set hour to hour + floor(minute / 60).
|
||
hour += floor(minute / 60.0);
|
||
|
||
// 10. Set minute to minute modulo 60.
|
||
minute = modulo(minute, 60.0);
|
||
|
||
// 11. Let deltaDays be floor(hour / 24).
|
||
auto delta_days = floor(hour / 24.0);
|
||
|
||
// 12. Set hour to hour modulo 24.
|
||
hour = modulo(hour, 24.0);
|
||
|
||
// 13. Return CreateTimeRecord(hour, minute, second, millisecond, microsecond, nanosecond, deltaDays).
|
||
return create_time_record(hour, minute, second, millisecond, microsecond, nanosecond, delta_days);
|
||
}
|
||
|
||
// 4.5.10 BalanceTime ( hour, minute, second, millisecond, microsecond, nanosecond ), https://tc39.es/proposal-temporal/#sec-temporal-balancetime
|
||
Time balance_time(double hour, double minute, double second, double millisecond, double microsecond, Crypto::SignedBigInteger const& nanosecond_value)
|
||
{
|
||
// 1. Set microsecond to microsecond + floor(nanosecond / 1000).
|
||
auto microsecond_value = Crypto::SignedBigInteger { microsecond }.plus(big_floor(nanosecond_value, NANOSECONDS_PER_MICROSECOND));
|
||
|
||
// 2. Set nanosecond to nanosecond modulo 1000.
|
||
auto nanosecond = modulo(nanosecond_value, NANOSECONDS_PER_MICROSECOND).to_double();
|
||
|
||
// 3. Set millisecond to millisecond + floor(microsecond / 1000).
|
||
auto millisecond_value = Crypto::SignedBigInteger { millisecond }.plus(big_floor(microsecond_value, MICROSECONDS_PER_MILLISECOND));
|
||
|
||
// 4. Set microsecond to microsecond modulo 1000.
|
||
microsecond = modulo(microsecond_value, MICROSECONDS_PER_MILLISECOND).to_double();
|
||
|
||
// 5. Set second to second + floor(millisecond / 1000).
|
||
auto second_value = Crypto::SignedBigInteger { second }.plus(big_floor(millisecond_value, MILLISECONDS_PER_SECOND));
|
||
|
||
// 6. Set millisecond to millisecond modulo 1000.
|
||
millisecond = modulo(millisecond_value, MILLISECONDS_PER_SECOND).to_double();
|
||
|
||
// 7. Set minute to minute + floor(second / 60).
|
||
auto minute_value = Crypto::SignedBigInteger { minute }.plus(big_floor(second_value, SECONDS_PER_MINUTE));
|
||
|
||
// 8. Set second to second modulo 60.
|
||
second = modulo(second_value, SECONDS_PER_MINUTE).to_double();
|
||
|
||
// 9. Set hour to hour + floor(minute / 60).
|
||
auto hour_value = Crypto::SignedBigInteger { hour }.plus(big_floor(minute_value, MINUTES_PER_HOUR));
|
||
|
||
// 10. Set minute to minute modulo 60.
|
||
minute = modulo(minute_value, MINUTES_PER_HOUR).to_double();
|
||
|
||
// 11. Let deltaDays be floor(hour / 24).
|
||
auto delta_days = big_floor(hour_value, HOURS_PER_DAY).to_double();
|
||
|
||
// 12. Set hour to hour modulo 24.
|
||
hour = modulo(hour_value, HOURS_PER_DAY).to_double();
|
||
|
||
// 13. Return CreateTimeRecord(hour, minute, second, millisecond, microsecond, nanosecond, deltaDays).
|
||
return create_time_record(hour, minute, second, millisecond, microsecond, nanosecond, delta_days);
|
||
}
|
||
|
||
// 4.5.11 CreateTemporalTime ( time [ , newTarget ] ), https://tc39.es/proposal-temporal/#sec-temporal-createtemporaltime
|
||
ThrowCompletionOr<GC::Ref<PlainTime>> create_temporal_time(VM& vm, Time const& time, GC::Ptr<FunctionObject> new_target)
|
||
{
|
||
auto& realm = *vm.current_realm();
|
||
|
||
// 1. If newTarget is not present, set newTarget to %Temporal.PlainTime%.
|
||
if (!new_target)
|
||
new_target = realm.intrinsics().temporal_plain_time_constructor();
|
||
|
||
// 2. Let object be ? OrdinaryCreateFromConstructor(newTarget, "%Temporal.PlainTime.prototype%", « [[InitializedTemporalTime]], [[Time]] »).
|
||
// 3. Set object.[[Time]] to time.
|
||
auto object = TRY(ordinary_create_from_constructor<PlainTime>(vm, *new_target, &Intrinsics::temporal_plain_time_prototype, time));
|
||
|
||
// 4. Return object.
|
||
return object;
|
||
}
|
||
|
||
// 4.5.12 ToTemporalTimeRecord ( temporalTimeLike [ , completeness ] ), https://tc39.es/proposal-temporal/#sec-temporal-totemporaltimerecord
|
||
ThrowCompletionOr<TemporalTimeLike> to_temporal_time_record(VM& vm, Object const& temporal_time_like, Completeness completeness)
|
||
{
|
||
// 1. If completeness is not present, set completeness to COMPLETE.
|
||
|
||
TemporalTimeLike result;
|
||
|
||
// 2. If completeness is COMPLETE, then
|
||
if (completeness == Completeness::Complete) {
|
||
// a. Let result be a new TemporalTimeLike Record with each field set to 0.
|
||
result = TemporalTimeLike::zero();
|
||
}
|
||
// 3. Else,
|
||
else {
|
||
// a. Let result be a new TemporalTimeLike Record with each field set to UNSET.
|
||
}
|
||
|
||
// 4. Let any be false.
|
||
auto any = false;
|
||
|
||
auto apply_field = [&](auto const& key, auto& result_field) -> ThrowCompletionOr<void> {
|
||
auto field = TRY(temporal_time_like.get(key));
|
||
if (field.is_undefined())
|
||
return {};
|
||
|
||
result_field = TRY(to_integer_with_truncation(vm, field, ErrorType::TemporalInvalidTimeLikeField, field, key));
|
||
any = true;
|
||
|
||
return {};
|
||
};
|
||
|
||
// 5. Let hour be ? Get(temporalTimeLike, "hour").
|
||
// 6. If hour is not undefined, then
|
||
// a. Set result.[[Hour]] to ? ToIntegerWithTruncation(hour).
|
||
// b. Set any to true.
|
||
TRY(apply_field(vm.names.hour, result.hour));
|
||
|
||
// 7. Let microsecond be ? Get(temporalTimeLike, "microsecond").
|
||
// 8. If microsecond is not undefined, then
|
||
// a. Set result.[[Microsecond]] to ? ToIntegerWithTruncation(microsecond).
|
||
// b. Set any to true.
|
||
TRY(apply_field(vm.names.microsecond, result.microsecond));
|
||
|
||
// 9. Let millisecond be ? Get(temporalTimeLike, "millisecond").
|
||
// 10. If millisecond is not undefined, then
|
||
// a. Set result.[[Millisecond]] to ? ToIntegerWithTruncation(millisecond).
|
||
// b. Set any to true.
|
||
TRY(apply_field(vm.names.millisecond, result.millisecond));
|
||
|
||
// 11. Let minute be ? Get(temporalTimeLike, "minute").
|
||
// 12. If minute is not undefined, then
|
||
// a. Set result.[[Minute]] to ? ToIntegerWithTruncation(minute).
|
||
// b. Set any to true.
|
||
TRY(apply_field(vm.names.minute, result.minute));
|
||
|
||
// 13. Let nanosecond be ? Get(temporalTimeLike, "nanosecond").
|
||
// 14. If nanosecond is not undefined, then
|
||
// a. Set result.[[Nanosecond]] to ? ToIntegerWithTruncation(nanosecond).
|
||
// b. Set any to true.
|
||
TRY(apply_field(vm.names.nanosecond, result.nanosecond));
|
||
|
||
// 15. Let second be ? Get(temporalTimeLike, "second").
|
||
// 16. If second is not undefined, then
|
||
// a. Set result.[[Second]] to ? ToIntegerWithTruncation(second).
|
||
// b. Set any to true.
|
||
TRY(apply_field(vm.names.second, result.second));
|
||
|
||
// 17. If any is false, throw a TypeError exception.
|
||
if (!any)
|
||
return vm.throw_completion<TypeError>(ErrorType::TemporalInvalidTime);
|
||
|
||
// 18. Return result.
|
||
return result;
|
||
}
|
||
|
||
// 4.5.13 TimeRecordToString ( time, precision ), https://tc39.es/proposal-temporal/#sec-temporal-timerecordtostring
|
||
String time_record_to_string(Time const& time, SecondsStringPrecision::Precision precision)
|
||
{
|
||
// 1. Let subSecondNanoseconds be time.[[Millisecond]] × 10**6 + time.[[Microsecond]] × 10**3 + time.[[Nanosecond]].
|
||
auto sub_second_nanoseconds = (static_cast<u64>(time.millisecond) * 1'000'000) + (static_cast<u64>(time.microsecond) * 1000) + static_cast<u64>(time.nanosecond);
|
||
|
||
// 2. Return FormatTimeString(time.[[Hour]], time.[[Minute]], time.[[Second]], subSecondNanoseconds, precision).
|
||
return format_time_string(time.hour, time.minute, time.second, sub_second_nanoseconds, precision);
|
||
}
|
||
|
||
// 4.5.14 CompareTimeRecord ( time1, time2 ), https://tc39.es/proposal-temporal/#sec-temporal-comparetimerecord
|
||
i8 compare_time_record(Time const& time1, Time const& time2)
|
||
{
|
||
// 1. If time1.[[Hour]] > time2.[[Hour]], return 1.
|
||
if (time1.hour > time2.hour)
|
||
return 1;
|
||
// 2. If time1.[[Hour]] < time2.[[Hour]], return -1.
|
||
if (time1.hour < time2.hour)
|
||
return -1;
|
||
|
||
// 3. If time1.[[Minute]] > time2.[[Minute]], return 1.
|
||
if (time1.minute > time2.minute)
|
||
return 1;
|
||
// 4. If time1.[[Minute]] < time2.[[Minute]], return -1.
|
||
if (time1.minute < time2.minute)
|
||
return -1;
|
||
|
||
// 5. If time1.[[Second]] > time2.[[Second]], return 1.
|
||
if (time1.second > time2.second)
|
||
return 1;
|
||
// 6. If time1.[[Second]] < time2.[[Second]], return -1.
|
||
if (time1.second < time2.second)
|
||
return -1;
|
||
|
||
// 7. If time1.[[Millisecond]] > time2.[[Millisecond]], return 1.
|
||
if (time1.millisecond > time2.millisecond)
|
||
return 1;
|
||
// 8. If time1.[[Millisecond]] < time2.[[Millisecond]], return -1.
|
||
if (time1.millisecond < time2.millisecond)
|
||
return -1;
|
||
|
||
// 9. If time1.[[Microsecond]] > time2.[[Microsecond]], return 1.
|
||
if (time1.microsecond > time2.microsecond)
|
||
return 1;
|
||
// 10. If time1.[[Microsecond]] < time2.[[Microsecond]], return -1.
|
||
if (time1.microsecond < time2.microsecond)
|
||
return -1;
|
||
|
||
// 11. If time1.[[Nanosecond]] > time2.[[Nanosecond]], return 1.
|
||
if (time1.nanosecond > time2.nanosecond)
|
||
return 1;
|
||
// 12. If time1.[[Nanosecond]] < time2.[[Nanosecond]], return -1.
|
||
if (time1.nanosecond < time2.nanosecond)
|
||
return -1;
|
||
|
||
// 13. Return 0.
|
||
return 0;
|
||
}
|
||
|
||
// 4.5.15 AddTime ( time, timeDuration ), https://tc39.es/proposal-temporal/#sec-temporal-addtime
|
||
Time add_time(Time const& time, TimeDuration const& time_duration)
|
||
{
|
||
auto nanoseconds = time_duration.plus(TimeDuration { static_cast<i64>(time.nanosecond) });
|
||
|
||
// 1. Return BalanceTime(time.[[Hour]], time.[[Minute]], time.[[Second]], time.[[Millisecond]], time.[[Microsecond]], time.[[Nanosecond]] + timeDuration).
|
||
return balance_time(time.hour, time.minute, time.second, time.millisecond, time.microsecond, nanoseconds);
|
||
}
|
||
|
||
// 4.5.16 RoundTime ( time, increment, unit, roundingMode ), https://tc39.es/proposal-temporal/#sec-temporal-roundtime
|
||
Time round_time(Time const& time, u64 increment, Unit unit, RoundingMode rounding_mode)
|
||
{
|
||
double quantity = 0;
|
||
|
||
switch (unit) {
|
||
// 1. If unit is DAY or HOUR, then
|
||
case Unit::Day:
|
||
case Unit::Hour:
|
||
// a. Let quantity be ((((time.[[Hour]] × 60 + time.[[Minute]]) × 60 + time.[[Second]]) × 1000 + time.[[Millisecond]]) × 1000 + time.[[Microsecond]]) × 1000 + time.[[Nanosecond]].
|
||
quantity = ((((time.hour * 60.0 + time.minute) * 60.0 + time.second) * 1000.0 + time.millisecond) * 1000.0 + time.microsecond) * 1000.0 + time.nanosecond;
|
||
break;
|
||
|
||
// 2. Else if unit is MINUTE, then
|
||
case Unit::Minute:
|
||
// a. Let quantity be (((time.[[Minute]] × 60 + time.[[Second]]) × 1000 + time.[[Millisecond]]) × 1000 + time.[[Microsecond]]) × 1000 + time.[[Nanosecond]].
|
||
quantity = (((time.minute * 60.0 + time.second) * 1000.0 + time.millisecond) * 1000.0 + time.microsecond) * 1000.0 + time.nanosecond;
|
||
break;
|
||
|
||
// 3. Else if unit is SECOND, then
|
||
case Unit::Second:
|
||
// a. Let quantity be ((time.[[Second]] × 1000 + time.[[Millisecond]]) × 1000 + time.[[Microsecond]]) × 1000 + time.[[Nanosecond]].
|
||
quantity = ((time.second * 1000.0 + time.millisecond) * 1000.0 + time.microsecond) * 1000.0 + time.nanosecond;
|
||
break;
|
||
|
||
// 4. Else if unit is MILLISECOND, then
|
||
case Unit::Millisecond:
|
||
// a. Let quantity be (time.[[Millisecond]] × 1000 + time.[[Microsecond]]) × 1000 + time.[[Nanosecond]].
|
||
quantity = (time.millisecond * 1000.0 + time.microsecond) * 1000.0 + time.nanosecond;
|
||
break;
|
||
|
||
// 5. Else if unit is MICROSECOND, then
|
||
case Unit::Microsecond:
|
||
// a. Let quantity be time.[[Microsecond]] × 1000 + time.[[Nanosecond]].
|
||
quantity = time.microsecond * 1000.0 + time.nanosecond;
|
||
break;
|
||
|
||
// 6. Else,
|
||
case Unit::Nanosecond:
|
||
// a. Assert: unit is NANOSECOND.
|
||
// b. Let quantity be time.[[Nanosecond]].
|
||
quantity = time.nanosecond;
|
||
break;
|
||
|
||
default:
|
||
VERIFY_NOT_REACHED();
|
||
}
|
||
|
||
// 7. Let unitLength be the value in the "Length in Nanoseconds" column of the row of Table 21 whose "Value" column contains unit.
|
||
auto unit_length = temporal_unit_length_in_nanoseconds(unit).to_u64();
|
||
|
||
// 8. Let result be RoundNumberToIncrement(quantity, increment × unitLength, roundingMode) / unitLength.
|
||
auto result = round_number_to_increment(quantity, increment * unit_length, rounding_mode) / static_cast<double>(unit_length);
|
||
|
||
switch (unit) {
|
||
// 9. If unit is DAY, then
|
||
case Unit::Day:
|
||
// a. Return CreateTimeRecord(0, 0, 0, 0, 0, 0, result).
|
||
return create_time_record(0, 0, 0, 0, 0, 0, result);
|
||
|
||
// 10. If unit is HOUR, then
|
||
case Unit::Hour:
|
||
// a. Return BalanceTime(result, 0, 0, 0, 0, 0).
|
||
return balance_time(result, 0, 0, 0, 0, 0);
|
||
|
||
// 11. If unit is MINUTE, then
|
||
case Unit::Minute:
|
||
// a. Return BalanceTime(time.[[Hour]], result, 0, 0, 0, 0).
|
||
return balance_time(time.hour, result, 0, 0, 0, 0);
|
||
|
||
// 12. If unit is SECOND, then
|
||
case Unit::Second:
|
||
// a. Return BalanceTime(time.[[Hour]], time.[[Minute]], result, 0, 0, 0).
|
||
return balance_time(time.hour, time.minute, result, 0, 0, 0);
|
||
|
||
// 13. If unit is MILLISECOND, then
|
||
case Unit::Millisecond:
|
||
// a. Return BalanceTime(time.[[Hour]], time.[[Minute]], time.[[Second]], result, 0, 0).
|
||
return balance_time(time.hour, time.minute, time.second, result, 0, 0);
|
||
|
||
// 14. If unit is MICROSECOND, then
|
||
case Unit::Microsecond:
|
||
// a. Return BalanceTime(time.[[Hour]], time.[[Minute]], time.[[Second]], time.[[Millisecond]], result, 0).
|
||
return balance_time(time.hour, time.minute, time.second, time.millisecond, result, 0);
|
||
|
||
// 15. Assert: unit is NANOSECOND.
|
||
case Unit::Nanosecond:
|
||
// 16. Return BalanceTime(time.[[Hour]], time.[[Minute]], time.[[Second]], time.[[Millisecond]], time.[[Microsecond]], result).
|
||
return balance_time(time.hour, time.minute, time.second, time.millisecond, time.microsecond, result);
|
||
|
||
default:
|
||
break;
|
||
}
|
||
|
||
VERIFY_NOT_REACHED();
|
||
}
|
||
|
||
// 4.5.17 DifferenceTemporalPlainTime ( operation, temporalTime, other, options ), https://tc39.es/proposal-temporal/#sec-temporal-differencetemporalplaintime
|
||
ThrowCompletionOr<GC::Ref<Duration>> difference_temporal_plain_time(VM& vm, DurationOperation operation, PlainTime const& temporal_time, Value other_value, Value options)
|
||
{
|
||
// 1. Set other to ? ToTemporalTime(other).
|
||
auto other = TRY(to_temporal_time(vm, other_value));
|
||
|
||
// 2. Let resolvedOptions be ? GetOptionsObject(options).
|
||
auto resolved_options = TRY(get_options_object(vm, options));
|
||
|
||
// 3. Let settings be ? GetDifferenceSettings(operation, resolvedOptions, TIME, « », NANOSECOND, HOUR).
|
||
auto settings = TRY(get_difference_settings(vm, operation, resolved_options, UnitGroup::Time, {}, Unit::Nanosecond, Unit::Hour));
|
||
|
||
// 4. Let timeDuration be DifferenceTime(temporalTime.[[Time]], other.[[Time]]).
|
||
auto time_duration = difference_time(temporal_time.time(), other->time());
|
||
|
||
// 5. Set timeDuration to ! RoundTimeDuration(timeDuration, settings.[[RoundingIncrement]], settings.[[SmallestUnit]], settings.[[RoundingMode]]).
|
||
time_duration = MUST(round_time_duration(vm, time_duration, Crypto::UnsignedBigInteger { settings.rounding_increment }, settings.smallest_unit, settings.rounding_mode));
|
||
|
||
// 6. Let duration be ! CombineDateAndTimeDuration(ZeroDateDuration(), timeDuration).
|
||
auto duration = MUST(combine_date_and_time_duration(vm, zero_date_duration(vm), move(time_duration)));
|
||
|
||
// 7. Let result be ! TemporalDurationFromInternal(duration, settings.[[LargestUnit]]).
|
||
auto result = MUST(temporal_duration_from_internal(vm, duration, settings.largest_unit));
|
||
|
||
// 8. If operation is SINCE, set result to CreateNegatedTemporalDuration(result).
|
||
if (operation == DurationOperation::Since)
|
||
result = create_negated_temporal_duration(vm, result);
|
||
|
||
// 9. Return result.
|
||
return result;
|
||
}
|
||
|
||
// 4.5.18 AddDurationToTime ( operation, temporalTime, temporalDurationLike ), https://tc39.es/proposal-temporal/#sec-temporal-adddurationtotime
|
||
ThrowCompletionOr<GC::Ref<PlainTime>> add_duration_to_time(VM& vm, ArithmeticOperation operation, PlainTime const& temporal_time, Value temporal_duration_like)
|
||
{
|
||
// 1. Let duration be ? ToTemporalDuration(temporalDurationLike).
|
||
auto duration = TRY(to_temporal_duration(vm, temporal_duration_like));
|
||
|
||
// 2. If operation is SUBTRACT, set duration to CreateNegatedTemporalDuration(duration).
|
||
if (operation == ArithmeticOperation::Subtract)
|
||
duration = create_negated_temporal_duration(vm, duration);
|
||
|
||
// 3. Let internalDuration be ToInternalDurationRecord(duration).
|
||
auto internal_duration = to_internal_duration_record(vm, duration);
|
||
|
||
// 4. Let result be AddTime(temporalTime.[[Time]], internalDuration.[[Time]]).
|
||
auto result = add_time(temporal_time.time(), internal_duration.time);
|
||
|
||
// 5. Return ! CreateTemporalTime(result).
|
||
return MUST(create_temporal_time(vm, result));
|
||
}
|
||
|
||
}
|