From bb77d5e6b968b0a2cc1be72d295f40a43a6687fb Mon Sep 17 00:00:00 2001 From: Isaac Cambron Date: Sat, 3 Nov 2018 17:41:58 -0400 Subject: [PATCH] cache intl objects for 85x speedup --- benchmarks/datetime.js | 3 +++ src/impl/locale.js | 47 ++++++++++++++++++++++++++++++++---------- 2 files changed, 39 insertions(+), 11 deletions(-) diff --git a/benchmarks/datetime.js b/benchmarks/datetime.js index b7d60515f..3f60524d2 100644 --- a/benchmarks/datetime.js +++ b/benchmarks/datetime.js @@ -37,6 +37,9 @@ suite .add("DateTime#toISO", () => { dt.toISO(); }) + .add("DateTime#toFormatString", () => { + dt.toLocaleString(); + }) .on("cycle", event => { console.log(String(event.target)); }) diff --git a/src/impl/locale.js b/src/impl/locale.js index e53a66521..2160a1bfe 100644 --- a/src/impl/locale.js +++ b/src/impl/locale.js @@ -4,6 +4,28 @@ import Settings from "../settings"; import DateTime from "../datetime"; import Formatter from "./formatter"; +let intlDTCache = {}; +function getCachedDTF(locString, opts = {}) { + const key = JSON.stringify([locString, opts]); + let dtf = intlDTCache[key]; + if (!dtf) { + dtf = new Intl.DateTimeFormat(locString, opts); + intlDTCache[key] = dtf; + } + return dtf; +} + +let intlNumCache = {}; +function getCachendINF(locString, opts = {}) { + const key = JSON.stringify([locString, opts]); + let inf = intlNumCache[key]; + if (!inf) { + inf = new Intl.NumberFormat(locString, opts); + intlNumCache[key] = inf; + } + return inf; +} + let sysLocaleCache = null; function systemLocale() { if (sysLocaleCache) { @@ -35,9 +57,9 @@ function parseLocaleString(localeStr) { let options; const smaller = localeStr.substring(0, uIndex); try { - options = Intl.DateTimeFormat(localeStr).resolvedOptions(); + options = getCachedDTF(localeStr).resolvedOptions(); } catch (e) { - options = Intl.DateTimeFormat(smaller).resolvedOptions(); + options = getCachedDTF(smaller).resolvedOptions(); } const { numberingSystem, calendar } = options; @@ -141,12 +163,12 @@ class IntlNumberFormatter { } this.floor = opts.floor; - this.intl = new Intl.NumberFormat(intl, intlOpts); + this.inf = getCachendINF(intl, intlOpts); } format(i) { const fixed = this.floor ? Math.floor(i) : i; - return this.intl.format(fixed); + return this.inf.format(fixed); } } @@ -184,11 +206,11 @@ class PolyDateFormatter { } if (this.hasIntl) { - const realIntlOpts = Object.assign({}, this.opts); + const intlOpts = Object.assign({}, this.opts); if (z) { - realIntlOpts.timeZone = z; + intlOpts.timeZone = z; } - this.dtf = new Intl.DateTimeFormat(intl, realIntlOpts); + this.dtf = getCachedDTF(intl, intlOpts); } } @@ -240,11 +262,14 @@ export default class Locale { localeR = specifiedLocale || (defaultToEN ? "en-US" : systemLocale()), numberingSystemR = numberingSystem || Settings.defaultNumberingSystem, outputCalendarR = outputCalendar || Settings.defaultOutputCalendar; + return new Locale(localeR, numberingSystemR, outputCalendarR, specifiedLocale); } static resetCache() { sysLocaleCache = null; + intlDTCache = {}; + intlNumCache = {}; } static fromObject({ locale, numberingSystem, outputCalendar } = {}) { @@ -276,7 +301,6 @@ export default class Locale { return this.fastNumbersCached; } - // todo: cache me listingMode(defaultOK = true) { const intl = hasIntl(), hasFTP = intl && hasFormatToParts(), @@ -358,9 +382,10 @@ export default class Locale { // for AM and PM. This is probably wrong, but it's makes parsing way easier. if (!this.meridiemCache) { const intl = { hour: "numeric", hour12: true }; - this.meridiemCache = [DateTime.utc(2016, 11, 13, 9), DateTime.utc(2016, 11, 13, 19)].map( - dt => this.extract(dt, intl, "dayperiod") - ); + this.meridiemCache = [ + DateTime.utc(2016, 11, 13, 9), + DateTime.utc(2016, 11, 13, 19) + ].map(dt => this.extract(dt, intl, "dayperiod")); } return this.meridiemCache;