Skip to content

Commit d2dde0a

Browse files
authored
formatDateTimeISO() fix (#2010)
* Fix for the DateTime ISO function * Fix implementation and added unit tests * Preserve milliseconds properly
1 parent aacec56 commit d2dde0a

File tree

2 files changed

+93
-1
lines changed

2 files changed

+93
-1
lines changed

apps/webapp/app/components/primitives/DateTime.tsx

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,45 @@ export function formatDateTime(
9898
}
9999

100100
export function formatDateTimeISO(date: Date, timeZone: string): string {
101-
return new Date(date.toLocaleString("en-US", { timeZone })).toISOString();
101+
// Special handling for UTC
102+
if (timeZone === "UTC") {
103+
return date.toISOString();
104+
}
105+
106+
// Get the date parts in the target timezone
107+
const dateFormatter = new Intl.DateTimeFormat("en-US", {
108+
timeZone,
109+
year: "numeric",
110+
month: "2-digit",
111+
day: "2-digit",
112+
hour: "2-digit",
113+
minute: "2-digit",
114+
second: "2-digit",
115+
hour12: false,
116+
});
117+
118+
// Get the timezone offset for this specific date
119+
const timeZoneFormatter = new Intl.DateTimeFormat("en-US", {
120+
timeZone,
121+
timeZoneName: "longOffset",
122+
});
123+
124+
const dateParts = Object.fromEntries(
125+
dateFormatter.formatToParts(date).map(({ type, value }) => [type, value])
126+
);
127+
128+
const timeZoneParts = timeZoneFormatter.formatToParts(date);
129+
const offset =
130+
timeZoneParts.find((part) => part.type === "timeZoneName")?.value.replace("GMT", "") ||
131+
"+00:00";
132+
133+
// Format: YYYY-MM-DDThh:mm:ss.sss±hh:mm
134+
return (
135+
`${dateParts.year}-${dateParts.month}-${dateParts.day}T` +
136+
`${dateParts.hour}:${dateParts.minute}:${dateParts.second}.${String(
137+
date.getMilliseconds()
138+
).padStart(3, "0")}${offset}`
139+
);
102140
}
103141

104142
// New component that only shows date when it changes
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { describe, it, expect } from "vitest";
2+
import { formatDateTimeISO } from "~/components/primitives/DateTime";
3+
4+
describe("formatDateTimeISO", () => {
5+
it("should format UTC dates with Z suffix", () => {
6+
const date = new Date("2025-04-29T14:01:19.000Z");
7+
const result = formatDateTimeISO(date, "UTC");
8+
expect(result).toBe("2025-04-29T14:01:19.000Z");
9+
});
10+
11+
describe("British Time (Europe/London)", () => {
12+
it("should format with +01:00 during BST (summer)", () => {
13+
// BST - British Summer Time (last Sunday in March to last Sunday in October)
14+
const summerDate = new Date("2025-07-15T14:01:19.000Z");
15+
const result = formatDateTimeISO(summerDate, "Europe/London");
16+
expect(result).toBe("2025-07-15T15:01:19.000+01:00");
17+
});
18+
19+
it("should format with +00:00 during GMT (winter)", () => {
20+
// GMT - Greenwich Mean Time (winter)
21+
const winterDate = new Date("2025-01-15T14:01:19.000Z");
22+
const result = formatDateTimeISO(winterDate, "Europe/London");
23+
expect(result).toBe("2025-01-15T14:01:19.000+00:00");
24+
});
25+
});
26+
27+
describe("US Pacific Time (America/Los_Angeles)", () => {
28+
it("should format with -07:00 during PDT (summer)", () => {
29+
// PDT - Pacific Daylight Time (second Sunday in March to first Sunday in November)
30+
const summerDate = new Date("2025-07-15T14:01:19.000Z");
31+
const result = formatDateTimeISO(summerDate, "America/Los_Angeles");
32+
expect(result).toBe("2025-07-15T07:01:19.000-07:00");
33+
});
34+
35+
it("should format with -08:00 during PST (winter)", () => {
36+
// PST - Pacific Standard Time (winter)
37+
const winterDate = new Date("2025-01-15T14:01:19.000Z");
38+
const result = formatDateTimeISO(winterDate, "America/Los_Angeles");
39+
expect(result).toBe("2025-01-15T06:01:19.000-08:00");
40+
});
41+
});
42+
43+
it("should preserve milliseconds", () => {
44+
const date = new Date("2025-04-29T14:01:19.123Z");
45+
const result = formatDateTimeISO(date, "UTC");
46+
expect(result).toBe("2025-04-29T14:01:19.123Z");
47+
});
48+
49+
it("should preserve milliseconds, not UTC", () => {
50+
const date = new Date("2025-04-29T14:01:19.123Z");
51+
const result = formatDateTimeISO(date, "Europe/London");
52+
expect(result).toBe("2025-04-29T15:01:19.123+01:00");
53+
});
54+
});

0 commit comments

Comments
 (0)