open-nomad/ui/app/utils/format-duration.js
2022-01-20 09:46:45 -05:00

96 lines
3.7 KiB
JavaScript

import moment from 'moment';
import { pluralize } from 'ember-inflector';
/**
* Metadata for all unit types
* name: identifier for the unit. Also maps to moment methods when applicable
* suffix: the preferred suffix for a unit
* inMoment: whether or not moment can be used to compute this unit value
* pluralizable: whether or not this suffix can be pluralized
* longSuffix: the suffix to use instead of suffix when longForm is true
*/
const allUnits = [
{ name: 'years', suffix: 'year', inMoment: true, pluralizable: true },
{ name: 'months', suffix: 'month', inMoment: true, pluralizable: true },
{ name: 'days', suffix: 'day', inMoment: true, pluralizable: true },
{ name: 'hours', suffix: 'h', longSuffix: 'hour', inMoment: true, pluralizable: false },
{ name: 'minutes', suffix: 'm', longSuffix: 'minute', inMoment: true, pluralizable: false },
{ name: 'seconds', suffix: 's', longSuffix: 'second', inMoment: true, pluralizable: false },
{ name: 'milliseconds', suffix: 'ms', inMoment: true, pluralizable: false },
{ name: 'microseconds', suffix: 'µs', inMoment: false, pluralizable: false },
{ name: 'nanoseconds', suffix: 'ns', inMoment: false, pluralizable: false },
];
const pluralizeUnits = (amount, unit, longForm) => {
let suffix;
if (longForm && unit.longSuffix) {
// Long form means always using full words (seconds insteand of s) which means
// pluralization is necessary.
suffix = amount === 1 ? unit.longSuffix : pluralize(unit.longSuffix);
} else {
// In the normal case, only pluralize based on the pluralizable flag
suffix = amount === 1 || !unit.pluralizable ? unit.suffix : pluralize(unit.suffix);
}
// A space should go between the value and the unit when the unit is a full word
// 300ns vs. 1 hour
const addSpace = unit.pluralizable || (longForm && unit.longSuffix);
return `${amount}${addSpace ? ' ' : ''}${suffix}`;
};
/**
* Format a Duration at a preferred precision
*
* @param {Number} duration The duration to format
* @param {String} units The units for the duration. Default to nanoseconds.
* @param {Boolean} longForm Whether or not to expand single character suffixes,
* used to ensure screen readers correctly read units.
*/
export default function formatDuration(duration = 0, units = 'ns', longForm = false) {
const durationParts = {};
// Moment only handles up to millisecond precision.
// Microseconds and nanoseconds need to be handled first,
// then Moment can take over for all larger units.
if (units === 'ns') {
durationParts.nanoseconds = duration % 1000;
durationParts.microseconds = Math.floor((duration % 1000000) / 1000);
duration = Math.floor(duration / 1000000);
} else if (units === 'mms') {
durationParts.microseconds = duration % 1000;
duration = Math.floor(duration / 1000);
}
let momentUnits = units;
if (units === 'ns' || units === 'mms') {
momentUnits = 'ms';
}
const momentDuration = moment.duration(duration, momentUnits);
// Get the count of each time unit that Moment handles
allUnits
.filterBy('inMoment')
.mapBy('name')
.forEach((unit) => {
durationParts[unit] = momentDuration[unit]();
});
// Format each time time bucket as a string
// e.g., { years: 5, seconds: 30 } -> [ '5 years', '30s' ]
const displayParts = allUnits.reduce((parts, unitType) => {
if (durationParts[unitType.name]) {
const count = durationParts[unitType.name];
parts.push(pluralizeUnits(count, unitType, longForm));
}
return parts;
}, []);
if (displayParts.length) {
return displayParts.join(' ');
}
// When the duration is 0, show 0 in terms of `units`
return pluralizeUnits(0, allUnits.findBy('suffix', units), longForm);
}