diff --git a/src/lib/strings/__tests__/time.test.ts b/src/lib/strings/__tests__/time.test.ts new file mode 100644 index 0000000000..1f6bc035b3 --- /dev/null +++ b/src/lib/strings/__tests__/time.test.ts @@ -0,0 +1,99 @@ +import {describe, expect, it} from '@jest/globals' +import {addDays, subDays, subHours, subMinutes, subSeconds} from 'date-fns' + +import {dateDiff} from '../time' + +const base = new Date('2024-06-17T00:00:00Z') + +describe('dateDiff', () => { + it(`works with numbers`, () => { + expect(dateDiff(subDays(base, 3), Number(base))).toEqual('3d') + }) + it(`works with strings`, () => { + expect(dateDiff(subDays(base, 3), base.toString())).toEqual('3d') + }) + it(`works with dates`, () => { + expect(dateDiff(subDays(base, 3), base)).toEqual('3d') + }) + + it(`equal values return now`, () => { + expect(dateDiff(base, base)).toEqual('now') + }) + it(`future dates return now`, () => { + expect(dateDiff(addDays(base, 3), base)).toEqual('now') + }) + + it(`values < 5 seconds ago return now`, () => { + const then = subSeconds(base, 4) + expect(dateDiff(then, base)).toEqual('now') + }) + it(`values >= 5 seconds ago return seconds`, () => { + const then = subSeconds(base, 5) + expect(dateDiff(then, base)).toEqual('5s') + }) + + it(`values < 1 min return seconds`, () => { + const then = subSeconds(base, 59) + expect(dateDiff(then, base)).toEqual('59s') + }) + it(`values >= 1 min return minutes`, () => { + const then = subSeconds(base, 60) + expect(dateDiff(then, base)).toEqual('1m') + }) + it(`minutes round down`, () => { + const then = subSeconds(base, 119) + expect(dateDiff(then, base)).toEqual('1m') + }) + + it(`values < 1 hour return minutes`, () => { + const then = subMinutes(base, 59) + expect(dateDiff(then, base)).toEqual('59m') + }) + it(`values >= 1 hour return hours`, () => { + const then = subMinutes(base, 60) + expect(dateDiff(then, base)).toEqual('1h') + }) + it(`hours round down`, () => { + const then = subMinutes(base, 119) + expect(dateDiff(then, base)).toEqual('1h') + }) + + it(`values < 1 day return hours`, () => { + const then = subHours(base, 23) + expect(dateDiff(then, base)).toEqual('23h') + }) + it(`values >= 1 day return days`, () => { + const then = subHours(base, 24) + expect(dateDiff(then, base)).toEqual('1d') + }) + it(`days round down`, () => { + const then = subHours(base, 47) + expect(dateDiff(then, base)).toEqual('1d') + }) + + it(`values < 30 days return days`, () => { + const then = subDays(base, 29) + expect(dateDiff(then, base)).toEqual('29d') + }) + it(`values >= 30 days return months`, () => { + const then = subDays(base, 30) + expect(dateDiff(then, base)).toEqual('1mo') + }) + it(`months round down`, () => { + const then = subDays(base, 59) + expect(dateDiff(then, base)).toEqual('1mo') + }) + it(`values are rounded by increments of 30`, () => { + const then = subDays(base, 61) + expect(dateDiff(then, base)).toEqual('2mo') + }) + + it(`values < 360 days return months`, () => { + const then = subDays(base, 359) + expect(dateDiff(then, base)).toEqual('11mo') + }) + it(`values >= 360 days return the earlier value`, () => { + const then = subDays(base, 361) + expect(dateDiff(then, base)).toEqual(then.toLocaleDateString()) + }) +}) diff --git a/src/lib/strings/time.ts b/src/lib/strings/time.ts index 2d5291a7fd..d95b9a0cbd 100644 --- a/src/lib/strings/time.ts +++ b/src/lib/strings/time.ts @@ -1,3 +1,5 @@ +import {differenceInSeconds} from 'date-fns' + export function ago(date: number | string | Date): string { return dateDiff(date, Date.now()) } @@ -7,28 +9,17 @@ const MINUTE = 60 const HOUR = MINUTE * 60 const DAY = HOUR * 24 const MONTH_30 = DAY * 30 -/** Returns the difference between date1 and date2 in full seconds, minutes, hours etc. All month are considered exactly 30 days. Assuming that date1 <= date2. Differences >= 360 days are returned as the "M/D/YYYY" string. */ + +/** + * Returns the difference between date1 and date2 in full seconds, minutes, + * hours etc. All month are considered exactly 30 days. Assuming that date1 <= + * date2. Differences >= 360 days are returned as the "M/D/YYYY" string + */ export function dateDiff( - date1: number | string | Date, - date2: number | string | Date, + earlier: number | string | Date, + later: number | string | Date, ): string { - let ts1: number - if (typeof date1 === 'string') { - ts1 = Number(new Date(date1)) - } else if (date1 instanceof Date) { - ts1 = Number(date1) - } else { - ts1 = date1 - } - let ts2: number - if (typeof date2 === 'string') { - ts2 = Number(new Date(date2)) - } else if (date2 instanceof Date) { - ts2 = Number(date2) - } else { - ts2 = date2 - } - const diffSeconds = Math.floor((ts2 - ts1) / 1e3) + const diffSeconds = differenceInSeconds(new Date(later), new Date(earlier)) if (diffSeconds < NOW) { return `now` } else if (diffSeconds < MINUTE) { @@ -44,7 +35,7 @@ export function dateDiff( if (diffMonths30 < 12) { return `${diffMonths30}mo` } else { - return new Date(ts1).toLocaleDateString() + return new Date(earlier).toLocaleDateString() } } }