fix(ui): 修复全局时区偏移问题与日期控件手动输入崩溃 Bug

This commit is contained in:
2026-04-28 16:19:41 +08:00
parent 110e75f0a1
commit bf57002313
6 changed files with 135 additions and 5 deletions
+73
View File
@@ -1,6 +1,79 @@
import { type ClassValue, clsx } from "clsx"
import { twMerge } from "tailwind-merge"
import { toZonedTime, fromZonedTime } from "date-fns-tz"
const TIMEZONE = "Asia/Shanghai"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
export function nowInShanghai(): Date {
const now = new Date()
const utcStr = now.toLocaleString("en-US", { timeZone: "UTC" })
const utcDate = new Date(utcStr)
const shanghaiOffset = getTimezoneOffset("Asia/Shanghai")
const utcOffset = 0
return new Date(utcDate.getTime() + (shanghaiOffset - utcOffset))
}
export function formatDateForDatetimeLocal(date: Date): string {
const zoned = toZonedTime(date, TIMEZONE)
const year = zoned.getFullYear()
const month = String(zoned.getMonth() + 1).padStart(2, "0")
const day = String(zoned.getDate()).padStart(2, "0")
const hours = String(zoned.getHours()).padStart(2, "0")
const minutes = String(zoned.getMinutes()).padStart(2, "0")
return `${year}-${month}-${day}T${hours}:${minutes}`
}
function getTimezoneOffset(timezone: string): number {
const formatter = new Intl.DateTimeFormat("en-US", {
timeZone: timezone,
timeZoneName: "longOffset",
})
const parts = formatter.formatToParts(new Date())
const tzPart = parts.find((p) => p.type === "timeZoneName")
if (!tzPart) return 0
const match = tzPart.value.match(/([+-]?\d{1,2}):?(\d{2})/)
if (!match) return 0
const hours = parseInt(match[1], 10)
const minutes = parseInt(match[2], 10)
return (hours * 60 + minutes) * 60 * 1000
}
export function parseDateTimeLocalToUTC(value: string): Date | null {
if (!value) return null
const zoned = fromZonedTime(value, TIMEZONE)
return zoned
}
export function parseDateTimeLocalToUTC_v2(year: number, month: number, day: number, hours: number, minutes: number): Date {
const shanghaiTime = new Date()
shanghaiTime.setFullYear(year)
shanghaiTime.setMonth(month)
shanghaiTime.setDate(day)
shanghaiTime.setHours(hours, minutes, 0, 0)
const shanghaiOffsetMs = getShanghaiOffsetMs(shanghaiTime)
const utcMs = shanghaiTime.getTime() - shanghaiOffsetMs
return new Date(utcMs)
}
function getShanghaiOffsetMs(date: Date): number {
const formatter = new Intl.DateTimeFormat("en-US", {
timeZone: "Asia/Shanghai",
timeZoneName: "short",
})
const parts = formatter.formatToParts(date)
const tzPart = parts.find((p) => p.type === "timeZoneName")
if (!tzPart) return 8 * 60 * 60 * 1000
const match = tzPart.value.match(/([+-])(\d{2}):?(\d{2})/)
if (!match) return 8 * 60 * 60 * 1000
const sign = match[1] === "+" ? 1 : -1
const offsetHours = parseInt(match[2], 10)
const offsetMinutes = parseInt(match[3], 10)
return sign * (offsetHours * 60 + offsetMinutes) * 60 * 1000
}