// Synthetic example for https://www.hirubix.com/blogs/booking-time-zones-daylight-saving // Save as booking-time-zone-check.mjs; run with Node.js 20+: node booking-time-zone-check.mjs import assert from "node:assert/strict"; const zone = "America/Denver"; const formatter = new Intl.DateTimeFormat("en-GB", { timeZone: zone, year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", hourCycle: "h23", timeZoneName: "longOffset", }); function localLabel(instant) { const parts = Object.fromEntries( formatter.formatToParts(new Date(instant)).map(({ type, value }) => [type, value]), ); return `${parts.year}-${parts.month}-${parts.day} ${parts.hour}:${parts.minute} ${parts.timeZoneName}`; } const rows = [ ["First 01:30", "2026-11-01T07:30:00Z", "2026-11-01 01:30 GMT-06:00"], ["Second 01:30", "2026-11-01T08:30:00Z", "2026-11-01 01:30 GMT-07:00"], ["Before spring jump", "2026-03-08T08:59:00Z", "2026-03-08 01:59 GMT-07:00"], ["After spring jump", "2026-03-08T09:00:00Z", "2026-03-08 03:00 GMT-06:00"], ["Sunday at 09:00", "2026-10-25T15:00:00Z", "2026-10-25 09:00 GMT-06:00"], ["Add 168 elapsed hours", "2026-11-01T15:00:00Z", "2026-11-01 08:00 GMT-07:00"], ["Next Sunday at 09:00", "2026-11-01T16:00:00Z", "2026-11-01 09:00 GMT-07:00"], ]; for (const [label, utc, expected] of rows) { assert.equal(localLabel(utc), expected, label); } const elapsedHours = (start, end) => (Date.parse(end) - Date.parse(start)) / 3_600_000; assert.equal(elapsedHours(rows[0][1], rows[1][1]), 1); assert.equal(elapsedHours(rows[4][1], rows[5][1]), 168); assert.equal(elapsedHours(rows[4][1], rows[6][1]), 169); console.log(JSON.stringify({ synthetic: true, runtime: { node: process.version, icu: process.versions.icu, tz: process.versions.tz }, zone, checksPassed: 10, rows: rows.map(([label, utc]) => ({ label, utc, local: localLabel(utc) })), }, null, 2));