1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 12:48:10 +00:00

LibJS: Allow formatting UTC-offset time zones with Intl.DateTimeFormat

These are normative changes in the ECMA-402 spec. See:
896ffcc
af4ec46
e25c455

(This combines the above commits into one patch as they each do not work
on their own).
This commit is contained in:
Timothy Flynn 2023-10-04 15:45:59 -04:00 committed by Andreas Kling
parent f31540e419
commit 39be5cb73a
7 changed files with 123 additions and 42 deletions

View file

@ -16,6 +16,7 @@
#include <LibJS/Runtime/Intl/NumberFormat.h>
#include <LibJS/Runtime/Intl/NumberFormatConstructor.h>
#include <LibJS/Runtime/NativeFunction.h>
#include <LibJS/Runtime/Temporal/TimeZone.h>
#include <LibJS/Runtime/Utf16String.h>
#include <LibLocale/Locale.h>
#include <LibLocale/NumberFormat.h>
@ -1124,20 +1125,33 @@ ThrowCompletionOr<NonnullGCPtr<Array>> format_date_time_range_to_parts(VM& vm, D
return result;
}
// 11.5.12 ToLocalTime ( epochNs, calendar, timeZone ), https://tc39.es/ecma402/#sec-tolocaltime
ThrowCompletionOr<LocalTime> to_local_time(VM& vm, Crypto::SignedBigInteger const& epoch_ns, StringView calendar, StringView time_zone)
// 11.5.12 ToLocalTime ( epochNs, calendar, timeZoneIdentifier ), https://tc39.es/ecma402/#sec-tolocaltime
ThrowCompletionOr<LocalTime> to_local_time(VM& vm, Crypto::SignedBigInteger const& epoch_ns, StringView calendar, StringView time_zone_identifier)
{
// 1. Let offsetNs be GetNamedTimeZoneOffsetNanoseconds(timeZone, epochNs).
auto offset_ns = get_named_time_zone_offset_nanoseconds(time_zone, epoch_ns);
double offset_ns { 0 };
// 1. If IsTimeZoneOffsetString(timeZoneIdentifier) is true, then
if (is_time_zone_offset_string(time_zone_identifier)) {
// a. Let offsetNs be ParseTimeZoneOffsetString(timeZoneIdentifier).
offset_ns = parse_time_zone_offset_string(time_zone_identifier);
}
// 2. Else,
else {
// a. Assert: IsValidTimeZoneName(timeZoneIdentifier) is true.
VERIFY(Temporal::is_available_time_zone_name(time_zone_identifier));
// b. Let offsetNs be GetNamedTimeZoneOffsetNanoseconds(timeZoneIdentifier, epochNs).
offset_ns = get_named_time_zone_offset_nanoseconds(time_zone_identifier, epoch_ns);
}
// NOTE: Unlike the spec, we still perform the below computations with BigInts until we are ready
// to divide the number by 10^6. The spec expects an MV here. If we try to use i64, we will
// overflow; if we try to use a double, we lose quite a bit of accuracy.
// 2. Let tz be (epochNs) + offsetNs.
// 3. Let tz be (epochNs) + offsetNs.
auto zoned_time_ns = epoch_ns.plus(Crypto::SignedBigInteger { offset_ns });
// 3. If calendar is "gregory", then
// 4. If calendar is "gregory", then
if (calendar == "gregory"sv) {
auto zoned_time_ms = zoned_time_ns.divided_by(s_one_million_bigint).quotient;
auto zoned_time = floor(zoned_time_ms.to_double(Crypto::UnsignedBigInteger::RoundingMode::ECMAScriptNumberValueFor));
@ -1171,7 +1185,7 @@ ThrowCompletionOr<LocalTime> to_local_time(VM& vm, Crypto::SignedBigInteger cons
};
}
// 4. Else,
// 5. Else,
// a. Return a record with the fields of Column 1 of Table 8 calculated from tz for the given calendar. The calculations should use best available information about the specified calendar.
// FIXME: Implement this when non-Gregorian calendars are supported by LibUnicode.
return vm.throw_completion<InternalError>(ErrorType::NotImplemented, "Non-Gregorian calendars"sv);