/** Generic display formatters shared across slices. */ export function formatBytes(value: number | null | undefined): string { if (value === null || value === undefined || !Number.isFinite(value)) return '—' const units = ['B', 'KB', 'MB', 'GB', 'TB'] let size = value let unit = 0 while (size >= 1024 && unit < units.length - 1) { size /= 1024 unit += 1 } return `${unit === 0 ? size : size.toFixed(1)} ${units[unit]}` } export function shortSha(sha: string | null | undefined, len = 7): string { return sha ? sha.slice(0, len) : '—' } export function fmtTime(value: string | number | null | undefined): string { if (value === null || value === undefined) return '—' if (typeof value === 'number') return new Date(value * 1000).toLocaleString('zh-CN', { hour12: false }) // Rust's time crate emits `… +00:00:00` (seconds in the offset), which // Date cannot parse — fold it back to `+00:00` before constructing. const iso = value.replace(/([+-]\d{2}):(\d{2}):(\d{2})$/, '$1:$2') const date = new Date(iso) return Number.isNaN(date.getTime()) ? value : date.toLocaleString('zh-CN', { hour12: false }) } /** Compact Chinese relative stamp ("19 天前") for commit rows. Accepts a unix * second value (the API's `author_time`) or a date string. */ export function fmtRelative(value: number | string | null | undefined): string { const epoch = typeof value === 'number' ? value : value ? Math.floor(new Date(value.replace(/([+-]\d{2}):(\d{2}):(\d{2})$/, '$1:$2')).getTime() / 1000) : Number.NaN if (!Number.isFinite(epoch)) return '—' const delta = Math.floor(Date.now() / 1000) - epoch // Future-dated commits (clock skew, rebased author time) read as "just now". if (delta < 60) return '刚刚' const minutes = Math.floor(delta / 60) if (minutes < 60) return `${minutes} 分钟前` const hours = Math.floor(minutes / 60) if (hours < 24) return `${hours} 小时前` const days = Math.floor(hours / 24) if (days < 31) return `${days} 天前` const months = Math.floor(days / 30) if (months < 12) return `${months} 个月前` return `${Math.floor(days / 365)} 年前` }