This commit is contained in:
Eric Bailey
2024-06-18 09:43:18 -05:00
parent 58adc0ef08
commit abb01c7d80
2 changed files with 193 additions and 0 deletions
+102
View File
@@ -0,0 +1,102 @@
import {describe, expect, it} from '@jest/globals'
import {MessageDescriptor} from '@lingui/core'
import {addDays, subDays, subHours, subMinutes, subSeconds} from 'date-fns'
import {dateDiff} from '../useTimeAgo'
const lingui: any = (obj: MessageDescriptor) => obj.message
const base = new Date('2024-06-17T00:00:00Z')
describe('dateDiff', () => {
it(`works with numbers`, () => {
expect(dateDiff(subDays(base, 3), Number(base), {lingui})).toEqual('3d')
})
it(`works with strings`, () => {
expect(dateDiff(subDays(base, 3), base.toString(), {lingui})).toEqual('3d')
})
it(`works with dates`, () => {
expect(dateDiff(subDays(base, 3), base, {lingui})).toEqual('3d')
})
it(`equal values return now`, () => {
expect(dateDiff(base, base, {lingui})).toEqual('now')
})
it(`future dates return now`, () => {
expect(dateDiff(addDays(base, 3), base, {lingui})).toEqual('now')
})
it(`values < 5 seconds ago return now`, () => {
const then = subSeconds(base, 4)
expect(dateDiff(then, base, {lingui})).toEqual('now')
})
it(`values >= 5 seconds ago return seconds`, () => {
const then = subSeconds(base, 5)
expect(dateDiff(then, base, {lingui})).toEqual('5s')
})
it(`values < 1 min return seconds`, () => {
const then = subSeconds(base, 59)
expect(dateDiff(then, base, {lingui})).toEqual('59s')
})
it(`values >= 1 min return minutes`, () => {
const then = subSeconds(base, 60)
expect(dateDiff(then, base, {lingui})).toEqual('1m')
})
it(`minutes round down`, () => {
const then = subSeconds(base, 119)
expect(dateDiff(then, base, {lingui})).toEqual('1m')
})
it(`values < 1 hour return minutes`, () => {
const then = subMinutes(base, 59)
expect(dateDiff(then, base, {lingui})).toEqual('59m')
})
it(`values >= 1 hour return hours`, () => {
const then = subMinutes(base, 60)
expect(dateDiff(then, base, {lingui})).toEqual('1h')
})
it(`hours round down`, () => {
const then = subMinutes(base, 119)
expect(dateDiff(then, base, {lingui})).toEqual('1h')
})
it(`values < 1 day return hours`, () => {
const then = subHours(base, 23)
expect(dateDiff(then, base, {lingui})).toEqual('23h')
})
it(`values >= 1 day return days`, () => {
const then = subHours(base, 24)
expect(dateDiff(then, base, {lingui})).toEqual('1d')
})
it(`days round down`, () => {
const then = subHours(base, 47)
expect(dateDiff(then, base, {lingui})).toEqual('1d')
})
it(`values < 30 days return days`, () => {
const then = subDays(base, 29)
expect(dateDiff(then, base, {lingui})).toEqual('29d')
})
it(`values >= 30 days return months`, () => {
const then = subDays(base, 30)
expect(dateDiff(then, base, {lingui})).toEqual('1mo')
})
it(`months round down`, () => {
const then = subDays(base, 59)
expect(dateDiff(then, base, {lingui})).toEqual('1mo')
})
it(`values are rounded by increments of 30`, () => {
const then = subDays(base, 61)
expect(dateDiff(then, base, {lingui})).toEqual('2mo')
})
it(`values < 360 days return months`, () => {
const then = subDays(base, 359)
expect(dateDiff(then, base, {lingui})).toEqual('11mo')
})
it(`values >= 360 days return the earlier value`, () => {
const then = subDays(base, 361)
expect(dateDiff(then, base, {lingui})).toEqual(then.toLocaleDateString())
})
})
+91
View File
@@ -0,0 +1,91 @@
import {useCallback, useMemo} from 'react'
import {msg, plural} from '@lingui/macro'
import {I18nContext, useLingui} from '@lingui/react'
import {differenceInSeconds} from 'date-fns'
export type TimeAgoOptions = {
lingui: I18nContext['_']
format?: 'long' | 'short'
}
export function useGetTimeAgo() {
const {_} = useLingui()
return useCallback(
(
date: number | string | Date,
options?: Omit<TimeAgoOptions, 'lingui'>,
) => {
return dateDiff(date, Date.now(), {lingui: _, format: options?.format})
},
[_],
)
}
export function useTimeAgo(
date: number | string | Date,
options?: Omit<TimeAgoOptions, 'lingui'>,
): string {
const timeAgo = useGetTimeAgo()
return useMemo(() => {
return timeAgo(date, {...options})
}, [date, options, timeAgo])
}
const NOW = 5
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
*/
export function dateDiff(
earlier: number | string | Date,
later: number | string | Date,
options: TimeAgoOptions,
): string {
const _ = options.lingui
const format = options?.format || 'short'
const long = format === 'long'
const diffSeconds = differenceInSeconds(new Date(later), new Date(earlier))
if (diffSeconds < NOW) {
return _(msg`now`)
} else if (diffSeconds < MINUTE) {
return `${diffSeconds}${
long ? ` ${plural(diffSeconds, {one: 'second', other: 'seconds'})}` : 's'
}`
} else if (diffSeconds < HOUR) {
const diff = Math.floor(diffSeconds / MINUTE)
return `${diff}${
long ? ` ${plural(diff, {one: 'minute', other: 'minutes'})}` : 'm'
}`
} else if (diffSeconds < DAY) {
const diff = Math.floor(diffSeconds / HOUR)
return `${diff}${
long ? ` ${plural(diff, {one: 'hour', other: 'hours'})}` : 'h'
}`
} else if (diffSeconds < MONTH_30) {
const diff = Math.floor(diffSeconds / DAY)
return `${diff}${
long ? ` ${plural(diff, {one: 'day', other: 'days'})}` : 'd'
}`
} else {
const diff = Math.floor(diffSeconds / MONTH_30)
if (diff < 12) {
return `${diff}${
long ? ` ${plural(diff, {one: 'month', other: 'months'})}` : 'mo'
}`
} else {
const str = new Date(earlier).toLocaleDateString()
if (long) {
return _(msg`on ${str}`)
}
return str
}
}
}