Skip to content

Cache Intl.DateTimeFormat to reduce memory footprint #10

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 9, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Cache Intl.DateTimeFormat to reduce memory footprint
Creating Intl.DateTimeFormat instances in V8 is very slow and memory heavy.
GetFormatterParts and GetCanonicalTimeZoneIdentifier are functions that
are called many times when using Temporal, and they used to create new
instances of Intl.DateTimeFormat for each call. In this commit, we cache them
using the time zone identifier as the key.

It should be noted that doing the same to SystemTimeZone was deliberately
avoided. This is due to the fact that the user's time zone may change during
the execution of a program. An example is calling Temporal.now.zonedDateTimeISO()
which should always output the correct time zone. This shouldn't be a problem
for server-sided code which usually doesn't (or rather, shouldn't) use the
time zone from the environment for calculations.
  • Loading branch information
ferk6a committed Jul 9, 2021
commit 289ea6f7fd02ff73acc4efb34ab7bbaeb158f3d2
44 changes: 23 additions & 21 deletions lib/ecmascript.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,27 @@ const ES2020 = {
Type
};

const IntlDateTimeFormatEnUsCache = new Map();

function getIntlDateTimeFormatEnUsForTimeZone(timeZoneIdentifier) {
let instance = IntlDateTimeFormatEnUsCache.get(timeZoneIdentifier);
if (instance === undefined) {
instance = new IntlDateTimeFormat('en-us', {
timeZone: String(timeZoneIdentifier),
hour12: false,
era: 'short',
year: 'numeric',
month: 'numeric',
day: 'numeric',
hour: 'numeric',
minute: 'numeric',
second: 'numeric'
});
IntlDateTimeFormatEnUsCache.set(timeZoneIdentifier, instance);
}
return instance;
}

export const ES = ObjectAssign({}, ES2020, {
ToPositiveInteger: ToPositiveInteger,
ToFiniteInteger: (value) => {
Expand Down Expand Up @@ -2081,16 +2102,7 @@ export const ES = ObjectAssign({}, ES2020, {
GetCanonicalTimeZoneIdentifier: (timeZoneIdentifier) => {
const offsetNs = ES.ParseOffsetString(timeZoneIdentifier);
if (offsetNs !== null) return ES.FormatTimeZoneOffsetString(offsetNs);
const formatter = new IntlDateTimeFormat('en-us', {
timeZone: String(timeZoneIdentifier),
hour12: false,
year: 'numeric',
month: 'numeric',
day: 'numeric',
hour: 'numeric',
minute: 'numeric',
second: 'numeric'
});
const formatter = getIntlDateTimeFormatEnUsForTimeZone(String(timeZoneIdentifier));
return formatter.resolvedOptions().timeZone;
},
GetIANATimeZoneOffsetNanoseconds: (epochNanoseconds, id) => {
Expand Down Expand Up @@ -2209,17 +2221,7 @@ export const ES = ObjectAssign({}, ES2020, {
return result;
},
GetFormatterParts: (timeZone, epochMilliseconds) => {
const formatter = new IntlDateTimeFormat('en-us', {
timeZone,
hour12: false,
era: 'short',
year: 'numeric',
month: 'numeric',
day: 'numeric',
hour: 'numeric',
minute: 'numeric',
second: 'numeric'
});
const formatter = getIntlDateTimeFormatEnUsForTimeZone(timeZone);
// FIXME: can this use formatToParts instead?
const datetime = formatter.format(new Date(epochMilliseconds));
const [date, fullYear, time] = datetime.split(/,\s+/);
Expand Down