Skip to content

Commit

Permalink
Perf: Cache ts offset guesses for quickDT (#1579)
Browse files Browse the repository at this point in the history
  • Loading branch information
schleyfox committed Mar 12, 2024
1 parent 73c4438 commit cafc4ee
Show file tree
Hide file tree
Showing 4 changed files with 259 additions and 108 deletions.
54 changes: 50 additions & 4 deletions src/datetime.js
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,36 @@ function normalizeUnitWithLocalWeeks(unit) {
}
}

// cache offsets for zones based on the current timestamp when this function is
// first called. When we are handling a datetime from components like (year,
// month, day, hour) in a time zone, we need a guess about what the timezone
// offset is so that we can convert into a UTC timestamp. One way is to find the
// offset of now in the zone. The actual date may have a different offset (for
// example, if we handle a date in June while we're in December in a zone that
// observes DST), but we can check and adjust that.
//
// When handling many dates, calculating the offset for now every time is
// expensive. It's just a guess, so we can cache the offset to use even if we
// are right on a time change boundary (we'll just correct in the other
// direction). Using a timestamp from first read is a slight optimization for
// handling dates close to the current date, since those dates will usually be
// in the same offset (we could set the timestamp statically, instead). We use a
// single timestamp for all zones to make things a bit more predictable.
//
// This is safe for quickDT (used by local() and utc()) because we don't fill in
// higher-order units from tsNow (as we do in fromObject, this requires that
// offset is calculated from tsNow).
function guessOffsetForZone(zone) {
if (!zoneOffsetGuessCache[zone]) {
if (zoneOffsetTs === undefined) {
zoneOffsetTs = Settings.now();
}

zoneOffsetGuessCache[zone] = zone.offset(zoneOffsetTs);
}
return zoneOffsetGuessCache[zone];
}

// this is a dumbed down version of fromObject() that runs about 60% faster
// but doesn't do any validation, makes a bunch of assumptions about what units
// are present, and so on.
Expand All @@ -379,8 +409,7 @@ function quickDT(obj, opts) {
return DateTime.invalid(unsupportedZone(zone));
}

const loc = Locale.fromObject(opts),
tsNow = Settings.now();
const loc = Locale.fromObject(opts);

let ts, o;

Expand All @@ -397,10 +426,10 @@ function quickDT(obj, opts) {
return DateTime.invalid(invalid);
}

const offsetProvis = zone.offset(tsNow);
const offsetProvis = guessOffsetForZone(zone);
[ts, o] = objToTS(obj, offsetProvis, zone);
} else {
ts = tsNow;
ts = Settings.now();
}

return new DateTime({ ts, zone, loc, o });
Expand Down Expand Up @@ -448,6 +477,18 @@ function lastOpts(argList) {
return [opts, args];
}

/**
* Timestamp to use for cached zone offset guesses (exposed for test)
*/
let zoneOffsetTs;
/**
* Cache for zone offset guesses (exposed for test).
*
* This optimizes quickDT via guessOffsetForZone to avoid repeated calls of
* zone.offset().
*/
let zoneOffsetGuessCache = {};

/**
* A DateTime is an immutable data structure representing a specific date and time and accompanying methods. It contains class and instance methods for creating, parsing, interrogating, transforming, and formatting them.
*
Expand Down Expand Up @@ -1000,6 +1041,11 @@ export default class DateTime {
return expanded.map((t) => t.val).join("");
}

static resetCache() {
zoneOffsetTs = undefined;
zoneOffsetGuessCache = {};
}

// INFO

/**
Expand Down
2 changes: 2 additions & 0 deletions src/settings.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import SystemZone from "./zones/systemZone.js";
import IANAZone from "./zones/IANAZone.js";
import Locale from "./impl/locale.js";
import DateTime from "./datetime.js";

import { normalizeZone } from "./impl/zoneUtil.js";
import { validateWeekSettings } from "./impl/util.js";
Expand Down Expand Up @@ -173,6 +174,7 @@ export default class Settings {
static resetCaches() {
Locale.resetCache();
IANAZone.resetCache();
DateTime.resetCache();
resetDigitRegexCache();
}
}

0 comments on commit cafc4ee

Please sign in to comment.