// Shared date/time formatting helpers. /** * 后端存储的是 UTC naive datetime(SQLite 不保留时区), * 序列化后无 Z/+00:00 后缀。JavaScript 对无时区字符串按本地时间处理, * 所以必须显式加 Z,让 Date 正确当作 UTC 解析,浏览器再转换为本地时区显示。 */ export function toDate(iso: string): Date { if (iso.endsWith('Z') || /[+-]\d{2}:\d{2}$/.test(iso)) return new Date(iso) return new Date(iso + 'Z') } /** 省略当年年份的紧凑格式,用于空间受限的列表行 */ export function shortDateTime(iso?: string | null): string { if (!iso) return '-' const d = toDate(iso) const pad = (n: number) => String(n).padStart(2, '0') const now = new Date() if (d.getFullYear() === now.getFullYear()) { return `${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}` } return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}` } export function formatDateTime(iso?: string | null): string { if (!iso) return '-' const d = toDate(iso) const pad = (n: number) => String(n).padStart(2, '0') return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}` } export function elapsedStr(startedAt?: string | null, completedAt?: string | null): string { if (!startedAt) return '-' const start = toDate(startedAt).getTime() const end = completedAt ? toDate(completedAt).getTime() : Date.now() const secs = Math.max(0, Math.round((end - start) / 1000)) if (secs < 60) return `${secs}s` const m = Math.floor(secs / 60) const s = secs % 60 if (m < 60) return `${m}m ${s}s` const h = Math.floor(m / 60) return `${h}h ${m % 60}m` }