diff --git a/lib/__tests__/timezone.test.ts b/lib/__tests__/timezone.test.ts index 2bdc6fbd8..5fcc01f18 100644 --- a/lib/__tests__/timezone.test.ts +++ b/lib/__tests__/timezone.test.ts @@ -172,4 +172,12 @@ describe('mail timestamp formatting honours the override', () => { expect(formatDate(received)).toContain('23:00'); expect(formatDateTime(received, '24h', { year: 'numeric', month: 'short', day: 'numeric' })).toContain('23:00'); }); + + it.each(['smart', 'relative', 'full'] as const)('formatDate returns "" for an invalid date in %s mode (#1099)', (dateFormat) => { + useSettingsStore.setState({ timeZone: 'Asia/Tokyo', dateFormat }); + expect(formatDate('not-a-valid-date')).toBe(''); + expect(formatDate('10000-01-01T00:00:00Z')).toBe(''); + expect(formatDate(new Date('nope'))).toBe(''); + expect(formatDate(undefined as unknown as string)).toBe(''); + }); }); diff --git a/lib/utils.ts b/lib/utils.ts index 67eb884c9..8262bcf25 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -96,6 +96,10 @@ function resolveDateLocale( */ export function formatDate(date: Date | string): string { const d = typeof date === "string" ? new Date(date) : date; + // An unparsable receivedAt (e.g. a 5-digit year) makes Intl throw + // "RangeError: Invalid time value" and takes the whole list down with it. + // Leave the row's date slot blank; the viewer header shows the raw value. (#1099) + if (!d || Number.isNaN(d.getTime())) return ""; const now = new Date(); const locale = getEffectiveLocale();