|
| 1 | +import moment from 'moment'; |
| 2 | + |
| 3 | +const allUnits = [ |
| 4 | + { name: 'years', suffix: 'year', inMoment: true, pluralizable: true }, |
| 5 | + { name: 'months', suffix: 'month', inMoment: true, pluralizable: true }, |
| 6 | + { name: 'days', suffix: 'day', inMoment: true, pluralizable: true }, |
| 7 | + { name: 'hours', suffix: 'h', inMoment: true, pluralizable: false }, |
| 8 | + { name: 'minutes', suffix: 'm', inMoment: true, pluralizable: false }, |
| 9 | + { name: 'seconds', suffix: 's', inMoment: true, pluralizable: false }, |
| 10 | + { name: 'milliseconds', suffix: 'ms', inMoment: true, pluralizable: false }, |
| 11 | + { name: 'microseconds', suffix: 'µs', inMoment: false, pluralizable: false }, |
| 12 | + { name: 'nanoseconds', suffix: 'ns', inMoment: false, pluralizable: false }, |
| 13 | +]; |
| 14 | + |
| 15 | +export default function formatDuration(duration = 0, units = 'ns') { |
| 16 | + const durationParts = {}; |
| 17 | + |
| 18 | + // Moment only handles up to millisecond precision. |
| 19 | + // Microseconds and nanoseconds need to be handled first, |
| 20 | + // then Moment can take over for all larger units. |
| 21 | + if (units === 'ns') { |
| 22 | + durationParts.nanoseconds = duration % 1000; |
| 23 | + durationParts.microseconds = Math.floor((duration % 1000000) / 1000); |
| 24 | + duration = Math.floor(duration / 1000000); |
| 25 | + } else if (units === 'mms') { |
| 26 | + durationParts.microseconds = duration % 1000; |
| 27 | + duration = Math.floor(duration / 1000); |
| 28 | + } |
| 29 | + |
| 30 | + let momentUnits = units; |
| 31 | + if (units === 'ns' || units === 'mms') { |
| 32 | + momentUnits = 'ms'; |
| 33 | + } |
| 34 | + const momentDuration = moment.duration(duration, momentUnits); |
| 35 | + |
| 36 | + // Get the count of each time unit that Moment handles |
| 37 | + allUnits |
| 38 | + .filterBy('inMoment') |
| 39 | + .mapBy('name') |
| 40 | + .forEach(unit => { |
| 41 | + durationParts[unit] = momentDuration[unit](); |
| 42 | + }); |
| 43 | + |
| 44 | + // Format each time time bucket as a string |
| 45 | + // e.g., { years: 5, seconds: 30 } -> [ '5 years', '30s' ] |
| 46 | + const displayParts = allUnits.reduce((parts, unitType) => { |
| 47 | + if (durationParts[unitType.name]) { |
| 48 | + const count = durationParts[unitType.name]; |
| 49 | + const suffix = |
| 50 | + count === 1 || !unitType.pluralizable ? unitType.suffix : unitType.suffix.pluralize(); |
| 51 | + parts.push(`${count}${unitType.pluralizable ? ' ' : ''}${suffix}`); |
| 52 | + } |
| 53 | + return parts; |
| 54 | + }, []); |
| 55 | + |
| 56 | + if (displayParts.length) { |
| 57 | + return displayParts.join(' '); |
| 58 | + } |
| 59 | + |
| 60 | + // When the duration is 0, show 0 in terms of `units` |
| 61 | + const unitTypeForUnits = allUnits.findBy('suffix', units); |
| 62 | + const suffix = unitTypeForUnits.pluralizable ? units.pluralize() : units; |
| 63 | + return `0${unitTypeForUnits.pluralizable ? ' ' : ''}${suffix}`; |
| 64 | +} |
0 commit comments