const zoneSelect = document.getElementById("zoneSelect"); const zoneLabel = document.getElementById("zoneLabel"); const offsetLabel = document.getElementById("offsetLabel"); const utcLabel = document.getElementById("utcLabel"); const dateLine = document.getElementById("dateLine"); const digital = document.getElementById("digital"); const zones = [ "America/New_York", "America/Chicago", "America/Denver", "America/Los_Angeles", "America/Anchorage", "Pacific/Honolulu", "UTC" ]; let serverOffsetMs = 0; let selectedZone = Intl.DateTimeFormat().resolvedOptions().timeZone || "America/New_York"; if (!zones.includes(selectedZone)) { selectedZone = "America/New_York"; } for (const zone of zones) { const option = document.createElement("option"); option.value = zone; option.textContent = zone; if (zone === selectedZone) { option.selected = true; } zoneSelect.appendChild(option); } zoneSelect.addEventListener("change", () => { selectedZone = zoneSelect.value; zoneLabel.textContent = selectedZone; }); async function syncWithServer() { const start = Date.now(); try { const response = await fetch(`/api/time?ts=${start}`, { cache: "no-store" }); const end = Date.now(); const data = await response.json(); const rtt = end - start; const estimatedServerAtEnd = data.epoch_ms + rtt / 2; serverOffsetMs = estimatedServerAtEnd - end; offsetLabel.textContent = `Synchronized (${serverOffsetMs >= 0 ? "+" : ""}${serverOffsetMs.toFixed(0)} ms, RTT ${rtt} ms)`; } catch (_error) { offsetLabel.textContent = "Sync failed"; } } function formatParts(date, zone) { const formatter = new Intl.DateTimeFormat("en-US", { timeZone: zone, hour12: false, year: "numeric", month: "long", day: "2-digit", weekday: "long", hour: "2-digit", minute: "2-digit", second: "2-digit" }); return formatter.formatToParts(date).reduce((accumulator, part) => { if (part.type !== "literal") { accumulator[part.type] = part.value; } return accumulator; }, {}); } function render() { const now = new Date(Date.now() + serverOffsetMs); const parts = formatParts(now, selectedZone); const centiseconds = String(Math.floor(now.getMilliseconds() / 10)).padStart(2, "0"); dateLine.textContent = `${parts.weekday}, ${parts.month} ${parts.day}, ${parts.year}`; digital.textContent = `${parts.hour}:${parts.minute}:${parts.second}.${centiseconds}`; zoneLabel.textContent = selectedZone; utcLabel.textContent = now.toISOString().replace("T", " ").replace("Z", " UTC"); requestAnimationFrame(render); } syncWithServer(); setInterval(syncWithServer, 20000); render();