diff --git a/src/utils/business/format.ts b/src/utils/business/format.ts index e289329..d8fb803 100644 --- a/src/utils/business/format.ts +++ b/src/utils/business/format.ts @@ -167,41 +167,101 @@ export function formatDateRange(startDate: string, endDate: string): string { /** * 格式化日期时间 - * @param date 日期字符串或时间戳(支持Unix时间戳秒,自动检测) + * 规则: + * 1. 带明确日期文本的字符串按原始时间文本展示,不跟随客户端本地时区转换 + * 2. 纯数字时间戳支持秒/毫秒自动识别 + * 3. Date/时间戳这类“只有绝对时刻、没有原始展示时区”的值,按 UTC 稳定输出 + * @param date 日期字符串或时间戳(支持 Unix 时间戳秒/毫秒,自动检测) * @param format 格式化模板,默认 'YYYY-MM-DD HH:mm:ss' * @returns 格式化后的日期字符串 */ +type DateTimeParts = { + year: string + month: string + day: string + hours: string + minutes: string + seconds: string +} + +const DATE_TIME_TEXT_REGEX = + /^(\d{4})-(\d{2})-(\d{2})(?:[T\s](\d{2})(?::(\d{2})(?::(\d{2})(?:\.\d+)?)?)?)?(?:Z|[+-]\d{2}:?\d{2})?$/ + +function formatDateTimeParts(parts: DateTimeParts, format: string): string { + return format + .replace('YYYY', parts.year) + .replace('MM', parts.month) + .replace('DD', parts.day) + .replace('HH', parts.hours) + .replace('mm', parts.minutes) + .replace('ss', parts.seconds) +} + +function parseDateTimeText(dateText: string): DateTimeParts | null { + const match = dateText.trim().match(DATE_TIME_TEXT_REGEX) + if (!match) return null + + return { + year: match[1], + month: match[2], + day: match[3], + hours: match[4] ?? '00', + minutes: match[5] ?? '00', + seconds: match[6] ?? '00' + } +} + +function normalizeTimestamp(timestamp: number): number { + const absoluteValue = Math.abs(timestamp) + + // 10 位左右按秒处理,13 位左右按毫秒处理 + if (absoluteValue >= 1e9 && absoluteValue < 1e12) { + return timestamp * 1000 + } + + return timestamp +} + +function getUtcDateTimeParts(date: Date): DateTimeParts { + return { + year: String(date.getUTCFullYear()), + month: String(date.getUTCMonth() + 1).padStart(2, '0'), + day: String(date.getUTCDate()).padStart(2, '0'), + hours: String(date.getUTCHours()).padStart(2, '0'), + minutes: String(date.getUTCMinutes()).padStart(2, '0'), + seconds: String(date.getUTCSeconds()).padStart(2, '0') + } +} + export function formatDateTime( date: string | number | Date | undefined | null, format: string = 'YYYY-MM-DD HH:mm:ss' ): string { - if (!date) return '-' + if (date === undefined || date === null || date === '') return '-' - let d: Date - const numDate = typeof date === 'string' ? Number(date) : date - if (typeof numDate === 'number' && numDate > 1000000000 && numDate < 2000000000) { - d = new Date(numDate * 1000) - } else { - d = new Date(date) + if (typeof date === 'string') { + const trimmedDate = date.trim() + if (!trimmedDate) return '-' + + const parsedText = parseDateTimeText(trimmedDate) + if (parsedText) { + return formatDateTimeParts(parsedText, format) + } + + const numericTimestamp = Number(trimmedDate) + if (!Number.isNaN(numericTimestamp)) { + const parsedDate = new Date(normalizeTimestamp(numericTimestamp)) + if (Number.isNaN(parsedDate.getTime())) return '-' + return formatDateTimeParts(getUtcDateTimeParts(parsedDate), format) + } } - if (isNaN(d.getTime())) return '-' + const parsedDate = new Date( + typeof date === 'number' ? normalizeTimestamp(date) : date + ) + if (Number.isNaN(parsedDate.getTime())) return '-' - // 使用 UTC 方法获取时间 - const year = d.getUTCFullYear() - const month = String(d.getUTCMonth() + 1).padStart(2, '0') - const day = String(d.getUTCDate()).padStart(2, '0') - const hours = String(d.getUTCHours()).padStart(2, '0') - const minutes = String(d.getUTCMinutes()).padStart(2, '0') - const seconds = String(d.getUTCSeconds()).padStart(2, '0') - - return format - .replace('YYYY', String(year)) - .replace('MM', month) - .replace('DD', day) - .replace('HH', hours) - .replace('mm', minutes) - .replace('ss', seconds) + return formatDateTimeParts(getUtcDateTimeParts(parsedDate), format) }