LibJS: Cache the result of parsing time zone identifiers

The result of parsing an identifier cannot change. It is not cheap to do
so, so let's cache the result.

This is hammered in a few test262 tests. On my machine, this reduces the
runtime of each test/staging/sm/Date/dst-offset-caching-{N}-of-8.js by
0.3 to 0.5 seconds. For example:

dst-offset-caching-1-of-8.js: Reduces from 1.2s -> 0.9s
dst-offset-caching-3-of-8.js: Reduces from 1.5s -> 1.1s
This commit is contained in:
Timothy Flynn 2026-02-18 12:55:34 -05:00 committed by Jelle Raaijmakers
parent 6e433f0d10
commit d1ed361239
2 changed files with 20 additions and 10 deletions

View file

@ -244,7 +244,7 @@ ThrowCompletionOr<String> to_temporal_time_zone_identifier(VM& vm, StringView te
i64 get_offset_nanoseconds_for(String const& time_zone, Crypto::SignedBigInteger const& epoch_nanoseconds)
{
// 1. Let parseResult be ! ParseTimeZoneIdentifier(timeZone).
auto parse_result = parse_time_zone_identifier(time_zone);
auto const& parse_result = parse_time_zone_identifier(time_zone);
// 2. If parseResult.[[OffsetMinutes]] is not empty, return parseResult.[[OffsetMinutes]] × (60 × 10**9).
if (parse_result.offset_minutes.has_value())
@ -323,7 +323,7 @@ ThrowCompletionOr<Vector<Crypto::SignedBigInteger>> get_possible_epoch_nanosecon
Vector<Crypto::SignedBigInteger> possible_epoch_nanoseconds;
// 1. Let parseResult be ! ParseTimeZoneIdentifier(timeZone).
auto parse_result = parse_time_zone_identifier(time_zone);
auto const& parse_result = parse_time_zone_identifier(time_zone);
// 2. If parseResult.[[OffsetMinutes]] is not empty, then
if (parse_result.offset_minutes.has_value()) {
@ -421,9 +421,15 @@ bool time_zone_equals(StringView one, StringView two)
return false;
}
// OPTIMIZATION: The result of parsing a time zone identifier will not change, so we can cache the result.
static HashMap<String, ParsedTimeZoneIdentifier> s_time_zone_id_cache;
// 11.1.16 ParseTimeZoneIdentifier ( identifier ), https://tc39.es/proposal-temporal/#sec-parsetimezoneidentifier
ThrowCompletionOr<ParsedTimeZoneIdentifier> parse_time_zone_identifier(VM& vm, String const& identifier)
{
if (auto result = s_time_zone_id_cache.get(identifier); result.has_value())
return *result;
// 1. Let parseResult be ParseText(StringToCodePoints(identifier), TimeZoneIdentifier).
auto parse_result = parse_iso8601(Production::TimeZoneIdentifier, identifier);
@ -431,19 +437,23 @@ ThrowCompletionOr<ParsedTimeZoneIdentifier> parse_time_zone_identifier(VM& vm, S
if (!parse_result.has_value())
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidTimeZoneString, identifier);
return parse_time_zone_identifier(*parse_result);
auto result = parse_time_zone_identifier(*parse_result);
s_time_zone_id_cache.set(identifier, result);
return result;
}
// 11.1.16 ParseTimeZoneIdentifier ( identifier ), https://tc39.es/proposal-temporal/#sec-parsetimezoneidentifier
ParsedTimeZoneIdentifier parse_time_zone_identifier(String const& identifier)
ParsedTimeZoneIdentifier const& parse_time_zone_identifier(String const& identifier)
{
// OPTIMIZATION: Some callers can assume that parsing will succeed.
return s_time_zone_id_cache.ensure(identifier, [&]() {
// 1. Let parseResult be ParseText(StringToCodePoints(identifier), TimeZoneIdentifier).
auto parse_result = parse_iso8601(Production::TimeZoneIdentifier, identifier);
VERIFY(parse_result.has_value());
// 1. Let parseResult be ParseText(StringToCodePoints(identifier), TimeZoneIdentifier).
auto parse_result = parse_iso8601(Production::TimeZoneIdentifier, identifier);
VERIFY(parse_result.has_value());
return parse_time_zone_identifier(*parse_result);
return parse_time_zone_identifier(*parse_result);
});
}
// 11.1.16 ParseTimeZoneIdentifier ( identifier ), https://tc39.es/proposal-temporal/#sec-parsetimezoneidentifier

View file

@ -35,7 +35,7 @@ ThrowCompletionOr<Vector<Crypto::SignedBigInteger>> get_possible_epoch_nanosecon
ThrowCompletionOr<Crypto::SignedBigInteger> get_start_of_day(VM&, String const& time_zone, ISODate);
bool time_zone_equals(StringView one, StringView two);
ThrowCompletionOr<ParsedTimeZoneIdentifier> parse_time_zone_identifier(VM&, String const& identifier);
ParsedTimeZoneIdentifier parse_time_zone_identifier(String const& identifier);
ParsedTimeZoneIdentifier const& parse_time_zone_identifier(String const& identifier);
ParsedTimeZoneIdentifier parse_time_zone_identifier(ParseResult const&);
}