export const BUSINESS_TIME_ZONE = "Asia/Shanghai"; const BUSINESS_OFFSET = "+08:00"; const DATE_ONLY_PATTERN = /^\d{4}-\d{2}-\d{2}$/; const EXPLICIT_OFFSET_PATTERN = /(?:Z|[+-]\d{2}:?\d{2})$/i; export function parseApiDateTime(value?: string | Date | null): Date | null { if (!value) return null; if (value instanceof Date) return Number.isNaN(value.getTime()) ? null : value; const text = value.trim(); if (!text) return null; const isoText = text.includes(" ") ? text.replace(" ", "T") : text; const normalized = DATE_ONLY_PATTERN.test(isoText) ? `${isoText}T00:00:00${BUSINESS_OFFSET}` : EXPLICIT_OFFSET_PATTERN.test(isoText) ? isoText : `${isoText}Z`; const parsed = new Date(normalized); return Number.isNaN(parsed.getTime()) ? null : parsed; } function businessParts(value?: string | Date | null) { const parsed = parseApiDateTime(value); if (!parsed) return null; const parts = new Intl.DateTimeFormat("zh-CN", { timeZone: BUSINESS_TIME_ZONE, year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit", hour12: false, hourCycle: "h23", }).formatToParts(parsed); return Object.fromEntries(parts.map((part) => [part.type, part.value])); } export function formatDate(value?: string | Date | null, fallback = "-") { const parts = businessParts(value); return parts ? `${parts.year}-${parts.month}-${parts.day}` : fallback; } export function formatTime(value?: string | Date | null, fallback = "-") { const parts = businessParts(value); return parts ? `${parts.hour}:${parts.minute}` : fallback; } export function formatDateTime(value?: string | Date | null, fallback = "-") { const parts = businessParts(value); return parts ? `${parts.year}-${parts.month}-${parts.day} ${parts.hour}:${parts.minute}:${parts.second}` : fallback; } export function businessDateKey(value?: string | Date | null): string | null { const parts = businessParts(value); return parts ? `${parts.year}-${parts.month}-${parts.day}` : null; } export function formatSessionTime( value?: string | Date | null, now: Date = new Date(), ): string { const targetKey = businessDateKey(value); const todayKey = businessDateKey(now); const time = formatTime(value, ""); if (!targetKey || !todayKey || !time) return ""; const dayNumber = (key: string) => { const [year, month, day] = key.split("-").map(Number); return Date.UTC(year, month - 1, day) / 86_400_000; }; const days = dayNumber(todayKey) - dayNumber(targetKey); if (days === 0) return time; if (days === 1) return `昨天 ${time}`; return `${targetKey.slice(5).replace("-", "/")} ${time}`; }