Moments. The server writes every time in UTC (aldebaran_time::moment): it knows neither this clock
nor this timezone. Here a time[data-ago] is rewritten as how long ago or how soon, with the date and
time where the reader is as its tooltip; an element with data-when keeps its text and gets only the
tooltip. Parts of the page are replaced as they change, so new moments are found as they arrive, and
all are brought up to date each minute.
6(() => { 7 const local = new Intl.DateTimeFormat(undefined, { 8 year: "numeric", month: "short", day: "numeric", hour: "numeric", minute: "2-digit", timeZoneName: "short", 9 }); 10 const relative = new Intl.RelativeTimeFormat(undefined, { numeric: "always" }); 11 const UNITS = [["year", 31536000], ["month", 2592000], ["day", 86400], ["hour", 3600], ["minute", 60]]; 12 function distance(seconds) { 13 const size = Math.abs(seconds); 14 if (size < 60) return seconds < 0 ? "just now" : "in under a minute"; 15 const [unit, length] = UNITS.find(([, length]) => size >= length); 16 return relative.format(Math.trunc(seconds / length), unit); 17 } 18 function set(element, name, value) { 19 if (element[name] !== value) element[name] = value; 20 } 21 function refresh() { 22 const now = Date.now(); 23 for (const time of document.querySelectorAll("time[data-ago][datetime]")) { 24 const at = Date.parse(time.dateTime); 25 if (Number.isNaN(at)) continue; 26 set(time, "textContent", distance((at - now) / 1000)); 27 set(time, "title", local.format(at)); 28 } 29 for (const element of document.querySelectorAll("[data-when]")) { 30 const at = Date.parse(element.dataset.when); 31 if (!Number.isNaN(at)) set(element, "title", local.format(at)); 32 } 33 } 34 refresh(); 35 // Writing a moment's own text is a change too; `set` writes nothing that is already so, which is what 36 // ends it. 37 let due = false; 38 new MutationObserver(() => { 39 if (due) return; 40 due = true; 41 requestAnimationFrame(() => { due = false; refresh(); }); 42 }).observe(document.body, { childList: true, subtree: true }); 43 setInterval(refresh, 60000); 44})();