@spotinst/spinnaker-deck
Version:
Spinnaker-Deck service, forked with support to Spotinst
101 lines (89 loc) • 3.54 kB
text/typescript
import { module } from 'angular';
import { formatDistanceToNow } from 'date-fns';
import { DateTime, Duration } from 'luxon';
import { react2angular } from 'react2angular';
import { SETTINGS } from 'core/config/settings';
import { SystemTimezone } from './SystemTimezone';
// Luxon supports up to 100 million days after epoch start
const MAX_VALID_INPUT = 8640000000000000;
const isInputValid = (input: any) => !(!input || isNaN(input) || input < 0 || input > MAX_VALID_INPUT);
export function duration(input: number) {
if (!isInputValid(input)) {
return '-';
}
// formatting does not support optionally omitting fields so we have to get
// a little weird with the format strings and the durations we send into them
const baseDuration = Duration.fromMillis(input);
const days = Math.floor(baseDuration.as('days'));
// remove any days - we will add them manually if needed
const thisDuration = baseDuration.minus({ days: Math.floor(baseDuration.as('days')) });
const format = thisDuration.days || Math.floor(thisDuration.as('hours')) ? 'hh:mm:ss' : 'mm:ss';
let dayLabel = '';
if (thisDuration.isValid) {
if (days > 0) {
dayLabel = days + 'd';
}
}
return thisDuration.isValid ? dayLabel + thisDuration.toFormat(format) : '-';
}
export function timeDiffToString(startTime: DateTime, endTime: DateTime) {
const duration = endTime.diff(startTime).shiftTo('days', 'hours', 'minutes', 'seconds');
const formatStrings = [];
let forcePush = false;
const addUnits = (unit: 'days' | 'hours' | 'minutes', unitFormat: string) => {
if (duration[unit] || forcePush) {
formatStrings.push(unitFormat);
forcePush = true;
}
};
addUnits('days', `d'd'`);
addUnits('hours', `h'h'`);
addUnits('minutes', `m'm'`);
formatStrings.push(`s's'`);
return duration.toFormat(formatStrings.join(' '));
}
export function timestamp(input: any) {
if (!isInputValid(input)) {
return '-';
}
const tz = SETTINGS.feature.displayTimestampsInUserLocalTime ? undefined : SETTINGS.defaultTimeZone;
const thisMoment = DateTime.fromMillis(parseInt(input, 10), { zone: tz });
return thisMoment.isValid ? thisMoment.toFormat('yyyy-MM-dd HH:mm:ss ZZZZ') : '-';
}
export function relativeTime(input?: number) {
if (!isInputValid(input)) {
return '-';
}
const now = Date.now();
const inFuture = input > now;
const thisMoment = DateTime.fromMillis(input);
const baseText = formatDistanceToNow(thisMoment.toJSDate(), { includeSeconds: true });
return thisMoment.isValid ? `${inFuture ? 'in ' : ''}${baseText}${inFuture ? '' : ' ago'}` : '-';
}
export function timePickerTime(input: any) {
if (input && !isNaN(input.hours) && !isNaN(input.minutes)) {
const hours = parseInt(input.hours, 10);
const minutes = parseInt(input.minutes, 10);
let result = '';
if (hours < 10) {
result += '0';
}
result += hours + ':';
if (minutes < 10) {
result += '0';
}
result += minutes;
return result;
}
return '-';
}
export const TIME_FORMATTERS = 'spinnaker.core.utils.timeFormatters';
module(TIME_FORMATTERS, [])
.filter('timestamp', () => timestamp)
.filter('relativeTime', () => relativeTime)
.filter('duration', () => duration)
.filter('timePickerTime', () => timePickerTime)
// Disable eslint react2angular-with-error-boundary rule
// Rule fixer would cause a circular package dependency between utils and presentation
// eslint-disable-next-line
.component('systemTimezone', react2angular(SystemTimezone));