export class TimeUnit { public constructor( public readonly milliseconds: number, public readonly longName: string, public readonly shortName: string, ) { } } export default class Time { public static readonly UNITS = { MILLISECOND: { milliseconds: 1, longName: 'millisecond', shortName: 'ms', }, SECOND: { milliseconds: 1000, longName: 'second', shortName: 's', }, MINUTE: { milliseconds: 60 * 1000, longName: 'minute', shortName: 'm', }, HOUR: { milliseconds: 60 * 60 * 1000, longName: 'hour', shortName: 'h', }, DAY: { milliseconds: 24 * 60 * 60 * 1000, longName: 'day', shortName: 'd', }, MONTH: { milliseconds: 30 * 24 * 60 * 60 * 1000, longName: 'month', shortName: 'M', }, YEAR: { milliseconds: 365 * 24 * 60 * 60 * 1000, longName: 'year', shortName: 'y', }, }; public static humanizeTimeSince( since: Date, short?: boolean, skipOneUnitNumber?: boolean, units?: TimeUnit[], ): string { return this.humanizeDuration(Date.now() - since.getTime(), short, skipOneUnitNumber, units); } public static humanizeTimeTo( to: Date, short?: boolean, skipOneUnitNumber?: boolean, units?: TimeUnit[], ): string { return this.humanizeDuration(to.getTime() - Date.now(), short, skipOneUnitNumber, units); } public static humanizeDuration( duration: number, short: boolean = false, skipOneUnitNumber: boolean = false, units: TimeUnit[] = [ this.UNITS.SECOND, this.UNITS.MINUTE, this.UNITS.HOUR, this.UNITS.DAY, this.UNITS.MONTH, this.UNITS.YEAR, ], ): string { for (let i = units.length - 1; i > 0; i--) { if (duration >= units[i - 1].milliseconds && duration < units[i].milliseconds) { const amount = Math.floor(duration / units[i - 1].milliseconds); const unit = short ? units[i - 1].shortName : ' ' + units[i - 1].longName + (amount > 1 ? 's' : ''); return (amount > 1 || !skipOneUnitNumber ? amount : '') + unit; } } return 'now'; } }