fix(frontend/chat): normalize message timestamp units to prevent invalid far-future dates

This commit is contained in:
wenjie 2026-03-06 18:28:12 +08:00
parent 71cd03430a
commit ed729858d6
2 changed files with 38 additions and 5 deletions

View file

@ -16,6 +16,8 @@ export function AssistantMessage({
timestamp = "", timestamp = "",
}: AssistantMessageProps) { }: AssistantMessageProps) {
const [isCopied, setIsCopied] = useState(false) const [isCopied, setIsCopied] = useState(false)
const formattedTimestamp =
timestamp !== "" ? formatMessageTime(timestamp) : ""
const handleCopy = () => { const handleCopy = () => {
navigator.clipboard.writeText(content).then(() => { navigator.clipboard.writeText(content).then(() => {
@ -29,10 +31,10 @@ export function AssistantMessage({
<div className="text-muted-foreground flex items-center justify-between gap-2 px-1 text-xs opacity-70"> <div className="text-muted-foreground flex items-center justify-between gap-2 px-1 text-xs opacity-70">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<span>PicoClaw</span> <span>PicoClaw</span>
{timestamp && ( {formattedTimestamp && (
<> <>
<span className="opacity-50"></span> <span className="opacity-50"></span>
<span>{formatMessageTime(timestamp)}</span> <span>{formattedTimestamp}</span>
</> </>
)} )}
</div> </div>

View file

@ -11,7 +11,7 @@ interface PicoMessage {
type: string type: string
id?: string id?: string
session_id?: string session_id?: string
timestamp?: number timestamp?: number | string
payload?: Record<string, unknown> payload?: Record<string, unknown>
} }
@ -28,9 +28,37 @@ function generateSessionId(): string {
return crypto.randomUUID() return crypto.randomUUID()
} }
const UNIX_MS_THRESHOLD = 1e12
function normalizeUnixTimestamp(timestamp: number): number {
return timestamp < UNIX_MS_THRESHOLD ? timestamp * 1000 : timestamp
}
function parseTimestamp(dateRaw: number | string | Date) {
if (typeof dateRaw === "number") {
return dayjs(normalizeUnixTimestamp(dateRaw))
}
if (typeof dateRaw === "string") {
const trimmed = dateRaw.trim()
if (/^-?\d+(\.\d+)?$/.test(trimmed)) {
const numeric = Number(trimmed)
if (Number.isFinite(numeric)) {
return dayjs(normalizeUnixTimestamp(numeric))
}
}
return dayjs(trimmed)
}
return dayjs(dateRaw)
}
// Helper to format message timestamps // Helper to format message timestamps
export function formatMessageTime(dateRaw: number | string | Date): string { export function formatMessageTime(dateRaw: number | string | Date): string {
const date = dayjs(dateRaw) const date = parseTimestamp(dateRaw)
if (!date.isValid()) {
return ""
}
const now = dayjs() const now = dayjs()
const isToday = date.isSame(now, "day") const isToday = date.isSame(now, "day")
@ -75,7 +103,10 @@ export function usePicoChat() {
const content = (payload.content as string) || "" const content = (payload.content as string) || ""
const messageId = (payload.message_id as string) || `pico-${Date.now()}` const messageId = (payload.message_id as string) || `pico-${Date.now()}`
// Use provided timestamp or current time // Use provided timestamp or current time
const timestampRaw = msg.timestamp ? msg.timestamp * 1000 : Date.now() const timestampRaw =
msg.timestamp !== undefined && Number.isFinite(Number(msg.timestamp))
? normalizeUnixTimestamp(Number(msg.timestamp))
: Date.now()
setMessages((prev) => [ setMessages((prev) => [
...prev, ...prev,