mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-27 01:50:24 +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: https://github.com/tc39/proposal-temporal/commit/27bffe1
This commit is contained in:
parent
205d63c3f0
commit
d527eb62da
Notes:
sideshowbarker
2024-07-17 21:11:33 +09:00
Author: https://github.com/linusg Commit: https://github.com/SerenityOS/serenity/commit/d527eb62da3 Pull-request: https://github.com/SerenityOS/serenity/pull/11815 Reviewed-by: https://github.com/IdanHo Reviewed-by: https://github.com/trflynn89 ✅
5 changed files with 49 additions and 27 deletions
|
@ -219,4 +219,4 @@ set(SOURCES
|
|||
)
|
||||
|
||||
serenity_lib(LibJS js)
|
||||
target_link_libraries(LibJS LibM LibCore LibCrypto LibRegex LibSyntax LibUnicode)
|
||||
target_link_libraries(LibJS LibM LibCore LibCrypto LibRegex LibSyntax LibTimeZone LibUnicode)
|
||||
|
|
|
@ -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() };
|
||||
|
|
|
@ -10,6 +10,14 @@ describe("normal behavior", () => {
|
|||
expect(Temporal.TimeZone.from(timeZone)).toBe(timeZone);
|
||||
expect(Temporal.TimeZone.from(timeZoneLike)).toBe(timeZoneLike);
|
||||
expect(Temporal.TimeZone.from(zonedDateTimeLike)).toBe(zonedDateTimeLike.timeZone);
|
||||
// TODO: test from("string") once ParseTemporalTimeZoneString is working
|
||||
expect(Temporal.TimeZone.from("UTC").id).toBe("UTC");
|
||||
expect(Temporal.TimeZone.from("GMT").id).toBe("UTC");
|
||||
expect(Temporal.TimeZone.from("Etc/UTC").id).toBe("UTC");
|
||||
expect(Temporal.TimeZone.from("Etc/GMT").id).toBe("UTC");
|
||||
// FIXME: https://github.com/tc39/proposal-temporal/issues/1993
|
||||
// expect(Temporal.TimeZone.from("Etc/GMT+12").id).toBe("Etc/GMT+12");
|
||||
// expect(Temporal.TimeZone.from("Etc/GMT-12").id).toBe("Etc/GMT-12");
|
||||
expect(Temporal.TimeZone.from("Europe/London").id).toBe("Europe/London");
|
||||
expect(Temporal.TimeZone.from("Europe/Isle_of_Man").id).toBe("Europe/London");
|
||||
});
|
||||
});
|
||||
|
|
|
@ -36,9 +36,22 @@ describe("normal behavior", () => {
|
|||
});
|
||||
|
||||
test("canonicalizes time zone name", () => {
|
||||
expect(new Temporal.TimeZone("Utc").id).toBe("UTC");
|
||||
expect(new Temporal.TimeZone("utc").id).toBe("UTC");
|
||||
expect(new Temporal.TimeZone("uTC").id).toBe("UTC");
|
||||
const values = [
|
||||
["UTC", "UTC"],
|
||||
["Utc", "UTC"],
|
||||
["utc", "UTC"],
|
||||
["uTc", "UTC"],
|
||||
["GMT", "UTC"],
|
||||
["Etc/UTC", "UTC"],
|
||||
["Etc/GMT", "UTC"],
|
||||
["Etc/GMT+12", "Etc/GMT+12"],
|
||||
["Etc/GMT-12", "Etc/GMT-12"],
|
||||
["Europe/London", "Europe/London"],
|
||||
["Europe/Isle_of_Man", "Europe/London"],
|
||||
];
|
||||
for (const [arg, expected] of values) {
|
||||
expect(new Temporal.TimeZone(arg).id).toBe(expected);
|
||||
}
|
||||
});
|
||||
|
||||
test("numeric UTC offset", () => {
|
||||
|
|
|
@ -8,6 +8,11 @@ describe("correct behavior", () => {
|
|||
["utc", "UTC"],
|
||||
["Utc", "UTC"],
|
||||
["UTC", "UTC"],
|
||||
["GMT", "UTC"],
|
||||
["Etc/UTC", "UTC"],
|
||||
["Etc/GMT", "UTC"],
|
||||
["Europe/London", "Europe/London"],
|
||||
["Europe/Isle_of_Man", "Europe/London"],
|
||||
["+00:00", "+00:00"],
|
||||
["+00:00:00", "+00:00"],
|
||||
["+00:00:00.000", "+00:00"],
|
||||
|
|
Loading…
Reference in a new issue