const addZero = (num: number, digits: number) => String(num).padStart(digits, "0");
interface DurationObject { d: number; h: number; m: number; s: number; ms: number; us: number; ns: number;}
const keyList: Record<keyof DurationObject, string> = { d: "days", h: "hours", m: "minutes", s: "seconds", ms: "milliseconds", us: "microseconds", ns: "nanoseconds",};
function millisecondsToDurationObject(ms: number): DurationObject { const absolute_ms = Math.abs(ms); return { d: Math.trunc(absolute_ms / 86400000), h: Math.trunc(absolute_ms / 3600000) % 24, m: Math.trunc(absolute_ms / 60000) % 60, s: Math.trunc(absolute_ms / 1000) % 60, ms: Math.trunc(absolute_ms) % 1000, us: Math.trunc(absolute_ms * 1000) % 1000, ns: Math.trunc(absolute_ms * 1000000) % 1000, };}
function durationArray( duration: DurationObject,): { type: keyof DurationObject; value: number }[] { return [ { type: "d", value: duration.d }, { type: "h", value: duration.h }, { type: "m", value: duration.m }, { type: "s", value: duration.s }, { type: "ms", value: duration.ms }, { type: "us", value: duration.us }, { type: "ns", value: duration.ns }, ];}
export interface PrettyDurationOptions { style: "narrow" | "digital" | "full"; ignoreZero: boolean;}
export function format( ms: number, options: Partial<PrettyDurationOptions> = {},): string { const opt = Object.assign( { style: "narrow", ignoreZero: false }, options, ); const duration = millisecondsToDurationObject(ms); const durationArr = durationArray(duration); switch (opt.style) { case "narrow": { if (opt.ignoreZero) { return `${ durationArr.filter((x) => x.value).map((x) => `${x.value}${x.type === "us" ? "µs" : x.type}` ) .join(" ") }`; } return `${ durationArr.map((x) => `${x.value}${x.type === "us" ? "µs" : x.type}`) .join(" ") }`; } case "full": { if (opt.ignoreZero) { return `${ durationArr.filter((x) => x.value).map((x) => `${x.value} ${keyList[x.type]}` ).join(", ") }`; } return `${ durationArr.map((x) => `${x.value} ${keyList[x.type]}`).join(", ") }`; } case "digital": { const arr = durationArr.map((x) => ["ms", "us", "ns"].includes(x.type) ? addZero(x.value, 3) : addZero(x.value, 2) ); if (opt.ignoreZero) { let cont = true; while (cont) { if (!Number(arr[arr.length - 1])) arr.pop(); else cont = false; } } return arr.join(":"); } default: { throw new TypeError(`style must be "narrow", "full", or "digital"!`); } }}