From dda2ab09195553ba3181bcab37a35259aa213ed4 Mon Sep 17 00:00:00 2001 From: Anant Thazhemadam Date: Mon, 29 Jun 2026 23:43:38 +0530 Subject: [PATCH] fix: keep same-month day durations monotonic Relative date labels around the month boundary can currently move backward in precision: a 25-day-old timestamp may render as "last month" while a 27-day-old timestamp renders as "27 days ago". In sorted date lists this makes newer items look older than later rows, even when the underlying timestamps are ordered correctly. Keep same-calendar-month day spans in days instead of promoting them through the intermediate four-week representation to one month. Preserve month rounding once the compared dates cross a calendar-month boundary. Add regression coverage for the 25/26-day same-month cases so the labels remain monotonic around the month boundary. --- src/duration.ts | 6 ++++-- test/duration.ts | 7 +++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/duration.ts b/src/duration.ts index 58492b27..c9488a97 100644 --- a/src/duration.ts +++ b/src/duration.ts @@ -181,8 +181,10 @@ export function roundToSingleUnit(duration: Duration, {relativeTo = Date.now()}: const daysDiff = Math.abs(Math.round((Number(newDate) - Number(relativeTo)) / 86400000)) + monthDateCorrection const monthsDiff = Math.abs(yearDiff * 12 + monthDiff) if (daysDiff < 27) { - if (days >= 6) { - weeks += Math.round(days / 7) + const roundedWeeks = Math.round(days / 7) + // Keep same-calendar-month spans as days instead of rounding 4 weeks to a month. + if (days >= 6 && (roundedWeeks < 4 || monthsDiff > 0)) { + weeks += roundedWeeks days = 0 } else { days = daysDiff diff --git a/test/duration.ts b/test/duration.ts index ecb9e68c..b90f915e 100644 --- a/test/duration.ts +++ b/test/duration.ts @@ -259,9 +259,9 @@ suite('duration', function () { ['-P21D', '-P3W', {relativeTo: new Date('2023-07-01T00:00:00')}], ['P24D', 'P3W', {relativeTo: new Date('2023-07-01T00:00:00')}], ['-P24D', '-P3W', {relativeTo: new Date('2023-07-01T00:00:00')}], - ['P24DT25H', 'P1M', {relativeTo: new Date('2023-07-01T00:00:00')}], + ['P24DT25H', 'P25D', {relativeTo: new Date('2023-07-01T00:00:00')}], ['-P24DT25H', '-P1M', {relativeTo: new Date('2023-07-01T00:00:00')}], - ['P25D', 'P1M', {relativeTo: new Date('2023-07-01T00:00:00')}], + ['P25D', 'P25D', {relativeTo: new Date('2023-07-01T00:00:00')}], ['-P25D', '-P1M', {relativeTo: new Date('2023-07-01T00:00:00')}], ['P1M1D', 'P1M', {relativeTo: new Date('2023-07-02T00:00:00')}], ['-P1M1D', '-P1M', {relativeTo: new Date('2023-07-02T00:00:00')}], @@ -365,6 +365,9 @@ suite('duration', function () { ['-P24D', [-3, 'week'], {relativeTo: '2023-01-01T00:00:00Z'}], ['P24DT25H', [1, 'month'], {relativeTo: '2023-01-15T00:00:00Z'}], ['P25D', [1, 'month'], {relativeTo: '2023-01-15T00:00:00Z'}], + ['P25D', [25, 'day'], {relativeTo: '2023-07-01T00:00:00Z'}], + ['-P25D', [-25, 'day'], {relativeTo: '2026-06-29T00:00:00Z'}], + ['-P26D', [-26, 'day'], {relativeTo: '2026-06-29T00:00:00Z'}], ['-P35D', [-1, 'month'], {relativeTo: '2023-02-07T22:22:57Z'}], ['-P45D', [-1, 'month'], {relativeTo: '2023-02-17T22:22:57Z'}], ['-P55D', [-1, 'month'], {relativeTo: '2023-02-27T22:22:57Z'}],