1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 00:27:43 +00:00

LibJS: Support non-UTC time zones in Temporal :^)

We can now recognize & normalize all time zones from the IANA time zone
database and not just 'UTC', which makes the LibJS Temporal
implementation a lot more useful! Thanks to the newly added LibTimeZone,
this was incredibly easy to implement :^)

This already includes these recent editorial changes in the Temporal
spec: 27bffe1
This commit is contained in:
Linus Groh 2022-01-11 20:20:16 +01:00
parent 205d63c3f0
commit d527eb62da
5 changed files with 49 additions and 27 deletions

View file

@ -16,6 +16,7 @@
#include <LibJS/Runtime/Temporal/TimeZone.h>
#include <LibJS/Runtime/Temporal/TimeZoneConstructor.h>
#include <LibJS/Runtime/Temporal/ZonedDateTime.h>
#include <LibTimeZone/TimeZone.h>
namespace JS::Temporal {
@ -27,35 +28,30 @@ TimeZone::TimeZone(String identifier, Object& prototype)
}
// 11.1.1 IsValidTimeZoneName ( timeZone ), https://tc39.es/proposal-temporal/#sec-isvalidtimezonename
// NOTE: This is the minimum implementation of IsValidTimeZoneName, supporting only the "UTC" time zone.
// 15.1.1 IsValidTimeZoneName ( timeZone ), https://tc39.es/proposal-temporal/#sup-isvalidtimezonename
bool is_valid_time_zone_name(String const& time_zone)
{
// 1. Assert: Type(timeZone) is String.
// 2. Let tzText be ! StringToCodePoints(timeZone).
// 3. Let tzUpperText be the result of toUppercase(tzText), according to the Unicode Default Case Conversion algorithm.
// 4. Let tzUpper be ! CodePointsToString(tzUpperText).
auto tz_upper = time_zone.to_uppercase();
// 5. If tzUpper and "UTC" are the same sequence of code points, return true.
if (tz_upper == "UTC")
return true;
// 6. Return false.
return false;
// 1. If one of the Zone or Link names of the IANA Time Zone Database is an ASCII-case-insensitive match of timeZone as described in 6.1, return true.
// 2. If timeZone is an ASCII-case-insensitive match of "UTC", return true.
// 3. Return false.
// NOTE: When LibTimeZone is built without ENABLE_TIME_ZONE_DATA, this only recognizes 'UTC',
// which matches the minimum requirements of the Temporal spec.
return ::TimeZone::time_zone_from_string(time_zone).has_value();
}
// 11.1.2 CanonicalizeTimeZoneName ( timeZone ), https://tc39.es/proposal-temporal/#sec-canonicalizetimezonename
// NOTE: This is the minimum implementation of CanonicalizeTimeZoneName, supporting only the "UTC" time zone.
// 15.1.2 CanonicalizeTimeZoneName ( timeZone ), https://tc39.es/proposal-temporal/#sup-canonicalizetimezonename
String canonicalize_time_zone_name(String const& time_zone)
{
// 1. Assert: Type(timeZone) is String.
// 1. Let ianaTimeZone be the String value of the Zone or Link name of the IANA Time Zone Database that is an ASCII-case-insensitive match of timeZone as described in 6.1.
// 2. If ianaTimeZone is a Link name, let ianaTimeZone be the String value of the corresponding Zone name as specified in the file backward of the IANA Time Zone Database.
auto iana_time_zone = ::TimeZone::canonicalize_time_zone(time_zone);
// 2. Assert: ! IsValidTimeZoneName(timeZone) is true.
VERIFY(is_valid_time_zone_name(time_zone));
// 3. If ianaTimeZone is "Etc/UTC" or "Etc/GMT", return "UTC".
// NOTE: This is already done in canonicalize_time_zone().
// 3. Return "UTC".
return "UTC";
// 4. Return ianaTimeZone.
return *iana_time_zone;
}
// 11.1.3 DefaultTimeZone ( ), https://tc39.es/proposal-temporal/#sec-defaulttimezone
@ -160,12 +156,12 @@ ISODateTime get_iso_parts_from_epoch(BigInt const& epoch_nanoseconds)
}
// 11.6.4 GetIANATimeZoneEpochValue ( timeZoneIdentifier, year, month, day, hour, minute, second, millisecond, microsecond, nanosecond ), https://tc39.es/proposal-temporal/#sec-temporal-getianatimezoneepochvalue
MarkedValueList get_iana_time_zone_epoch_value(GlobalObject& global_object, StringView time_zone_identifier, i32 year, u8 month, u8 day, u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond)
MarkedValueList get_iana_time_zone_epoch_value(GlobalObject& global_object, [[maybe_unused]] StringView time_zone_identifier, i32 year, u8 month, u8 day, u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond)
{
// The abstract operation GetIANATimeZoneEpochValue is an implementation-defined algorithm that returns a List of integers. Each integer in the List represents a number of nanoseconds since the Unix epoch in UTC that may correspond to the given calendar date and wall-clock time in the IANA time zone identified by timeZoneIdentifier.
// When the input represents a local time repeating multiple times at a negative time zone transition (e.g. when the daylight saving time ends or the time zone offset is decreased due to a time zone rule change), the returned List will have more than one element. When the input represents a skipped local time at a positive time zone transition (e.g. when the daylight saving time starts or the time zone offset is increased due to a time zone rule change), the returned List will be empty. Otherwise, the returned List will have one element.
VERIFY(time_zone_identifier == "UTC"sv);
// FIXME: Implement this properly for non-UTC timezones.
// FIXME: MarkedValueList<T> for T != Value would still be nice.
auto& vm = global_object.vm();
auto list = MarkedValueList { vm.heap() };