import type { AdminApiLocale } from "@/lib/admin-locale"; function formatParts(date: Date, timeZone?: string): string { const parts = new Intl.DateTimeFormat("en-CA", { timeZone, year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit", hourCycle: "h23", }).formatToParts(date); const map = new Map(parts.map((part) => [part.type, part.value])); const year = map.get("year") ?? "0000"; const month = map.get("month") ?? "00"; const day = map.get("day") ?? "00"; const hour = map.get("hour") ?? "00"; const minute = map.get("minute") ?? "00"; const second = map.get("second") ?? "00"; return `${year}-${month}-${day} ${hour}:${minute}:${second}`; } const WEEKDAY_KEYS = [ "sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday", ] as const; export type AdminWeekdayKey = (typeof WEEKDAY_KEYS)[number]; export function adminWeekdayKeyForDate(date: Date = new Date()): AdminWeekdayKey { return WEEKDAY_KEYS[date.getDay()] ?? "sunday"; } /** * 仪表盘顶栏日期:数字日期 + i18n 星期(避免 Intl 在 ne 等语言下回退到系统中文)。 */ export function formatAdminCalendarToday(locale: AdminApiLocale, weekdayLabel: string): string { const d = new Date(); const y = d.getFullYear(); const m = String(d.getMonth() + 1).padStart(2, "0"); const day = String(d.getDate()).padStart(2, "0"); const datePart = locale === "en" ? `${m}/${day}/${y}` : `${y}-${m}-${day}`; return `${datePart} ${weekdayLabel}`; } const NAIVE_SCHEDULE_CLOCK_RE = /^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})$/; /** 浏览器本地时区短标签(如 CST、GMT+8),用于界面说明。 */ export function getAdminBrowserTimeZoneLabel(date = new Date()): string { try { const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone; const parts = new Intl.DateTimeFormat(undefined, { timeZone, timeZoneName: "short", }).formatToParts(date); return parts.find((part) => part.type === "timeZoneName")?.value ?? timeZone; } catch { return "Local"; } } /** * 将接口 ISO 8601 时间转为本地时区的 `YYYY-MM-DD HH:mm:ss`(表单预填、期号展示)。 */ export function isoToAdminLocalScheduleValue( iso: string | null | undefined, ): string { if (iso == null || iso === "") { return ""; } const ms = Date.parse(iso); if (Number.isNaN(ms)) { return ""; } return formatParts(new Date(ms)); } /** * 将本地时区下的计划时刻转为指定排期时区(默认 UTC)的 naive `YYYY-MM-DD HH:mm:ss`,供创建/编辑期号 API。 */ export function adminLocalScheduleValueToTimezoneNaive( clock: string, scheduleTimezone = "UTC", ): string { const trimmed = clock.trim(); if (trimmed === "") { return ""; } const match = NAIVE_SCHEDULE_CLOCK_RE.exec(trimmed); if (!match) { return trimmed; } const [, y, mo, d, h, mi, s] = match; const localMs = new Date( Number(y), Number(mo) - 1, Number(d), Number(h), Number(mi), Number(s), ).getTime(); if (Number.isNaN(localMs)) { return trimmed; } try { return formatParts(new Date(localMs), scheduleTimezone); } catch { return trimmed; } } /** * 将接口返回的 ISO 时间串格式化为浏览器本地时区下的 `YYYY-MM-DD HH:mm:ss`。 */ export function formatAdminInstant( iso: string | null | undefined, options: { locale: AdminApiLocale; }, ): string { void options; if (iso == null || iso === "") { return "—"; } const ms = Date.parse(iso); if (Number.isNaN(ms)) { return "—"; } return formatParts(new Date(ms)); } /** * 将接口返回的 ISO 时间串格式化到指定时区,便于并列展示多个地区的时间。 */ export function formatAdminInstantInTimeZone( iso: string | null | undefined, options: { locale: AdminApiLocale; timeZone: string; }, ): string { void options.locale; if (iso == null || iso === "") { return "—"; } const ms = Date.parse(iso); if (Number.isNaN(ms)) { return "—"; } try { return formatParts(new Date(ms), options.timeZone); } catch { return formatParts(new Date(ms)); } }