size: 1 KiB

const millisecondsPerSecond = 1000;
const secondsPerMinute = 60;
const minutesPerHour = 60;
const hoursPerDay = 24;
const daysPerWeek = 7;
const daysPerMonth = 31;
const daysPerYear = 365; // getting imprecise here, but no big deal
const intervals = {
  year:
    millisecondsPerSecond *
    secondsPerMinute *
    minutesPerHour *
    hoursPerDay *
    daysPerYear,
  month:
    millisecondsPerSecond *
    secondsPerMinute *
    minutesPerHour *
    hoursPerDay *
    daysPerMonth,
  week:
    millisecondsPerSecond *
    secondsPerMinute *
    minutesPerHour *
    hoursPerDay *
    daysPerWeek,
  day: millisecondsPerSecond * secondsPerMinute * minutesPerHour * hoursPerDay,
  hour: millisecondsPerSecond * secondsPerMinute * minutesPerHour,
  minute: millisecondsPerSecond * secondsPerMinute,
  second: millisecondsPerSecond,
};
const relativeDateFormat = new Intl.RelativeTimeFormat("en", { style: "long" });

// https://stackoverflow.com/a/78704662
function formatRelativeTime(isoStr) {
  const diff = new Date(isoStr) - new Date();
  for (const interval in intervals) {
    if (intervals[interval] <= Math.abs(diff)) {
      return relativeDateFormat.format(
        Math.trunc(diff / intervals[interval]),
        interval,
      );
    }
  }
  return relativeDateFormat.format(diff / 1000, "second");
}

document.addEventListener("DOMContentLoaded", function () {
  // Progressive enhancement: If javascript is enabled, certain <time> elements
  // will be rewritten from ISO datetime strings to human-readable relative text
  // (e.g. 2 days ago).
  document.querySelectorAll("time.relative").forEach((element) => {
    const timeStr = element.getAttribute("datetime");
    if (!timeStr) return;
    const relativeTimeStr = formatRelativeTime(timeStr);
    element.innerHTML = relativeTimeStr;
  });
});