diff --git a/app/components/Header.tsx b/app/components/Header.tsx index dc80268..fdcf78b 100644 --- a/app/components/Header.tsx +++ b/app/components/Header.tsx @@ -8,26 +8,18 @@ import { Github as GithubIcon } from "./icons/Github"; import { AuthNav } from "./AuthNav"; import { BrandMark } from "./BrandMark"; import { LiveEditionLabel } from "./LiveEditionLabel"; +import { LiveDate } from "./LiveDate"; export async function Header() { const t = await getTranslations("header"); - const now = new Date(); - const editionTimestampMs = now.getTime(); - const formattedDate = now.toLocaleDateString("en-US", { - month: "long", - day: "numeric", - year: "numeric", - timeZone: "Australia/Sydney", - }); + const editionTimestampMs = new Date().getTime(); return (
-
- {formattedDate} -
+
diff --git a/app/components/LiveDate.tsx b/app/components/LiveDate.tsx new file mode 100644 index 0000000..9b0339f --- /dev/null +++ b/app/components/LiveDate.tsx @@ -0,0 +1,40 @@ +"use client"; + +import { useEffect, useState } from "react"; + +type LiveDateProps = { + // milliseconds since epoch + initialTimestamp: number; +}; + +// 固定时区(悉尼)保证服务端/客户端首帧一致,避免 hydration mismatch; +// 挂载后每秒刷新,避免 SSG/ISR 把 build 时刻的日期冻住。 +const TIMEZONE = "Australia/Sydney"; + +function formatDate(ms: number) { + return new Date(ms).toLocaleDateString("en-US", { + month: "long", + day: "numeric", + year: "numeric", + timeZone: TIMEZONE, + }); +} + +export function LiveDate({ initialTimestamp }: LiveDateProps) { + const [formattedDate, setFormattedDate] = useState(() => + formatDate(initialTimestamp), + ); + + useEffect(() => { + const update = () => setFormattedDate(formatDate(Date.now())); + update(); + const intervalId = setInterval(update, 1000); + return () => clearInterval(intervalId); + }, []); + + return ( +
+ {formattedDate} +
+ ); +}