Skip to content

Commit 5cf6fdf

Browse files
committed
Fix implementation and added unit tests
1 parent 5e58ccd commit 5cf6fdf

File tree

2 files changed

+65
-26
lines changed

2 files changed

+65
-26
lines changed

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

Lines changed: 17 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -103,24 +103,7 @@ export function formatDateTimeISO(date: Date, timeZone: string): string {
103103
return date.toISOString();
104104
}
105105

106-
// Get the offset in minutes for the specified timezone
107-
const formatter = new Intl.DateTimeFormat("en-US", {
108-
timeZone,
109-
timeZoneName: "shortOffset",
110-
});
111-
const tzOffset =
112-
formatter
113-
.formatToParts(date)
114-
.find((p) => p.type === "timeZoneName")
115-
?.value.replace("GMT", "") ?? "";
116-
117-
// Format the offset properly as ±HH:mm
118-
const offsetNum = parseInt(tzOffset);
119-
const offsetHours = Math.abs(Math.floor(offsetNum)).toString().padStart(2, "0");
120-
const sign = offsetNum >= 0 ? "+" : "-";
121-
const formattedOffset = `${sign}${offsetHours}:00`;
122-
123-
// Format the date parts
106+
// Get the date parts in the target timezone
124107
const dateFormatter = new Intl.DateTimeFormat("en-US", {
125108
timeZone,
126109
year: "numeric",
@@ -132,18 +115,26 @@ export function formatDateTimeISO(date: Date, timeZone: string): string {
132115
hour12: false,
133116
});
134117

135-
const parts = dateFormatter.formatToParts(date);
136-
const dateParts: Record<string, string> = {};
137-
parts.forEach((part) => {
138-
dateParts[part.type] = part.value;
118+
// Get the timezone offset for this specific date
119+
const timeZoneFormatter = new Intl.DateTimeFormat("en-US", {
120+
timeZone,
121+
timeZoneName: "longOffset",
139122
});
140123

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+
141133
// Format: YYYY-MM-DDThh:mm:ss.sss±hh:mm
142-
const isoString =
134+
return (
143135
`${dateParts.year}-${dateParts.month}-${dateParts.day}T` +
144-
`${dateParts.hour}:${dateParts.minute}:${dateParts.second}.000${formattedOffset}`;
145-
146-
return isoString;
136+
`${dateParts.hour}:${dateParts.minute}:${dateParts.second}.000${offset}`
137+
);
147138
}
148139

149140
// New component that only shows date when it changes
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
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+
});

0 commit comments

Comments
 (0)