From 4d00f2f46d374ba1104d3de3296308d7527dfa60 Mon Sep 17 00:00:00 2001 From: Michael Lange Date: Wed, 30 May 2018 01:03:32 -0700 Subject: [PATCH] Duration formatting utility The cloest Moment.js has is "homanize" which isn't precise enough. --- ui/app/helpers/format-duration.js | 8 ++++ ui/app/utils/format-duration.js | 47 +++++++++++++++++++++ ui/tests/unit/utils/format-duration-test.js | 23 ++++++++++ 3 files changed, 78 insertions(+) create mode 100644 ui/app/helpers/format-duration.js create mode 100644 ui/app/utils/format-duration.js create mode 100644 ui/tests/unit/utils/format-duration-test.js diff --git a/ui/app/helpers/format-duration.js b/ui/app/helpers/format-duration.js new file mode 100644 index 000000000..c85a14b41 --- /dev/null +++ b/ui/app/helpers/format-duration.js @@ -0,0 +1,8 @@ +import Helper from '@ember/component/helper'; +import formatDuration from '../utils/format-duration'; + +function formatDurationHelper([duration], { units }) { + return formatDuration(duration, units); +} + +export default Helper.helper(formatDurationHelper); diff --git a/ui/app/utils/format-duration.js b/ui/app/utils/format-duration.js new file mode 100644 index 000000000..2ed5f238d --- /dev/null +++ b/ui/app/utils/format-duration.js @@ -0,0 +1,47 @@ +import moment from 'moment'; + +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', inMoment: true, pluralizable: false }, + { name: 'minutes', suffix: 'm', inMoment: true, pluralizable: false }, + { name: 'seconds', suffix: 's', 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 }, +]; + +export default function formatDuration(duration = 0, units = 'ns') { + const durationParts = {}; + + 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); + } + + const momentDuration = moment.duration(duration, ['ns', 'mms'].includes(units) ? 'ms' : units); + + allUnits + .filterBy('inMoment') + .mapBy('name') + .forEach(unit => { + durationParts[unit] = momentDuration[unit](); + }); + + const displayParts = allUnits.reduce((parts, unitType) => { + if (durationParts[unitType.name]) { + const count = durationParts[unitType.name]; + const suffix = + count === 1 || !unitType.pluralizable ? unitType.suffix : unitType.suffix.pluralize(); + parts.push(`${count}${unitType.pluralizable ? ' ' : ''}${suffix}`); + } + return parts; + }, []); + + return displayParts.join(' '); +} diff --git a/ui/tests/unit/utils/format-duration-test.js b/ui/tests/unit/utils/format-duration-test.js new file mode 100644 index 000000000..1e38c222a --- /dev/null +++ b/ui/tests/unit/utils/format-duration-test.js @@ -0,0 +1,23 @@ +import { module, test } from 'ember-qunit'; +import formatDuration from 'nomad-ui/utils/format-duration'; + +module('Unit | Util | formatDuration'); + +test('When all units have values, all units are displayed', function(assert) { + const expectation = '39 years 1 month 13 days 23h 31m 30s 987ms 654µs 400ns'; + assert.equal(formatDuration(1234567890987654321), expectation, expectation); +}); + +test('Any unit without values gets dropped from the display', function(assert) { + const expectation = '14 days 6h 56m 890ms 980µs'; + assert.equal(formatDuration(1234560890980000), expectation, expectation); +}); + +test('The units option allows for units coarser than nanoseconds', function(assert) { + const expectation1 = '1s 200ms'; + const expectation2 = '20m'; + const expectation3 = '1 month 1 day'; + assert.equal(formatDuration(1200, 'ms'), expectation1, expectation1); + assert.equal(formatDuration(1200, 's'), expectation2, expectation2); + assert.equal(formatDuration(32, 'd'), expectation3, expectation3); +});