You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

64 lines
1.9 KiB

2 months ago
/** 业务时间统一按北京时间(Asia/Shanghai,无夏令时)处理。 */
const SHANGHAI = 'Asia/Shanghai'
function pad(n: number): string {
return String(n).padStart(2, '0')
}
/**
* API ISO / 任意可解析时间 → 日期时间选择器模型(北京时间 `YYYY-MM-DD HH:mm:ss`)。
*/
export function toShanghaiPickerValue(iso: string | null | undefined): string | null {
if (!iso) return null
const d = new Date(iso)
if (Number.isNaN(d.getTime())) return null
const parts = new Intl.DateTimeFormat('en-GB', {
timeZone: SHANGHAI,
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hourCycle: 'h23',
}).formatToParts(d)
const get = (type: Intl.DateTimeFormatPartTypes): string =>
parts.find((p) => p.type === type)?.value ?? '00'
const hour = get('hour') === '24' ? '00' : get('hour')
return `${get('year')}-${get('month')}-${get('day')} ${hour}:${get('minute')}:${get('second')}`
}
/**
* 选择器值(按北京时间理解)→ API ISO8601(带偏移,避免被当成 UTC)。
*/
export function fromShanghaiPickerValue(s: string | null | undefined): string | null {
const t = (s ?? '').trim()
if (!t) return null
const m = t.match(/^(\d{4})-(\d{2})-(\d{2})[ T](\d{2}):(\d{2})(?::(\d{2}))?/)
if (!m) return null
const [, y, mo, d, h, mi, sec = '00'] = m
return `${y}-${mo}-${d}T${h}:${mi}:${sec}+08:00`
}
/** 列表/详情展示用北京时间。 */
export function formatShanghaiDateTime(iso: string | null | undefined, empty = '—'): string {
const v = toShanghaiPickerValue(iso)
if (!v) return empty
return v.replace(/:\d{2}$/, '') // YYYY-MM-DD HH:mm
}
export function formatShanghaiDateTimeFull(iso: string | null | undefined, empty = '—'): string {
return toShanghaiPickerValue(iso) ?? empty
}
/** 调试/校验用 */
export function shanghaiOffsetLabel(): string {
return `${SHANGHAI} (UTC+8)`
}
export { pad }