time.spec.ts 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. import {
  2. dayKeyOf,
  3. fmtDate,
  4. fmtDateTime,
  5. fmtDayLabel,
  6. fmtDuration,
  7. fmtRelTime,
  8. fmtTime,
  9. tsOf,
  10. } from './time';
  11. /**
  12. * 时间格式化 —— 全项目唯一实现。
  13. *
  14. * 重点回归:
  15. * ① Parse 的 Date 形态 `{__type:'Date', iso}` 不能被 String() 成 `[object Object]`
  16. * ② 相对时间的边界(刚刚 / 分钟前 / 昨天)
  17. * ③ 无法解析时不能返回 NaN 或 Invalid Date
  18. */
  19. describe('time', () => {
  20. describe('tsOf 归一(所有格式化都依赖它)', () => {
  21. it('毫秒数字原样返回', () => {
  22. expect(tsOf(1700000000000)).toBe(1700000000000);
  23. });
  24. it('Date 对象', () => {
  25. const d = new Date('2026-10-27T10:00:00Z');
  26. expect(tsOf(d)).toBe(d.getTime());
  27. });
  28. it('ISO 字符串', () => {
  29. expect(tsOf('2026-10-27T10:00:00Z')).toBe(Date.parse('2026-10-27T10:00:00Z'));
  30. });
  31. it('🔴 Parse 的 {__type,iso} 形态(不能变成 [object Object])', () => {
  32. const t = tsOf({ __type: 'Date', iso: '2026-10-27T10:00:00.000Z' });
  33. expect(t).toBe(Date.parse('2026-10-27T10:00:00.000Z'));
  34. expect(Number.isFinite(t)).toBeTrue();
  35. });
  36. it('{__type, value} 形态', () => {
  37. expect(tsOf({ __type: 'Date', value: '2026-10-27T10:00:00.000Z' })).toBe(
  38. Date.parse('2026-10-27T10:00:00.000Z'),
  39. );
  40. });
  41. it('无法解析 → NaN(而不是 0 —— 0 会被当成 1970 年)', () => {
  42. expect(Number.isNaN(tsOf(null))).toBeTrue();
  43. expect(Number.isNaN(tsOf('not-a-date'))).toBeTrue();
  44. expect(Number.isNaN(tsOf({}))).toBeTrue();
  45. });
  46. });
  47. describe('格式化输出', () => {
  48. const ISO = '2026-10-27T14:35:00+08:00';
  49. it('fmtTime / fmtDate / fmtDateTime 都能输出非空字符串', () => {
  50. expect(fmtTime(ISO)).toMatch(/\d{1,2}:\d{2}/);
  51. expect(fmtDate(ISO)).toMatch(/\d/);
  52. expect(fmtDateTime(ISO)).toMatch(/\d/);
  53. });
  54. it('🔴 无法解析时不返回 Invalid Date 之类的脏字符串', () => {
  55. for (const fn of [fmtTime, fmtDate, fmtDateTime, fmtDayLabel]) {
  56. const out = fn('garbage');
  57. expect(out).not.toContain('Invalid');
  58. expect(out).not.toContain('NaN');
  59. }
  60. });
  61. });
  62. describe('fmtRelTime(会话里的时间显示)', () => {
  63. // 实际语义:今天 → 时刻;昨天 → 「昨天 HH:MM」;更早 → 日期
  64. const NOW = new Date('2026-10-27T12:00:00');
  65. function rel(iso: string): string {
  66. return fmtRelTime(iso, NOW);
  67. }
  68. it('今天 → 只显示时刻', () => {
  69. const out = rel(new Date('2026-10-27T09:30:00').toISOString());
  70. expect(out).toMatch(/^\d{1,2}:\d{2}$/);
  71. });
  72. it('昨天 → 「昨天 + 时刻」', () => {
  73. const out = rel(new Date('2026-10-26T16:20:00').toISOString());
  74. expect(out).toContain('昨天');
  75. expect(out).toMatch(/\d{1,2}:\d{2}/);
  76. });
  77. it('今年更早 → 月-日', () => {
  78. const out = rel(new Date('2026-03-05T10:00:00').toISOString());
  79. expect(out).toMatch(/\d{2}-\d{2}/);
  80. expect(out).not.toContain('昨天');
  81. });
  82. it('往年 → 带年份的日期', () => {
  83. const out = rel(new Date('2025-03-05T10:00:00').toISOString());
  84. expect(out).toContain('2025');
  85. });
  86. it('🔴 无法解析 → 占位符,不返回 Invalid Date', () => {
  87. const out = rel('garbage');
  88. expect(out).toBe('—');
  89. expect(out).not.toContain('Invalid');
  90. });
  91. });
  92. describe('dayKeyOf(事件流分组)', () => {
  93. // 实际语义:今天 / 昨天 / 更早用日期
  94. it('同一个自然日 → 同一个 key', () => {
  95. const a = dayKeyOf('2026-10-20T01:00:00');
  96. const b = dayKeyOf('2026-10-20T23:00:00');
  97. expect(a).toBe(b);
  98. });
  99. it('跨天 → key 不同', () => {
  100. expect(dayKeyOf('2026-10-20T10:00:00')).not.toBe(dayKeyOf('2026-10-21T10:00:00'));
  101. });
  102. it('今天的 key 是「今天」', () => {
  103. expect(dayKeyOf(new Date().toISOString())).toBe('今天');
  104. });
  105. it('无法解析 → 「未知时间」(不崩)', () => {
  106. expect(dayKeyOf('garbage')).toBe('未知时间');
  107. });
  108. });
  109. describe('fmtDuration', () => {
  110. it('小时数 → 可读文案', () => {
  111. expect(fmtDuration(436)).toBeTruthy();
  112. });
  113. it('0 / 未定义时不返回奇怪内容', () => {
  114. expect(fmtDuration(0)).not.toContain('NaN');
  115. expect(fmtDuration(undefined)).not.toContain('NaN');
  116. });
  117. });
  118. });