-
Notifications
You must be signed in to change notification settings - Fork 60
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
76 additions
and
29 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import { beforeEach, describe, expect, it, vi } from 'vitest' | ||
|
||
import { relativeTime } from './relativeTime' | ||
|
||
describe('relativeTime', () => { | ||
const SECONDS = 1000 | ||
const MINUTES = 60 * SECONDS | ||
const HOURS = 60 * MINUTES | ||
const DAYS = 24 * HOURS | ||
const YEARS = 365 * DAYS | ||
const NOW = new Date(2000, 6, 31, 12, 0, 0) | ||
|
||
beforeEach(() => { | ||
vi.useFakeTimers() | ||
vi.setSystemTime(NOW) | ||
}) | ||
|
||
it('returns formatted relative time', () => { | ||
expect(relativeTime(new Date(NOW.getTime() - 3 * SECONDS))).toBe( | ||
'Less than a minute ago', | ||
) | ||
expect(relativeTime(new Date(NOW.getTime() - 30 * SECONDS))).toBe( | ||
'1 minute ago', | ||
) | ||
expect(relativeTime(new Date(NOW.getTime() - 30 * MINUTES))).toBe( | ||
'30 minutes ago', | ||
) | ||
expect(relativeTime(new Date(NOW.getTime() - 59 * MINUTES))).toBe( | ||
'About 1 hour ago', | ||
) | ||
expect(relativeTime(new Date(NOW.getTime() - 2 * HOURS))).toBe( | ||
'Today at 10:00 AM', | ||
) | ||
expect(relativeTime(new Date(NOW.getTime() - 3 * DAYS))).toBe( | ||
'Last Friday at 12:00 PM', | ||
) | ||
expect( | ||
relativeTime(new Date(NOW.getTime() - (6 * DAYS + 12 * HOURS))), | ||
).toBe('Jul 25, 2000, 12:00:00 AM') | ||
expect(relativeTime(new Date(NOW.getTime() - 7 * DAYS))).toBe( | ||
'Jul 24, 2000, 12:00:00 PM', | ||
) | ||
expect(relativeTime(new Date(NOW.getTime() - 1 * YEARS))).toBe( | ||
'Aug 1, 1999, 12:00:00 PM', | ||
) | ||
}) | ||
|
||
it('returns "-" when no date is provided', () => { | ||
expect(relativeTime(null)).toBe('-') | ||
expect(relativeTime(undefined)).toBe('-') | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,29 @@ | ||
import { format, formatDistanceToNow, formatRelative } from 'date-fns' | ||
import { | ||
differenceInDays, | ||
differenceInHours, | ||
format, | ||
formatDistanceToNow, | ||
formatRelative, | ||
} from 'date-fns' | ||
|
||
const HOURS = 1000 * 60 * 60 | ||
const DAYS = HOURS * 24 | ||
function capitalize(str: string) { | ||
return str.replace(/^\w/, (c) => c.toUpperCase()) | ||
} | ||
|
||
export function relativeTime(date?: Date | null) { | ||
if (date == null) return '-' | ||
export function relativeTime(date: Date | null | undefined) { | ||
if (!date) return '-' | ||
// NOTE: This is a dummy defense to avoid crashing on the frontend | ||
if (!(date instanceof Date)) return '-' | ||
|
||
const now = new Date() | ||
const diff = now.getTime() - date.getTime() | ||
const capitalize = (str: string) => str.replace(/^\w/, (c) => c.toUpperCase()) | ||
|
||
if (diff < 1 * HOURS) { | ||
if (differenceInHours(now, date) < 1) { | ||
return capitalize(formatDistanceToNow(date, { addSuffix: true })) | ||
} | ||
if (diff < 7 * DAYS) return capitalize(formatRelative(date, new Date())) | ||
|
||
if (differenceInDays(now, date) < 6) { | ||
return capitalize(formatRelative(date, now)) | ||
} | ||
|
||
return capitalize(format(date, 'PPpp')) | ||
} |