diff --git a/src/duration.ts b/src/duration.ts index 5dc7a3c..a0529e3 100644 --- a/src/duration.ts +++ b/src/duration.ts @@ -151,33 +151,45 @@ export function roundToSingleUnit(duration: Duration, {relativeTo = Date.now()}: if (!days && hours >= 21) days += Math.round(hours / 24) if (days || weeks || months || years) hours = 0 + // Resolve calendar dates const currentYear = relativeTo.getFullYear() let currentMonth = relativeTo.getMonth() const currentDate = relativeTo.getDate() - if (days >= 27 || (years + months && days)) { - relativeTo.setDate(currentDate + days * sign) - months += Math.abs( - relativeTo.getFullYear() >= currentYear - ? relativeTo.getMonth() - currentMonth - : relativeTo.getMonth() - currentMonth - 12, - ) - if (months) { - days = 0 + if (days >= 27 || years + months + days) { + const newDate = new Date(relativeTo) + newDate.setFullYear(currentYear + years * sign) + newDate.setMonth(currentMonth + months * sign) + newDate.setDate(currentDate + days * sign) + const yearDiff = newDate.getFullYear() - relativeTo.getFullYear() + const monthDiff = newDate.getMonth() - relativeTo.getMonth() + const daysDiff = Math.abs(Math.round((Number(newDate) - Number(relativeTo)) / 86400000)) + const monthsDiff = Math.abs(yearDiff * 12 + monthDiff) + if (daysDiff < 27) { + if (days >= 6) { + weeks += Math.round(days / 7) + days = 0 + } else { + days = daysDiff + } + months = years = 0 + } else if (monthsDiff < 11) { + months = monthsDiff + years = 0 + } else { + months = 0 + years = yearDiff * sign } + if (months || years) days = 0 currentMonth = relativeTo.getMonth() } - - if (days >= 6) weeks += Math.round(days / 7) - if (weeks || months || years) days = 0 + if (years) months = 0 if (weeks >= 4) months += Math.round(weeks / 4) if (months || years) weeks = 0 - - if (months >= 11 || (years && months)) { - relativeTo.setMonth(relativeTo.getMonth() + months * sign) - years += Math.abs(currentYear - relativeTo.getFullYear()) + if (days && weeks && !months && !years) { + weeks += Math.round(days / 7) + days = 0 } - if (years) months = 0 return new Duration( years * sign, diff --git a/test/duration.ts b/test/duration.ts index ef96d97..d90c490 100644 --- a/test/duration.ts +++ b/test/duration.ts @@ -293,6 +293,7 @@ suite('duration', function () { ['P9M20DT25H', 'P10M', {relativeTo: new Date('2023-01-12T00:00:00Z')}], ['P11M', 'P1Y', {relativeTo: new Date('2022-11-01T00:00:00Z')}], ['-P11M', '-P1Y', {relativeTo: new Date('2022-11-01T00:00:00Z')}], + ['-P11M15D', '-P1Y', {relativeTo: new Date('2024-01-06T00:00:00')}], ['P1Y4D', 'P1Y', {relativeTo: new Date('2022-11-01T00:00:00Z')}], ['P1Y5M13D', 'P1Y', {relativeTo: new Date('2023-01-01T00:00:00Z')}], ['P1Y5M15D', 'P1Y', {relativeTo: new Date('2023-01-01T00:00:00Z')}], @@ -308,7 +309,12 @@ suite('duration', function () { relativeTo: new Date('2022-01-01T00:00:00Z'), }, ], - ['-P27D', '-P1M', {relativeTo: new Date('2023-02-28T00:00:00Z')}], + ['-P27D', '-P27D', {relativeTo: new Date('2023-02-28T00:00:00Z')}], + ['-P27D', '-P1M', {relativeTo: new Date('2023-02-27T00:00:00Z')}], + ['P1Y2M1D', 'P2Y', {relativeTo: new Date('2022-12-31T12:00:00.000Z')}], + ['-P1Y8D', '-P1Y', {relativeTo: new Date('2024-01-11T12:00:00.000Z')}], + ['-P1Y7DT19H43M19S', '-P1Y', {relativeTo: new Date('2024-01-11T12:00:00.000Z')}], + ['-P1Y11D', '-P2Y', {relativeTo: new Date('2024-01-11T12:00:00.000Z')}], ]) for (const [input, expected, opts] of roundTests) { test(`roundToSingleUnit(${input}) === ${expected}`, () => { diff --git a/test/relative-time.js b/test/relative-time.js index 1f20571..c49d731 100644 --- a/test/relative-time.js +++ b/test/relative-time.js @@ -482,7 +482,7 @@ suite('relative-time', function () { time.setAttribute('datetime', datetime) time.setAttribute('format', 'micro') await Promise.resolve() - assert.equal(time.shadowRoot.textContent, '10y') + assert.equal(time.shadowRoot.textContent, '11y') }) test('micro formats future times', async () => { @@ -2416,14 +2416,14 @@ suite('relative-time', function () { datetime: '2024-03-01T12:00:00.000Z', tense: 'future', format: 'auto', - expected: 'in 3 years', + expected: 'in 2 years', }, { reference: '2022-12-31T12:00:00.000Z', datetime: '2024-03-01T12:00:00.000Z', tense: 'future', format: 'micro', - expected: '3y', + expected: '2y', }, { reference: '2021-04-24T12:00:00.000Z', @@ -2432,6 +2432,13 @@ suite('relative-time', function () { format: 'micro', expected: '2y', }, + { + reference: '2024-01-04T12:00:00.000Z', + datetime: '2020-02-16T16:16:41.000Z', + tense: 'past', + format: 'auto', + expected: '4 years ago', + }, ]) for (const {