How to optimize Date format operations
Many web applications require the presentation of user-friendly date strings within their UI. For example, most users would
prefer seeing Monday, September 6, 2021
rather than the ISO time string 2021-09-07T00:00:00.000Z
!
In order to translate JavaScript Date
objects into user-friendly date strings, web developers must utilize the available
in-browser date string formatting capabilities.
Beware of Date.toLocaleDateString(...)
Date.toLocaleDateString(...)
is the go-to API JavaScript developers use for formatting dates due to its convenient access and
straightforward API.
It accepts both a locale (for example, en-US
) and a set of formatter options, which specify how the date should be formatted.
Consider this example:
const today = new Date();
const todayFormattedNicely = today.toLocaleDateString('en-US', {
month: 'long',
weekday: 'long',
day: 'numeric',
year: 'numeric'
});
// "Sunday, September 5, 2021"
console.log(todayFormattedNicely);
There are various options one can provide in the second argument to produce different kinds of date and time format strings.
Performance Implications
Since the Date
object is a native JavaScript built-in, Date.toLocaleDateString
invokes native codepaths within the JavaScript engine.
Specifically, this API instructs the engine to:
- Create a new
DateTimeFormat
object for the specified locale and format arguments. ⚠️This operation is expensive!⚠️ - Format the
Date
as a formatted date string utilizing theDateTimeFormat
- Return the formatted date string, discarding the
DateTimeFormat
Note: I encourage you to read the V8 source code of this yourself and compare with my summary!
If a web application only requires formatting of a single Date
within its lifetime, then this series of steps is acceptable. But modern web apps
often have long lifetimes and format multiple dates (consider a web dashboard, CRM, or social network). It's therefore wasteful to
keep creating and discarding DateTimeFormat
objects to format each date string.
Consider the following example:
const datesToFormat = [
new Date("1775-04-18T00:00:00.000Z"),
new Date("1775-04-19T00:00:00.000Z"),
new Date("1775-04-20T00:00:00.000Z"),
new Date("1775-04-21T00:00:00.000Z"),
new Date("1775-04-22T00:00:00.000Z"),
];
for (const date of datesToFormat) {
const formattedDateString = date.toLocaleDateString('en-US', {
day: 'numeric',
month: 'long',
year: 'numeric'
});
// "April 19, 1775" ... "April 23, 1775"
console.log(formattedDateString);
}
We are formatting 5 Date
objects here, but each call to toLocaleDateString()
is internally creating an expensive
new DateTimeFormat
and discarding it after formatting the Date
to a string.
As a result, we are wasting CPU time, degrading the browser's ability to generate frames, and degrading user experience.
Solution: Intl.DateTimeFormat
Fortunately, browsers support an API called Intl.DateTimeFormat
that allows web developers to create DateTimeFormat
objects,
and cache them in-memory for usage across multiple dates.
This API utilizes the same arguments as Date.toLocaleDateString(...)
, but produces a re-usable formatter instead.
Consider this example:
// Expensive to create, but re-usable and only created once!
const formatter = new Intl.DateTimeFormat('en-US', {
day: 'numeric',
month: 'long',
year: 'numeric'
});
const datesToFormat = [
new Date("1775-04-18T00:00:00.000Z"),
new Date("1775-04-19T00:00:00.000Z"),
new Date("1775-04-20T00:00:00.000Z"),
new Date("1775-04-21T00:00:00.000Z"),
new Date("1775-04-22T00:00:00.000Z"),
];
for (const date of datesToFormat) {
// This operation is very fast!
const dateString = formatter.format(date);
// "April 19, 1775" ... "April 23, 1775"
console.log(dateString);
}
By using Intl.DateTimeFormat
, we are reducing overall work by in-memory caching the DateTimeFormat
and re-using it for each date format operation,
instead of continuously creating and discarding DateTimeFormat
objects internally with toLocaleDateString()
.
Profiled Example
I've put together an demo page that formats 1000 dates utilizing both approaches.
On my machine, formatting 1000 Date
objects using toLocaleDateString()
took about 217ms:
This time was mostly spent creating 1000 distinct DateTimeFormat
objects.
On the same machine, creating a single Intl.DateTimeFormat
object and using it to format 1000 Date
objects took around 4ms:
If you want to see try this yourself check out the demo page with your profiler running!
That's all for this tip! Thanks for reading! Discover more similar tips matching CPU and JS Optimization.