unify Shanghai time across Semi UI workspaces

This commit is contained in:
lingniu
2026-07-18 10:51:55 +08:00
parent bba6a621aa
commit 5805635ccf
9 changed files with 116 additions and 43 deletions

View File

@@ -1,5 +1,5 @@
import { describe, expect, it } from 'vitest';
import { formatZhNumber } from './formatters';
import { formatShanghaiDateTime, formatZhNumber } from './formatters';
describe('formatZhNumber', () => {
it('formats Chinese locale numbers with independent precision caches', () => {
@@ -13,3 +13,17 @@ describe('formatZhNumber', () => {
expect(formatZhNumber(Number.NaN, 2)).toBe('NaN');
});
});
describe('formatShanghaiDateTime', () => {
it('renders explicit timestamps in Shanghai and preserves local business values', () => {
expect(formatShanghaiDateTime('2026-07-18T02:33:00Z')).toBe('2026-07-18 10:33:00');
expect(formatShanghaiDateTime('2026-07-18T02:33:00+08:00')).toBe('2026-07-18 02:33:00');
expect(formatShanghaiDateTime('2026-07-18 02:33:00')).toBe('2026-07-18 02:33:00');
});
it('converts timezone-less UTC service timestamps only when the source contract says UTC', () => {
expect(formatShanghaiDateTime('2026-07-18 02:33:00', { sourceZone: 'utc' })).toBe('2026-07-18 10:33:00');
expect(formatShanghaiDateTime('', { sourceZone: 'utc' })).toBe('—');
expect(formatShanghaiDateTime(undefined, { empty: '尚未上报' })).toBe('尚未上报');
});
});

View File

@@ -1,4 +1,16 @@
const zhNumberFormatters = new Map<number, Intl.NumberFormat>();
const shanghaiDateTimeFormatter = new Intl.DateTimeFormat('sv-SE', {
timeZone: 'Asia/Shanghai',
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
});
export type DateTimeSourceZone = 'preserve' | 'utc';
export function formatZhNumber(value: number, maximumFractionDigits = 0) {
let formatter = zhNumberFormatters.get(maximumFractionDigits);
@@ -8,3 +20,18 @@ export function formatZhNumber(value: number, maximumFractionDigits = 0) {
}
return formatter.format(value);
}
export function formatShanghaiDateTime(
value?: string,
{ empty = '—', sourceZone = 'preserve' }: { empty?: string; sourceZone?: DateTimeSourceZone } = {}
) {
if (!value?.trim()) return empty;
const source = value.trim();
const fallback = source.replace('T', ' ').replace(/\.\d{1,9}/, '').replace(/Z$/, '').slice(0, 19);
const hasExplicitZone = /(?:Z|[+-]\d{2}:?\d{2})$/i.test(source);
if (!hasExplicitZone && sourceZone === 'preserve') return fallback;
const normalized = hasExplicitZone ? source : `${source.replace(' ', 'T')}Z`;
const parsed = new Date(normalized);
if (!Number.isFinite(parsed.getTime())) return fallback || empty;
return shanghaiDateTimeFormatter.format(parsed).replace('T', ' ');
}