diff --git a/src/plugin/duration/index.js b/src/plugin/duration/index.js index e2060c344..ca02c9379 100644 --- a/src/plugin/duration/index.js +++ b/src/plugin/duration/index.js @@ -23,6 +23,16 @@ const unitToMS = { weeks: MILLISECONDS_A_WEEK } +const RELATIVE_TIME_UNITS = ['years', 'months', 'days', 'hours', 'minutes', 'seconds'] +const RELATIVE_TIME_UNIT_MAP = { + years: 'y', + months: 'M', + days: 'd', + hours: 'h', + minutes: 'm', + seconds: 's' +} + const isDuration = d => d instanceof Duration // eslint-disable-line no-use-before-define let $d @@ -229,11 +239,58 @@ class Duration { return wrapper(this.$ms, this) } - humanize(withSuffix) { - return $d() - .add(this.$ms, 'ms') - .locale(this.$l) - .fromNow(!withSuffix) + humanize(options) { + let withSuffix = false + let round = true + + if (typeof options === 'boolean') { + withSuffix = options + } else if (typeof options === 'object' && options !== null) { + ({ withSuffix, round = true } = options) + } + + if (round) { + return $d() + .add(this.$ms, 'ms') + .locale(this.$l) + .fromNow(!withSuffix) + } + + const loc = $d().locale(this.$l).$locale().relativeTime + let resultStr = '' + const isFuture = this.$ms > 0 + + for (let i = 0; i < RELATIVE_TIME_UNITS.length; i += 1) { + const unitName = RELATIVE_TIME_UNITS[i] + const val = this.get(unitName) + if (val) { + const absVal = Math.abs(val) + const unitKey = RELATIVE_TIME_UNIT_MAP[unitName] + + let locKey = unitKey + if (absVal !== 1) { + const pluralKey = `${unitKey}${unitKey}` + if (loc[pluralKey]) { + locKey = pluralKey + } + } + + const format = loc[locKey] + resultStr = format.replace('%d', absVal) + break + } + } + + if (!resultStr) { + resultStr = loc.s + } + + if (withSuffix) { + const pastOrFuture = isFuture ? loc.future : loc.past + return pastOrFuture.replace('%s', resultStr) + } + + return resultStr } valueOf() { diff --git a/src/plugin/relativeTime/index.js b/src/plugin/relativeTime/index.js index 8d61c45b0..7c62cd484 100644 --- a/src/plugin/relativeTime/index.js +++ b/src/plugin/relativeTime/index.js @@ -7,6 +7,7 @@ export default (o, c, d) => { future: 'in %s', past: '%s ago', s: 'a few seconds', + ss: '%d seconds', m: 'a minute', mm: '%d minutes', h: 'an hour', diff --git a/test/plugin/duration.test.js b/test/plugin/duration.test.js index 3e726af91..b7e5d2905 100644 --- a/test/plugin/duration.test.js +++ b/test/plugin/duration.test.js @@ -309,3 +309,30 @@ describe('Format', () => { .toBe('2/02.0002TEST9:09:6:06:8:08:5:05:1:01:010') }) }) + +it('humanize() with round: false option', () => { + expect(dayjs.duration({ minutes: 45 }).humanize({ round: false })) + .toBe('45 minutes') + expect(dayjs.duration({ seconds: 10 }).humanize({ round: false })) + .toBe('10 seconds') + expect(dayjs.duration({ seconds: 1 }).humanize({ round: false })) + .toBe('a few seconds') + expect(dayjs.duration({ minutes: 1 }).humanize({ round: false })) + .toBe('a minute') + expect(dayjs.duration({ hours: 2 }).humanize({ round: false })) + .toBe('2 hours') + expect(dayjs.duration({ years: 1 }).humanize({ round: false })) + .toBe('a year') + expect(dayjs.duration({ minutes: 45 }).humanize()) + .toBe('an hour') + expect(dayjs.duration({ minutes: 45 }).humanize({ round: false, withSuffix: true })) + .toBe('in 45 minutes') + expect(dayjs.duration({ minutes: 45 }).humanize(true)) + .toBe('in an hour') + expect(dayjs.duration({ minutes: -45 }).humanize({ round: false, withSuffix: true })) + .toBe('45 minutes ago') + expect(dayjs.duration({ milliseconds: 100 }).humanize({ round: false })) + .toBe('a few seconds') + expect(dayjs.duration({ milliseconds: 200 }).humanize({ round: false, withSuffix: true })) + .toBe('in a few seconds') +})