Skip to content

Commit e37d5f0

Browse files
committed
Route Day.js operations through injected factory
1 parent a5d12be commit e37d5f0

3 files changed

Lines changed: 84 additions & 5 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@salt-ds/date-adapters": patch
3+
---
4+
5+
Fixed the Day.js adapter to consistently use an injected Day.js factory for timezone resolution, UTC/timezone date creation, `today`, and `now`.

packages/date-adapters/src/__tests__/dayjs.spec.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import dayjs from "dayjs";
2+
import "dayjs/locale/fr";
23
import customParseFormat from "dayjs/plugin/customParseFormat";
34
import timezone from "dayjs/plugin/timezone";
45
import utc from "dayjs/plugin/utc";
@@ -11,9 +12,78 @@ dayjs.extend(utc);
1112
dayjs.extend(timezone);
1213
dayjs.extend(customParseFormat);
1314

15+
const createInjectedDayjs = () => {
16+
const fixedNow = "2030-06-15T12:34:56.000Z";
17+
const systemTimezone = "Asia/Tokyo";
18+
const injectedTimezone = ((
19+
value?: Parameters<typeof dayjs.tz>[0],
20+
timezone?: string,
21+
) => dayjs.tz(value ?? fixedNow, timezone)) as typeof dayjs.tz;
22+
injectedTimezone.guess = () => systemTimezone;
23+
injectedTimezone.setDefault = dayjs.tz.setDefault;
24+
const injectedUtc = ((...args: Parameters<typeof dayjs.utc>) =>
25+
args.length === 0
26+
? dayjs.utc(fixedNow)
27+
: dayjs.utc(...args)) as typeof dayjs.utc;
28+
const factory = Object.assign(
29+
(...args: Parameters<typeof dayjs>) =>
30+
args.length === 0 ? dayjs(fixedNow) : dayjs(...args),
31+
{ tz: injectedTimezone, utc: injectedUtc },
32+
);
33+
34+
return { factory, fixedNow, systemTimezone };
35+
};
36+
1437
describe("GIVEN a AdapterDayjs", () => {
1538
const adapter = new AdapterDayjs({ locale: "en" });
1639

40+
describe("GIVEN an injected Day.js factory", () => {
41+
const { factory, fixedNow, systemTimezone } = createInjectedDayjs();
42+
const injectedAdapter = new AdapterDayjs({ locale: "fr" }, factory);
43+
44+
it.each([
45+
"default",
46+
"system",
47+
] as const)("SHOULD use its timezone configuration for %s dates", (timezone) => {
48+
const date = injectedAdapter.date("2025-01-05T00:00:00", timezone);
49+
50+
expect(injectedAdapter.getTimezone(date)).toBe(systemTimezone);
51+
expect(date.format("Z")).toBe("+09:00");
52+
});
53+
54+
it("SHOULD use its UTC factory", () => {
55+
const date = injectedAdapter.date("2025-01-05T00:00:00", "UTC");
56+
57+
expect(date.utcOffset()).toBe(0);
58+
});
59+
60+
it("SHOULD use its timezone factory for an explicit IANA timezone", () => {
61+
const date = injectedAdapter.date(
62+
"2025-01-05T00:00:00",
63+
"America/New_York",
64+
);
65+
66+
expect(injectedAdapter.getTimezone(date)).toBe("America/New_York");
67+
expect(date.format("Z")).toBe("-05:00");
68+
});
69+
70+
it("SHOULD apply the configured locale", () => {
71+
const date = injectedAdapter.date("2025-01-05T00:00:00", "UTC");
72+
73+
expect(injectedAdapter.format(date, "MMMM")).toBe("janvier");
74+
});
75+
76+
it("SHOULD use its clock for today", () => {
77+
expect(injectedAdapter.today("UTC").format("YYYY-MM-DD HH:mm:ss")).toBe(
78+
"2030-06-15 00:00:00",
79+
);
80+
});
81+
82+
it("SHOULD use its clock for now", () => {
83+
expect(injectedAdapter.now("UTC").toISOString()).toBe(fixedNow);
84+
});
85+
});
86+
1787
it("SHOULD create a Dayjs date in the system timezone", () => {
1888
const date = adapter.date("2023-11-01", "system");
1989
expect(date.isValid()).toBe(true);

packages/date-adapters/src/dayjs-adapter/index.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@ import {
1616

1717
type Constructor = {
1818
(...args: Parameters<typeof defaultDayjs>): Dayjs;
19-
tz?: (value: Parameters<typeof defaultDayjs>[0], timezone: string) => Dayjs;
19+
tz?: {
20+
(value: Parameters<typeof defaultDayjs>[0], timezone: string): Dayjs;
21+
guess?: () => string;
22+
setDefault?: (timezone?: string) => void;
23+
};
2024
utc?: (value?: Parameters<typeof defaultDayjs>[0]) => Dayjs;
2125
};
2226

@@ -88,7 +92,7 @@ export class AdapterDayjs implements SaltDateAdapter<Dayjs, string> {
8892
return undefined;
8993
}
9094
if (timezone === "system") {
91-
return defaultDayjs.tz.guess();
95+
return this.dayjs.tz?.guess?.() ?? defaultDayjs.tz.guess();
9296
}
9397
return timezone;
9498
};
@@ -102,11 +106,11 @@ export class AdapterDayjs implements SaltDateAdapter<Dayjs, string> {
102106
if (!this.dayjs.tz) {
103107
throw new Error("Salt Day.js adapter: missing timezone plugin");
104108
}
105-
const timezone = defaultDayjs.tz.guess();
109+
const timezone = this.dayjs.tz.guess?.() ?? defaultDayjs.tz.guess();
106110
if (timezone !== "UTC") {
107-
return defaultDayjs.tz(value, timezone);
111+
return this.dayjs.tz(value, timezone);
108112
}
109-
return defaultDayjs(value);
113+
return this.dayjs(value);
110114
};
111115

112116
/**

0 commit comments

Comments
 (0)