$ 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.
/** 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
- A
Dateobject is the wrong type for a duration —new Date(ms)is a point in time in 1970 and will misbehave past 24 hours. Keep durations as numbers. - Months and years are not fixed lengths; if you need them, compute from two actual dates, not from a millisecond count.
Intl.DurationFormatis the upcoming standard for this and already ships in some browsers; check support before using it.
Ad slot