Skip to content

Commit 20139a3

Browse files
committed
Fix broken caching in guessOffsetForZone
1 parent aedc7bd commit 20139a3

File tree

2 files changed

+39
-7
lines changed

2 files changed

+39
-7
lines changed

src/datetime.js

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -389,15 +389,25 @@ function normalizeUnitWithLocalWeeks(unit) {
389389
// This is safe for quickDT (used by local() and utc()) because we don't fill in
390390
// higher-order units from tsNow (as we do in fromObject, this requires that
391391
// offset is calculated from tsNow).
392+
/**
393+
* @param {Zone} zone
394+
* @return {number}
395+
*/
392396
function guessOffsetForZone(zone) {
393-
if (!zoneOffsetGuessCache[zone]) {
394-
if (zoneOffsetTs === undefined) {
395-
zoneOffsetTs = Settings.now();
396-
}
397+
if (zoneOffsetTs === undefined) {
398+
zoneOffsetTs = Settings.now();
399+
}
397400

398-
zoneOffsetGuessCache[zone] = zone.offset(zoneOffsetTs);
401+
// Do not cache anything but IANA zones, because it is not safe to do so.
402+
// Guessing an offset which is not present in the zone can cause wrong results from fixOffset
403+
if (zone.type !== "iana") {
404+
return zone.offset(zoneOffsetTs);
405+
}
406+
const zoneName = zone.name;
407+
if (!zoneOffsetGuessCache[zoneName]) {
408+
zoneOffsetGuessCache[zoneName] = zone.offset(zoneOffsetTs);
399409
}
400-
return zoneOffsetGuessCache[zone];
410+
return zoneOffsetGuessCache[zoneName];
401411
}
402412

403413
// this is a dumbed down version of fromObject() that runs about 60% faster

test/datetime/create.test.js

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* global test expect */
22

3-
import { DateTime } from "../../src/luxon";
3+
import { DateTime, Settings } from "../../src/luxon";
44
import Helpers from "../helpers";
55

66
const withDefaultLocale = Helpers.withDefaultLocale,
@@ -897,3 +897,25 @@ test("private language subtags don't break unicode subtags", () => {
897897
expect(res.outputCalendar).toBe("islamic");
898898
expect(res.numberingSystem).toBe("thai");
899899
});
900+
901+
test("DateTime.local works even after time zone change", () => {
902+
// This test catches errors produced when guessOffsetForZone produces wildy wrong guesses
903+
// This guards against a regression by broken caching in that method
904+
Settings.resetCaches();
905+
withDefaultZone("America/Los_Angeles", () => {
906+
expect(DateTime.local(2024).year).toBe(2024);
907+
});
908+
withDefaultZone("America/Chicago", () => {
909+
const dateTime = DateTime.local(2024, 11, 3, 0, 5, 0);
910+
expect(dateTime.zoneName).toBe("America/Chicago");
911+
expect(dateTime.toObject()).toEqual({
912+
year: 2024,
913+
month: 11,
914+
day: 3,
915+
hour: 0,
916+
minute: 5,
917+
second: 0,
918+
millisecond: 0,
919+
});
920+
});
921+
});

0 commit comments

Comments
 (0)