@travetto/runtime
Version:
Runtime for travetto applications.
111 lines (101 loc) • 3.46 kB
text/typescript
const MIN = 1000 * 60;
const DAY = 24 * MIN * 60;
const TIME_UNITS = {
y: DAY * 365,
M: DAY * 30,
w: DAY * 7,
d: DAY,
h: MIN * 60,
m: MIN,
s: 1000,
ms: 1
};
export type TimeSpan = `${number}${keyof typeof TIME_UNITS}`;
export type TimeUnit = keyof typeof TIME_UNITS;
const TIME_PATTERN = new RegExp(`^(?<amount>-?[0-9.]+)(?<unit>${Object.keys(TIME_UNITS).join('|')})$`);
const TIME_LIKE_STRING = /\d{1,30}[a-z]$/i;
export class TimeUtil {
/**
* Test to see if a string is valid for relative time
* @param val
*/
static isTimeSpan(value: string): value is TimeSpan {
return TIME_PATTERN.test(value);
}
/**
* Returns time units convert to ms
* @param amount Number of units to extend
* @param unit Time unit to extend ('ms', 's', 'm', 'h', 'd', 'w', 'y')
*/
static asMillis(amount: Date | number | TimeSpan, unit?: TimeUnit): number {
if (amount instanceof Date) {
return amount.getTime();
} else if (typeof amount === 'string') {
const groups: { amount?: string, unit?: TimeUnit } = amount.match(TIME_PATTERN)?.groups ?? {};
const amountString = groups.amount ?? `${amount}`;
unit = groups.unit ?? unit ?? 'ms';
if (!TIME_UNITS[unit]) {
return NaN;
}
amount = amountString.includes('.') ? parseFloat(amountString) : parseInt(amountString, 10);
}
return amount * TIME_UNITS[unit ?? 'ms'];
}
/**
* Returns the time converted to seconds
* @param date The date to convert
*/
static asSeconds(date: Date | number | TimeSpan, unit?: TimeUnit): number {
return Math.trunc(this.asMillis(date, unit) / 1000);
}
/**
* Returns the time converted to a Date
* @param date The date to convert
*/
static asDate(date: Date | number | TimeSpan, unit?: TimeUnit): Date {
return new Date(this.asMillis(date, unit));
}
/**
* Resolve time or span to possible time
*/
static fromValue(value: Date | number | string | undefined): number | undefined {
switch (typeof value) {
case 'number': return Number.isNaN(value) ? undefined : value;
case 'object': return value.getTime();
case 'undefined': return undefined;
case 'string': {
if (TIME_LIKE_STRING.test(value)) { // Looks like span
return this.isTimeSpan(value) ? this.asMillis(value) : undefined;
} else {
const parsed = parseInt(value, 10);
return Number.isNaN(parsed) ? undefined : parsed;
}
}
}
}
/**
* Returns a new date with `amount` units into the future
* @param amount Number of units to extend
* @param unit Time unit to extend ('ms', 's', 'm', 'h', 'd', 'w', 'y')
*/
static fromNow(amount: number | TimeSpan, unit: TimeUnit = 'ms'): Date {
return new Date(Date.now() + this.asMillis(amount, unit));
}
/**
* Returns a pretty timestamp
* @param time Time in milliseconds
*/
static asClock(time: number): string {
const rawSeconds = Math.trunc(time / 1000);
const seconds = rawSeconds % 60;
const minutes = Math.trunc(rawSeconds / 60) % 60;
const hours = Math.trunc(rawSeconds / 3600);
if (hours) {
return `${hours.toString().padStart(2, '0')}h ${minutes.toString().padStart(2, '0')}m`;
} else if (minutes) {
return `${minutes.toString().padStart(2, '0')}m ${seconds.toString().padStart(2, '0')}s`;
} else {
return `${seconds.toString().padStart(2, '0')}s`;
}
}
}