Repository: LadybirdBrowser/ladybird
File: Libraries/LibJS/Runtime/Date.cpp (C++)

Code before:
}

// 21.4.1.18 GetUTCEpochNanoseconds ( year, month, day, hour, minute, second, millisecond, microsecond, nanosecond ), https://tc39.es/ecma262/#sec-getutcepochnanoseconds
// 14.5.1 GetUTCEpochNanoseconds ( isoDateTime ), https://tc39.es/proposal-temporal/#sec-getutcepochnanoseconds
Crypto::SignedBigInteger get_utc_epoch_nanoseconds(Temporal::ISODateTime const& iso_date_time)
{
    // 1. Let date be MakeDay(𝔽(isoDateTime.[[ISODate]].[[Year]]), 𝔽(isoDateTime.[[ISODate]].[[Month]] - 1), 𝔽(isoDateTime.[[ISODate]].[[Day]])).
    auto date = make_day(iso_date_time.iso_date.year, iso_date_time.iso_date.month - 1, iso_date_time.iso_date.day);

    // 2. Let time be MakeTime(𝔽(isoDateTime.[[Time]].[[Hour]]), 𝔽(isoDateTime.[[Time]].[[Minute]]), 𝔽(isoDateTime.[[Time]].[[Second]]), 𝔽(isoDateTime.[[Time]].[[Millisecond]])).
    auto time = make_time(iso_date_time.time.hour, iso_date_time.time.minute, iso_date_time.time.second, iso_date_time.time.millisecond);

    // 3. Let ms be MakeDate(date, time).
    auto ms = make_date(date, time);

    // 4. Assert: ms is an integral Number.
    VERIFY(ms == trunc(ms));

    // 5. Return ℤ(ℝ(ms) × 10**6 + isoDateTime.[[Time]].[[Microsecond]] × 10**3 + isoDateTime.[[Time]].[[Nanosecond]]).
    auto result = Crypto::SignedBigInteger { ms }.multiplied_by(Temporal::NANOSECONDS_PER_MILLISECOND);
    result = result.plus(Crypto::SignedBigInteger { static_cast<i32>(iso_date_time.time.microsecond) }.multiplied_by(Temporal::NANOSECONDS_PER_MICROSECOND));
    result = result.plus(Crypto::SignedBigInteger { static_cast<i32>(iso_date_time.time.nanosecond) });
    return result;
}

static i64 clip_double_to_sane_time(double value)
{
    static constexpr auto min_double = static_cast<double>(NumericLimits<i64>::min());
    static constexpr auto max_double = static_cast<double>(NumericLimits<i64>::max());

    // The provided epoch milliseconds value is potentially out of range for AK::Duration and subsequently
    // get_time_zone_offset(). We can safely assume that the TZDB has no useful information that far
    // into the past and future anyway, so clamp it to the i64 range.
    if (value < min_double)
        return NumericLimits<i64>::min();
    if (value > max_double)
        return NumericLimits<i64>::max();

    return static_cast<i64>(value);
}

// 21.4.1.20 GetNamedTimeZoneEpochNanoseconds ( timeZoneIdentifier, year, month, day, hour, minute, second, millisecond, microsecond, nanosecond ), https://tc39.es/ecma262/#sec-getnamedtimezoneepochnanoseconds
// 14.6.3 GetNamedTimeZoneEpochNanoseconds ( timeZoneIdentifier, isoDateTime ), https://tc39.es/proposal-temporal/#sec-getnamedtimezoneepochnanoseconds
Vector<Crypto::SignedBigInteger> get_named_time_zone_epoch_nanoseconds(StringView time_zone_identifier, Temporal::ISODateTime const& iso_date_time)
{
    auto local_nanoseconds = get_utc_epoch_nanoseconds(iso_date_time);
    auto local_time = UnixDateTime::from_nanoseconds_since_epoch(Temporal::clip_bigint_to_sane_time(local_nanoseconds));

    auto offsets = Unicode::disambiguated_time_zone_offsets(time_zone_identifier, local_time);

    Vector<Crypto::SignedBigInteger> result;

Review comment (refactor):
If we're moving one of these, let's move both - kinda awkward to have them in different files otherwise

Though instead of moving them, it's probably fine to just declare them in Date.h? Temporal already uses other helpers from there

Code after:
}

// 21.4.1.18 GetUTCEpochNanoseconds ( year, month, day, hour, minute, second, millisecond, microsecond, nanosecond ), https://tc39.es/ecma262/#sec-getutcepochnanoseconds
// 14.5.1 GetUTCEpochNanoseconds ( isoDateTime ), https://tc39.es/proposal-temporal/#sec-getutcepochnanoseconds
Crypto::SignedBigInteger get_utc_epoch_nanoseconds(Temporal::ISODateTime const& iso_date_time)
{
    // 1. Let date be MakeDay(𝔽(isoDateTime.[[ISODate]].[[Year]]), 𝔽(isoDateTime.[[ISODate]].[[Month]] - 1), 𝔽(isoDateTime.[[ISODate]].[[Day]])).
    auto date = make_day(iso_date_time.iso_date.year, iso_date_time.iso_date.month - 1, iso_date_time.iso_date.day);

    // 2. Let time be MakeTime(𝔽(isoDateTime.[[Time]].[[Hour]]), 𝔽(isoDateTime.[[Time]].[[Minute]]), 𝔽(isoDateTime.[[Time]].[[Second]]), 𝔽(isoDateTime.[[Time]].[[Millisecond]])).
    auto time = make_time(iso_date_time.time.hour, iso_date_time.time.minute, iso_date_time.time.second, iso_date_time.time.millisecond);

    // 3. Let ms be MakeDate(date, time).
    auto ms = make_date(date, time);

    // 4. Assert: ms is an integral Number.
    VERIFY(ms == trunc(ms));

    // 5. Return ℤ(ℝ(ms) × 10**6 + isoDateTime.[[Time]].[[Microsecond]] × 10**3 + isoDateTime.[[Time]].[[Nanosecond]]).
    auto result = Crypto::SignedBigInteger { ms }.multiplied_by(Temporal::NANOSECONDS_PER_MILLISECOND);
    result = result.plus(Crypto::SignedBigInteger { static_cast<i32>(iso_date_time.time.microsecond) }.multiplied_by(Temporal::NANOSECONDS_PER_MICROSECOND));
    result = result.plus(Crypto::SignedBigInteger { static_cast<i32>(iso_date_time.time.nanosecond) });
    return result;
}

i64 clip_bigint_to_sane_time(Crypto::SignedBigInteger const& value)
{
    static Crypto::SignedBigInteger const min_bigint { NumericLimits<i64>::min() };
    static Crypto::SignedBigInteger const max_bigint { NumericLimits<i64>::max() };

    // The provided epoch (nano)seconds value is potentially out of range for AK::Duration and subsequently
    // get_time_zone_offset(). We can safely assume that the TZDB has no useful information that far
    // into the past and future anyway, so clamp it to the i64 range.
    if (value < min_bigint)
        return NumericLimits<i64>::min();
    if (value > max_bigint)
        return NumericLimits<i64>::max();

    return value.to_i64();
}

i64 clip_double_to_sane_time(double value)
{
    static constexpr auto min_double = static_cast<double>(NumericLimits<i64>::min());
    static constexpr auto max_double = static_cast<double>(NumericLimits<i64>::max());

    // The provided epoch milliseconds value is potentially out of range for AK::Duration and subsequently
    // get_time_zone_offset(). We can safely assume that the TZDB has no useful information that far
    // into the past and future anyway, so clamp it to the i64 range.
    if (value < min_double)
        return NumericLimits<i64>::min();