$ snippet format-duration

Format a duration from milliseconds

90061000 → '1d 1h 1m 1s', plus a clock-style '01:30:05' and a rounded '2 hours' — without a date library.

JavaScript
/** 90061000 → "1d 1h 1m 1s"; drops leading zero units; `parts` caps how many to show. */
function formatDuration(ms, { parts = 4, short = true } = {}) {
  if (!Number.isFinite(ms)) return "—";
  ms = Math.abs(Math.round(ms));
  const units = [["day", 86400000], ["hour", 3600000], ["minute", 60000], ["second", 1000]];
  const out = [];
  for (const [name, size] of units) {
    const v = Math.floor(ms / size);
    if (v || out.length) { out.push(short ? v + name[0] : `${v} ${name}${v === 1 ? "" : "s"}`); ms -= v * size; }
    if (out.length === parts) break;
  }
  return out.length ? out.join(" ") : (short ? "0s" : "0 seconds");
}

/** 5405000 → "01:30:05"; hours are omitted when zero unless `alwaysHours`. */
function formatClock(ms, { alwaysHours = false } = {}) {
  const total = Math.floor(Math.abs(ms) / 1000);
  const h = Math.floor(total / 3600), m = Math.floor((total % 3600) / 60), s = total % 60;
  const p = (n) => String(n).padStart(2, "0");
  return (h || alwaysHours ? p(h) + ":" : "") + p(m) + ":" + p(s);
}

/** Rounded to the largest unit: 7200000 → "2 hours"; localized. */
function formatApprox(ms, locale = "en") {
  const units = [["day", 86400000], ["hour", 3600000], ["minute", 60000], ["second", 1000]];
  const [unit, size] = units.find(([, size]) => Math.abs(ms) >= size) ?? units[3];
  return new Intl.NumberFormat(locale, { style: "unit", unit, unitDisplay: "long", maximumFractionDigits: 0 })
    .format(ms / size);
}

How it works

Three formats for three jobs: the compact 1d 2h for logs and dashboards, the mm:ss clock for players and timers, and a single rounded unit for human copy ("about 2 hours"). All of them work from milliseconds so you can pass end - start directly.

Gotchas

Try it
Result
Ad slot