import { dayKeyOf, fmtDate, fmtDateTime, fmtDayLabel, fmtDuration, fmtRelTime, fmtTime, tsOf, } from './time'; /** * 时间格式化 —— 全项目唯一实现。 * * 重点回归: * ① Parse 的 Date 形态 `{__type:'Date', iso}` 不能被 String() 成 `[object Object]` * ② 相对时间的边界(刚刚 / 分钟前 / 昨天) * ③ 无法解析时不能返回 NaN 或 Invalid Date */ describe('time', () => { describe('tsOf 归一(所有格式化都依赖它)', () => { it('毫秒数字原样返回', () => { expect(tsOf(1700000000000)).toBe(1700000000000); }); it('Date 对象', () => { const d = new Date('2026-10-27T10:00:00Z'); expect(tsOf(d)).toBe(d.getTime()); }); it('ISO 字符串', () => { expect(tsOf('2026-10-27T10:00:00Z')).toBe(Date.parse('2026-10-27T10:00:00Z')); }); it('🔴 Parse 的 {__type,iso} 形态(不能变成 [object Object])', () => { const t = tsOf({ __type: 'Date', iso: '2026-10-27T10:00:00.000Z' }); expect(t).toBe(Date.parse('2026-10-27T10:00:00.000Z')); expect(Number.isFinite(t)).toBeTrue(); }); it('{__type, value} 形态', () => { expect(tsOf({ __type: 'Date', value: '2026-10-27T10:00:00.000Z' })).toBe( Date.parse('2026-10-27T10:00:00.000Z'), ); }); it('无法解析 → NaN(而不是 0 —— 0 会被当成 1970 年)', () => { expect(Number.isNaN(tsOf(null))).toBeTrue(); expect(Number.isNaN(tsOf('not-a-date'))).toBeTrue(); expect(Number.isNaN(tsOf({}))).toBeTrue(); }); }); describe('格式化输出', () => { const ISO = '2026-10-27T14:35:00+08:00'; it('fmtTime / fmtDate / fmtDateTime 都能输出非空字符串', () => { expect(fmtTime(ISO)).toMatch(/\d{1,2}:\d{2}/); expect(fmtDate(ISO)).toMatch(/\d/); expect(fmtDateTime(ISO)).toMatch(/\d/); }); it('🔴 无法解析时不返回 Invalid Date 之类的脏字符串', () => { for (const fn of [fmtTime, fmtDate, fmtDateTime, fmtDayLabel]) { const out = fn('garbage'); expect(out).not.toContain('Invalid'); expect(out).not.toContain('NaN'); } }); }); describe('fmtRelTime(会话里的时间显示)', () => { // 实际语义:今天 → 时刻;昨天 → 「昨天 HH:MM」;更早 → 日期 const NOW = new Date('2026-10-27T12:00:00'); function rel(iso: string): string { return fmtRelTime(iso, NOW); } it('今天 → 只显示时刻', () => { const out = rel(new Date('2026-10-27T09:30:00').toISOString()); expect(out).toMatch(/^\d{1,2}:\d{2}$/); }); it('昨天 → 「昨天 + 时刻」', () => { const out = rel(new Date('2026-10-26T16:20:00').toISOString()); expect(out).toContain('昨天'); expect(out).toMatch(/\d{1,2}:\d{2}/); }); it('今年更早 → 月-日', () => { const out = rel(new Date('2026-03-05T10:00:00').toISOString()); expect(out).toMatch(/\d{2}-\d{2}/); expect(out).not.toContain('昨天'); }); it('往年 → 带年份的日期', () => { const out = rel(new Date('2025-03-05T10:00:00').toISOString()); expect(out).toContain('2025'); }); it('🔴 无法解析 → 占位符,不返回 Invalid Date', () => { const out = rel('garbage'); expect(out).toBe('—'); expect(out).not.toContain('Invalid'); }); }); describe('dayKeyOf(事件流分组)', () => { // 实际语义:今天 / 昨天 / 更早用日期 it('同一个自然日 → 同一个 key', () => { const a = dayKeyOf('2026-10-20T01:00:00'); const b = dayKeyOf('2026-10-20T23:00:00'); expect(a).toBe(b); }); it('跨天 → key 不同', () => { expect(dayKeyOf('2026-10-20T10:00:00')).not.toBe(dayKeyOf('2026-10-21T10:00:00')); }); it('今天的 key 是「今天」', () => { expect(dayKeyOf(new Date().toISOString())).toBe('今天'); }); it('无法解析 → 「未知时间」(不崩)', () => { expect(dayKeyOf('garbage')).toBe('未知时间'); }); }); describe('fmtDuration', () => { it('小时数 → 可读文案', () => { expect(fmtDuration(436)).toBeTruthy(); }); it('0 / 未定义时不返回奇怪内容', () => { expect(fmtDuration(0)).not.toContain('NaN'); expect(fmtDuration(undefined)).not.toContain('NaN'); }); }); });