github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/ui/app/utils/format-duration.js (about) 1 import moment from 'moment'; 2 import { pluralize } from 'ember-inflector'; 3 4 /** 5 * Metadata for all unit types 6 * name: identifier for the unit. Also maps to moment methods when applicable 7 * suffix: the preferred suffix for a unit 8 * inMoment: whether or not moment can be used to compute this unit value 9 * pluralizable: whether or not this suffix can be pluralized 10 * longSuffix: the suffix to use instead of suffix when longForm is true 11 */ 12 const allUnits = [ 13 { name: 'years', suffix: 'year', inMoment: true, pluralizable: true }, 14 { name: 'months', suffix: 'month', inMoment: true, pluralizable: true }, 15 { name: 'days', suffix: 'day', inMoment: true, pluralizable: true }, 16 { 17 name: 'hours', 18 suffix: 'h', 19 longSuffix: 'hour', 20 inMoment: true, 21 pluralizable: false, 22 }, 23 { 24 name: 'minutes', 25 suffix: 'm', 26 longSuffix: 'minute', 27 inMoment: true, 28 pluralizable: false, 29 }, 30 { 31 name: 'seconds', 32 suffix: 's', 33 longSuffix: 'second', 34 inMoment: true, 35 pluralizable: false, 36 }, 37 { name: 'milliseconds', suffix: 'ms', inMoment: true, pluralizable: false }, 38 { name: 'microseconds', suffix: 'µs', inMoment: false, pluralizable: false }, 39 { name: 'nanoseconds', suffix: 'ns', inMoment: false, pluralizable: false }, 40 ]; 41 42 const pluralizeUnits = (amount, unit, longForm) => { 43 let suffix; 44 45 if (longForm && unit.longSuffix) { 46 // Long form means always using full words (seconds insteand of s) which means 47 // pluralization is necessary. 48 suffix = amount === 1 ? unit.longSuffix : pluralize(unit.longSuffix); 49 } else { 50 // In the normal case, only pluralize based on the pluralizable flag 51 suffix = 52 amount === 1 || !unit.pluralizable ? unit.suffix : pluralize(unit.suffix); 53 } 54 55 // A space should go between the value and the unit when the unit is a full word 56 // 300ns vs. 1 hour 57 const addSpace = unit.pluralizable || (longForm && unit.longSuffix); 58 return `${amount}${addSpace ? ' ' : ''}${suffix}`; 59 }; 60 61 /** 62 * Format a Duration at a preferred precision 63 * 64 * @param {Number} duration The duration to format 65 * @param {String} units The units for the duration. Default to nanoseconds. 66 * @param {Boolean} longForm Whether or not to expand single character suffixes, 67 * used to ensure screen readers correctly read units. 68 */ 69 export default function formatDuration( 70 duration = 0, 71 units = 'ns', 72 longForm = false 73 ) { 74 const durationParts = {}; 75 76 // Moment only handles up to millisecond precision. 77 // Microseconds and nanoseconds need to be handled first, 78 // then Moment can take over for all larger units. 79 if (units === 'ns') { 80 durationParts.nanoseconds = duration % 1000; 81 durationParts.microseconds = Math.floor((duration % 1000000) / 1000); 82 duration = Math.floor(duration / 1000000); 83 } else if (units === 'mms') { 84 durationParts.microseconds = duration % 1000; 85 duration = Math.floor(duration / 1000); 86 } 87 88 let momentUnits = units; 89 if (units === 'ns' || units === 'mms') { 90 momentUnits = 'ms'; 91 } 92 const momentDuration = moment.duration(duration, momentUnits); 93 94 // Get the count of each time unit that Moment handles 95 allUnits 96 .filterBy('inMoment') 97 .mapBy('name') 98 .forEach((unit) => { 99 durationParts[unit] = momentDuration[unit](); 100 }); 101 102 // Format each time time bucket as a string 103 // e.g., { years: 5, seconds: 30 } -> [ '5 years', '30s' ] 104 const displayParts = allUnits.reduce((parts, unitType) => { 105 if (durationParts[unitType.name]) { 106 const count = durationParts[unitType.name]; 107 parts.push(pluralizeUnits(count, unitType, longForm)); 108 } 109 return parts; 110 }, []); 111 112 if (displayParts.length) { 113 return displayParts.join(' '); 114 } 115 116 // When the duration is 0, show 0 in terms of `units` 117 return pluralizeUnits(0, allUnits.findBy('suffix', units), longForm); 118 }