Live-service games love recurring events that start at a fixed UTC time. Players, though, live in every time zone, and "the event starts at 18:00 UTC" is a common source of confusion. Here is a small, dependency-free way to show event times in the visitor's own zone.
1. Store the schedule in UTC
// Daily event windows, defined once in UTC const EVENT_WINDOWS_UTC = [ { start: "14:00", durationMin: 60 }, { start: "20:00", durationMin: 60 }, ]; 2. Build the next occurrence as a real Date
function nextOccurrence({ start, durationMin }, now = new Date()) { const [h, m] = start.split(":").map(Number); const d = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), h, m)); if (d.getTime() + durationMin * 60000 < now.getTime()) d.setUTCDate(d.getUTCDate() + 1); return d; } 3. Format it with Intl, which knows the user's zone
const fmt = new Intl.DateTimeFormat(undefined, { weekday: "short", hour: "2-digit", minute: "2-digit", timeZoneName: "short", }); for (const w of EVENT_WINDOWS_UTC) { console.log(fmt.format(nextOccurrence(w))); } Intl.DateTimeFormat().resolvedOptions().timeZone returns the IANA zone (for example Asia/Shanghai), so you can also let users pick another zone and pass timeZone explicitly.
4. Add a countdown
function countdown(target) { const s = Math.max(0, Math.floor((target - Date.now()) / 1000)); const hh = String(Math.floor(s / 3600)).padStart(2, "0"); const mm = String(Math.floor((s % 3600) / 60)).padStart(2, "0"); return `${hh}:${mm}`; } Gotchas
- Daylight saving time: keep the source of truth in UTC and convert at display time, never the other way round.
- Don't parse "14:00" with
new Date("14:00"); browsers disagree. - Cache nothing that depends on the user's zone on the server.
A real-world example
If you want to see this pattern in a finished page, FN Power Hour is a free fan-made planner that shows Fortnite Treasure Hours in your own time zone; it also has a chest tracker.
Happy building!
