/** * 自动将任意精度的时间戳转换为毫秒级时间戳 * 支持:秒(10位)、毫秒(13位)、微秒(16位)、纳秒(19位) */ function normalizeTimestamp(ts: number): number { if (ts <= 0) return ts; const len = Math.floor(ts).toString().length; if (len <= 10) { return ts * 1000; } else if (len <= 13) { return ts; } else if (len <= 16) { return Math.floor(ts / 1000); } else { return Math.floor(ts / 1000000); } } /** * 格式化时间戳为友好的显示格式(用于聊天/消息列表等) * - 当天、昨天:显示详细时间 HH:MM(昨天带「昨天」前缀) * - 大于昨天且在七天内:显示「X天前」 * - 超过七天且本年:显示 MM/DD * - 非本年:显示 YY/MM/DD * 自动识别时间戳精度,支持秒/毫秒/微秒/纳秒级时间戳 */ export function formatTime(ts: number): string { const MS = 1; const MINUTE = MS * 1000 * 60; const HOUR = MINUTE * 60; const DAY = HOUR * 24; const normalizedTs = normalizeTimestamp(ts); const now = Date.now(); const diff = now - normalizedTs; if (diff <= 0) return '未来'; if (diff < MINUTE) return '刚刚'; const target = new Date(normalizedTs); const today = new Date(now); const pad = (n: number) => String(n).padStart(2, '0'); const hm = `${pad(target.getHours())}:${pad(target.getMinutes())}`; const isSameDay = (a: Date, b: Date) => a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate(); if (isSameDay(today, target)) return hm; const yesterday = new Date(today); yesterday.setDate(today.getDate() - 1); if (isSameDay(yesterday, target)) return `昨天 ${hm}`; // 按日历天数算「X天前」,避免「前天」因不足 48 小时被算成 1 天 const todayStart = new Date(today.getFullYear(), today.getMonth(), today.getDate()).getTime(); const targetStart = new Date(target.getFullYear(), target.getMonth(), target.getDate()).getTime(); const calendarDaysAgo = Math.floor((todayStart - targetStart) / DAY); if (calendarDaysAgo >= 2 && calendarDaysAgo <= 7) return `${calendarDaysAgo}天前`; const MM = pad(target.getMonth() + 1); const DD = pad(target.getDate()); if (target.getFullYear() !== today.getFullYear()) { const YY = String(target.getFullYear()).slice(-2); return `${YY}/${MM}/${DD}`; } return `${MM}/${DD}`; } /** * 信息流用时间格式(Feed 专用) * - 1 分钟内:刚刚 * - 1 分钟~1 小时:xx分钟前 * - 1 小时~1 天:xx小时前 * - 1 天~1 周:x天前 * - 超过 1 周、今年内:MM/DD * - 不是今年:YYYY/MM/DD */ export function formatFeedTime(ts: number): string { const MS = 1; const MINUTE = MS * 1000 * 60; const HOUR = MINUTE * 60; const DAY = HOUR * 24; const WEEK = DAY * 7; const normalizedTs = normalizeTimestamp(ts); const now = Date.now(); const diff = now - normalizedTs; if (diff <= 0) return '刚刚'; if (diff < MINUTE) return '刚刚'; if (diff < HOUR) { const m = Math.floor(diff / MINUTE); return `${m}分钟前`; } if (diff < DAY) { const h = Math.floor(diff / HOUR); return `${h}小时前`; } if (diff < WEEK) { const d = Math.floor(diff / DAY); return `${d}天前`; } const target = new Date(normalizedTs); const today = new Date(now); const pad = (n: number) => String(n).padStart(2, '0'); const MM = pad(target.getMonth() + 1); const DD = pad(target.getDate()); if (target.getFullYear() !== today.getFullYear()) { return `${target.getFullYear()}/${MM}/${DD}`; } return `${MM}/${DD}`; }