UNPKG

@samual/duration

Version:

Normalize and format durations of time.

88 lines (87 loc) 3.86 kB
import { DurationError } from "./DurationError.js" class FormatDurationError extends DurationError {} Object.defineProperty(FormatDurationError.prototype, "name", { value: "FormatDurationError" }) class FormatEmptyDurationError extends FormatDurationError {} Object.defineProperty(FormatEmptyDurationError.prototype, "name", { value: "FormatEmptyDurationError" }) class FormatNonIntegerDurationError extends FormatDurationError {} Object.defineProperty(FormatEmptyDurationError.prototype, "name", { value: "FormatEmptyDurationError" }) class FormatDurationOptionError extends FormatDurationError {} Object.defineProperty(FormatDurationOptionError.prototype, "name", { value: "FormatDurationOptionError" }) const formatDuration = (duration, options = {}) => { if (null != options.maxEntries) { if (options.maxEntries < 1) throw new FormatDurationOptionError("maxEntries must be at least 1") if (!Number.isInteger(options.maxEntries)) throw new FormatDurationOptionError("maxEntries must be an integer") } const nonIntegerEntry = Object.entries(duration).find(([, value]) => value && !Number.isInteger(value)) if (nonIntegerEntry) throw new FormatNonIntegerDurationError( `Given number must be an integer, got ${nonIntegerEntry[0]}: ${nonIntegerEntry[1]}` ) const yearUnitNameSingular = options.yearUnitNameSingular ?? "year", yearUnitNamePlural = options.yearUnitNamePlural ?? "years", dayUnitNameSingular = options.dayUnitNameSingular ?? "day", dayUnitNamePlural = options.dayUnitNamePlural ?? "days", hourUnitNameSingular = options.hourUnitNameSingular ?? "hour", hourUnitNamePlural = options.hourUnitNamePlural ?? "hours", minuteUnitNameSingular = options.minuteUnitNameSingular ?? "minute", minuteUnitNamePlural = options.minuteUnitNamePlural ?? "minutes", secondUnitNameSingular = options.secondUnitNameSingular ?? "second", secondUnitNamePlural = options.secondUnitNamePlural ?? "seconds", millisecondUnitNameSingular = options.millisecondUnitNameSingular ?? "millisecond", millisecondUnitNamePlural = options.millisecondUnitNamePlural ?? "milliseconds", entries = [ null != duration.years && { value: duration.years, unit: 1 == duration.years ? yearUnitNameSingular : yearUnitNamePlural }, null != duration.days && { value: duration.days, unit: 1 == duration.days ? dayUnitNameSingular : dayUnitNamePlural }, null != duration.hours && { value: duration.hours, unit: 1 == duration.hours ? hourUnitNameSingular : hourUnitNamePlural }, null != duration.minutes && { value: duration.minutes, unit: 1 == duration.minutes ? minuteUnitNameSingular : minuteUnitNamePlural }, null != duration.seconds && { value: duration.seconds, unit: 1 == duration.seconds ? secondUnitNameSingular : secondUnitNamePlural }, null != duration.milliseconds && { value: duration.milliseconds, unit: 1 == duration.milliseconds ? millisecondUnitNameSingular : millisecondUnitNamePlural } ].filter(Boolean) if (!entries.length) throw new FormatEmptyDurationError("Cannot format empty duration") if (options.hideZero) { const nonZeros = "leading" == options.hideZero ? entries.slice( Math.max( 0, entries.findIndex(item => item.value) ) ) : entries.filter(item => item.value) return nonZeros.length ? nonZeros .slice(0, options.maxEntries) .map(item => `${item.value}${options.noSpaceBeforeUnit ? "" : " "}${item.unit}`) .join(options.separator ?? ", ") : "0 " + entries.at(-1).unit } return entries .slice(0, options.maxEntries) .map(item => `${item.value}${options.noSpaceBeforeUnit ? "" : " "}${item.unit}`) .join(options.separator ?? ", ") } export { FormatDurationError, FormatDurationOptionError, FormatEmptyDurationError, FormatNonIntegerDurationError, formatDuration }