/* * Copyright (c) 2021-2023, Linus Groh * Copyright (c) 2021, Idan Horowitz * Copyright (c) 2023, Shannon Booth * Copyright (c) 2024, Tim Flynn * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include #include #include #include #include #include namespace JS::Temporal { GC_DEFINE_ALLOCATOR(Instant); // 8 Temporal.Instant Objects, https://tc39.es/proposal-temporal/#sec-temporal-instant-objects Instant::Instant(BigInt const& epoch_nanoseconds, Object& prototype) : Object(ConstructWithPrototypeTag::Tag, prototype) , m_epoch_nanoseconds(epoch_nanoseconds) { } void Instant::visit_edges(Cell::Visitor& visitor) { Base::visit_edges(visitor); visitor.visit(m_epoch_nanoseconds); } // nsMaxInstant = 10**8 × nsPerDay = 8.64 × 10**21 Crypto::SignedBigInteger const NANOSECONDS_MAX_INSTANT = "8640000000000000000000"_sbigint; // nsMinInstant = -nsMaxInstant = -8.64 × 10**21 Crypto::SignedBigInteger const NANOSECONDS_MIN_INSTANT = "-8640000000000000000000"_sbigint; // nsPerDay = 10**6 × ℝ(msPerDay) = 8.64 × 10**13 Crypto::UnsignedBigInteger const NANOSECONDS_PER_DAY = 86400000000000_bigint; // Non-standard: Crypto::UnsignedBigInteger const NANOSECONDS_PER_HOUR = 3600000000000_bigint; Crypto::UnsignedBigInteger const NANOSECONDS_PER_MINUTE = 60000000000_bigint; Crypto::UnsignedBigInteger const NANOSECONDS_PER_SECOND = 1000000000_bigint; Crypto::UnsignedBigInteger const NANOSECONDS_PER_MILLISECOND = 1000000_bigint; Crypto::UnsignedBigInteger const NANOSECONDS_PER_MICROSECOND = 1000_bigint; Crypto::UnsignedBigInteger const NANOSECONDS_PER_NANOSECOND = 1_bigint; Crypto::UnsignedBigInteger const MICROSECONDS_PER_MILLISECOND = 1000_bigint; Crypto::UnsignedBigInteger const MILLISECONDS_PER_SECOND = 1000_bigint; Crypto::UnsignedBigInteger const SECONDS_PER_MINUTE = 60_bigint; Crypto::UnsignedBigInteger const MINUTES_PER_HOUR = 60_bigint; Crypto::UnsignedBigInteger const HOURS_PER_DAY = 24_bigint; // 8.5.1 IsValidEpochNanoseconds ( epochNanoseconds ), https://tc39.es/proposal-temporal/#sec-temporal-isvalidepochnanoseconds bool is_valid_epoch_nanoseconds(Crypto::SignedBigInteger const& epoch_nanoseconds) { // 1. If ℝ(epochNanoseconds) < nsMinInstant or ℝ(epochNanoseconds) > nsMaxInstant, then if (epoch_nanoseconds < NANOSECONDS_MIN_INSTANT || epoch_nanoseconds > NANOSECONDS_MAX_INSTANT) { // a. Return false. return false; } // 2. Return true. return true; } // 8.5.2 CreateTemporalInstant ( epochNanoseconds [ , newTarget ] ), https://tc39.es/proposal-temporal/#sec-temporal-isvalidepochnanoseconds ThrowCompletionOr> create_temporal_instant(VM& vm, BigInt const& epoch_nanoseconds, GC::Ptr new_target) { auto& realm = *vm.current_realm(); // 1. Assert: IsValidEpochNanoseconds(epochNanoseconds) is true. VERIFY(is_valid_epoch_nanoseconds(epoch_nanoseconds.big_integer())); // 2. If newTarget is not present, set newTarget to %Temporal.Instant%. if (!new_target) new_target = realm.intrinsics().temporal_instant_constructor(); // 3. Let object be ? OrdinaryCreateFromConstructor(newTarget, "%Temporal.Instant.prototype%", « [[InitializedTemporalInstant]], [[EpochNanoseconds]] »). // 4. Set object.[[EpochNanoseconds]] to epochNanoseconds. auto object = TRY(ordinary_create_from_constructor(vm, *new_target, &Intrinsics::temporal_instant_prototype, epoch_nanoseconds)); // 5. Return object. return object; } // 8.5.3 ToTemporalInstant ( item ), https://tc39.es/proposal-temporal/#sec-temporal-totemporalinstant ThrowCompletionOr> to_temporal_instant(VM& vm, Value item) { // 1. If item is an Object, then if (item.is_object()) { auto const& object = item.as_object(); // a. If item has an [[InitializedTemporalInstant]] or [[InitializedTemporalZonedDateTime]] internal slot, then // FIXME: Handle ZonedDateTime. if (is(object)) { // i. Return ! CreateTemporalInstant(item.[[EpochNanoseconds]]). return MUST(create_temporal_instant(vm, static_cast(object).epoch_nanoseconds())); } // b. NOTE: This use of ToPrimitive allows Instant-like objects to be converted. // c. Set item to ? ToPrimitive(item, STRING). item = TRY(item.to_primitive(vm, Value::PreferredType::String)); } // 2. If item is not a String, throw a TypeError exception. if (!item.is_string()) return vm.throw_completion(ErrorType::TemporalInvalidInstantString, item); // 3. Let parsed be ? ParseISODateTime(item, « TemporalInstantString »). auto parsed = TRY(parse_iso_date_time(vm, item.as_string().utf8_string_view(), { { Production::TemporalInstantString } })); // 4. Assert: Either parsed.[[TimeZone]].[[OffsetString]] is not empty or parsed.[[TimeZone]].[[Z]] is true, but not both. auto const& offset_string = parsed.time_zone.offset_string; auto z_designator = parsed.time_zone.z_designator; VERIFY(offset_string.has_value() || z_designator); VERIFY(!offset_string.has_value() || !z_designator); // 5. If parsed.[[TimeZone]].[[Z]] is true, let offsetNanoseconds be 0; otherwise, let offsetNanoseconds be // ! ParseDateTimeUTCOffset(parsed.[[TimeZone]].[[OffsetString]]). auto offset_nanoseconds = z_designator ? 0.0 : parse_date_time_utc_offset(*offset_string); // 6. If parsed.[[Time]] is START-OF-DAY, let time be MidnightTimeRecord(); else let time be parsed.[[Time]]. auto time = parsed.time.has() ? midnight_time_record() : parsed.time.get