Skip to content

Commit 7db58b3

Browse files
authored
Merge pull request #285 from lilyinstarlight/fix/relative-time-at-end-of-longer-months
Correctly count month durations relative to the end of longer months
2 parents ed0833b + 60fb1ef commit 7db58b3

File tree

3 files changed

+35
-6
lines changed

3 files changed

+35
-6
lines changed

src/duration.ts

+10-4
Original file line numberDiff line numberDiff line change
@@ -153,16 +153,23 @@ export function roundToSingleUnit(duration: Duration, {relativeTo = Date.now()}:
153153

154154
// Resolve calendar dates
155155
const currentYear = relativeTo.getFullYear()
156-
let currentMonth = relativeTo.getMonth()
156+
const currentMonth = relativeTo.getMonth()
157157
const currentDate = relativeTo.getDate()
158158
if (days >= 27 || years + months + days) {
159+
const newMonthDate = new Date(relativeTo)
160+
newMonthDate.setDate(1)
161+
newMonthDate.setMonth(currentMonth + months * sign + 1)
162+
newMonthDate.setDate(0)
163+
const monthDateCorrection = Math.max(0, currentDate - newMonthDate.getDate())
164+
159165
const newDate = new Date(relativeTo)
160166
newDate.setFullYear(currentYear + years * sign)
167+
newDate.setDate(currentDate - monthDateCorrection)
161168
newDate.setMonth(currentMonth + months * sign)
162-
newDate.setDate(currentDate + days * sign)
169+
newDate.setDate(currentDate - monthDateCorrection + days * sign)
163170
const yearDiff = newDate.getFullYear() - relativeTo.getFullYear()
164171
const monthDiff = newDate.getMonth() - relativeTo.getMonth()
165-
const daysDiff = Math.abs(Math.round((Number(newDate) - Number(relativeTo)) / 86400000))
172+
const daysDiff = Math.abs(Math.round((Number(newDate) - Number(relativeTo)) / 86400000)) + monthDateCorrection
166173
const monthsDiff = Math.abs(yearDiff * 12 + monthDiff)
167174
if (daysDiff < 27) {
168175
if (days >= 6) {
@@ -180,7 +187,6 @@ export function roundToSingleUnit(duration: Duration, {relativeTo = Date.now()}:
180187
years = yearDiff * sign
181188
}
182189
if (months || years) days = 0
183-
currentMonth = relativeTo.getMonth()
184190
}
185191
if (years) months = 0
186192

test/duration.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,9 @@ suite('duration', function () {
367367
['-P55D', [-1, 'month'], {relativeTo: '2023-02-27T22:22:57Z'}],
368368
['-P65D', [-3, 'month'], {relativeTo: '2023-02-28T22:22:57Z'}],
369369
['-P75D', [-3, 'month'], {relativeTo: '2023-03-09T22:22:57Z'}],
370+
['P1M', [1, 'month'], {relativeTo: '2024-05-31T00:00:00Z'}],
371+
['-P1M', [-1, 'month'], {relativeTo: '2024-05-31T00:00:00Z'}],
372+
['-P3M', [-3, 'month'], {relativeTo: '2023-05-30T00:00:00Z'}],
370373
[
371374
'P8M',
372375
[8, 'month'],
@@ -396,6 +399,8 @@ suite('duration', function () {
396399
},
397400
],
398401
['P1M1D', [1, 'month'], {relativeTo: new Date('2022-12-01T00:00:00Z')}],
402+
['P1M1D', [2, 'month'], {relativeTo: new Date('2023-01-31T00:00:00Z')}],
403+
['P1M30D', [2, 'month'], {relativeTo: new Date('2023-01-31T00:00:00Z')}],
399404
[
400405
'P9M20DT25H',
401406
[9, 'month'],
@@ -478,14 +483,14 @@ suite('duration', function () {
478483
],
479484
])
480485
for (const [input, [val, unit], opts] of relativeTests) {
481-
test(`getRelativeTimeUnit(${input}) === [${val}, ${unit}]`, () => {
486+
test(`getRelativeTimeUnit(${input}${opts ? `, ${JSON.stringify(opts)}` : ''}) === [${val}, ${unit}]`, () => {
482487
assert.deepEqual(
483488
getRelativeTimeUnit(Duration.from(input), opts || {relativeTo: new Date('2023-07-01T00:00:00')}),
484489
[val, unit],
485490
)
486491
})
487492
if (opts?.relativeTo) continue
488-
test(`getRelativeTimeUnit(-${input}) === [-${val}, ${unit}]`, () => {
493+
test(`getRelativeTimeUnit(-${input}${opts ? `, ${JSON.stringify(opts)}` : ''}) === [-${val}, ${unit}]`, () => {
489494
assert.deepEqual(
490495
getRelativeTimeUnit(Duration.from(`-${input}`), opts || {relativeTo: new Date('2023-07-01T00:00:00')}),
491496
[-val, unit],

test/relative-time.js

+18
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,24 @@ suite('relative-time', function () {
482482
assert.equal(time.shadowRoot.textContent, '4 months ago')
483483
})
484484

485+
test('rewrites from last few days of month to smaller last month', async () => {
486+
freezeTime(new Date(2024, 4, 31))
487+
const time = document.createElement('relative-time')
488+
time.setAttribute('tense', 'past')
489+
time.setAttribute('datetime', '2024-04-30T00:00:00Z')
490+
await Promise.resolve()
491+
assert.equal(time.shadowRoot.textContent, 'last month')
492+
})
493+
494+
test('rewrites from last few days of month to smaller previous month', async () => {
495+
freezeTime(new Date(2024, 4, 31))
496+
const time = document.createElement('relative-time')
497+
time.setAttribute('tense', 'past')
498+
time.setAttribute('datetime', '2024-02-29T00:00:00Z')
499+
await Promise.resolve()
500+
assert.equal(time.shadowRoot.textContent, '3 months ago')
501+
})
502+
485503
test('micro formats years', async () => {
486504
const datetime = new Date()
487505
datetime.setFullYear(datetime.getFullYear() - 10)

0 commit comments

Comments
 (0)