import { formatDateTime, formatTime } from "@/shared/utils/date-time"; const zonedTime = /(? 23 || Number(minutes) > 59 || Number(seconds) >= 60) return null; const value = Date.parse(`${day}T${clock}${zone}`); return Number.isFinite(value) ? value : null; } /** Display historical report prose without changing stored facts, IDs, URLs or code. */ export function formatDisplayText(text: string | null | undefined): string { if (!text) return text ?? ""; return text.split(/(https?:\/\/\S+|tjwater:\/\/\S+|`[^`]*`)/g).map((part, index) => { if (index % 2) return part; return part.replace(utcRange, (original, day: string | undefined, start: string, end: string) => { const startAt = parseTime(day ?? "2000-01-01", start); let endAt = parseTime(day ?? "2000-01-01", end); if (startAt === null || endAt === null) return original; if (endAt < startAt) endAt += 24 * 60 * 60_000; const dateOptions = { year: "numeric", month: "numeric", day: "numeric" } as const; const crossesDay = formatDateTime(startAt, dateOptions) !== formatDateTime(endAt, dateOptions); const startLabel = day ? formatDateTime(startAt, { ...dateOptions, hour: "2-digit", minute: "2-digit" }) : formatTime(startAt); const endLabel = crossesDay && day ? formatDateTime(endAt, { ...dateOptions, hour: "2-digit", minute: "2-digit" }) : `${crossesDay ? "次日 " : ""}${formatTime(endAt)}`; return `${startLabel}–${endLabel}(UTC+8)`; }).replace(zonedTime, (original, day: string, clock: string, zone: string) => { const value = parseTime(day, clock, zone); return value === null ? original : `${formatDateTime(value)}(UTC+8)`; }).replace(utcPoint, (original, day: string | undefined, clock: string) => { const value = parseTime(day ?? "2000-01-01", clock); if (value === null) return original; return `${day ? formatDateTime(value) : formatTime(value)}(UTC+8)`; }); }).join(""); } /** Chart labels use the display clock without repeating its zone to readers. */ export function formatChartDisplayText(value: string): string { return formatDisplayText(value) .replace(/[((]\s*(?:上海时间|Asia\/Shanghai|UTC\s*\+\s*8(?::00)?)\s*[))]/g, "") .replace(/(?:上海时间|Asia\/Shanghai|UTC\s*\+\s*8(?::00)?)(?![\w:+-])\s*/g, "") .replace(/[,,]\s*([))])/g, "$1") .trim(); } type ChartDisplayFields = { title: string; x_axis_name?: string; y_axis_name?: string; x_data: string[]; series: Array<{ name: string }>; }; /** Preserve values and original sample timestamps; convert only visible labels. */ export function withChartDisplayTimes(chart: T): T { const utcAxis = /UTC(?:\s*\+?0(?::00)?)?(?![\w+:-])/.test(chart.x_axis_name ?? ""); const bareTime = /^(?:\d{4}-\d{2}-\d{2}[T ])?\d{2}:\d{2}(?::\d{2}(?:\.\d{1,9})?)?$/; return { ...chart, title: formatChartDisplayText(chart.title), ...(chart.x_axis_name ? { x_axis_name: formatChartDisplayText(chart.x_axis_name.replace(/UTC(?:\s*\+?0(?::00)?)?(?![\w+:-])/g, "UTC+8")) || "时间" } : {}), x_data: chart.x_data.map((label) => formatChartDisplayText(utcAxis && bareTime.test(label) ? `${label} UTC` : label)), series: chart.series.map((series) => ({ ...series, name: formatChartDisplayText(series.name) })) }; }