diff --git a/app/dashboard/page.tsx b/app/dashboard/page.tsx
index 6db6ff7..4d58c95 100644
--- a/app/dashboard/page.tsx
+++ b/app/dashboard/page.tsx
@@ -3,6 +3,7 @@ import { createSupabaseServerClient } from '@/lib/supabase/server';
import { getSupabaseAdmin } from '@/lib/supabase/admin';
import { ClaimForm } from './claim-form';
import { TokenReveal } from './token-reveal';
+import { formatDate } from '@/lib/format';
export const dynamic = 'force-dynamic';
@@ -92,9 +93,7 @@ export default async function DashboardPage() {
Last seen
- {tunnel.last_seen_at
- ? new Date(tunnel.last_seen_at).toLocaleString()
- : 'never'}
+ {tunnel.last_seen_at ? formatDate(tunnel.last_seen_at) : 'never'}
diff --git a/lib/format.ts b/lib/format.ts
index ea11eea..3c6b594 100644
--- a/lib/format.ts
+++ b/lib/format.ts
@@ -11,9 +11,34 @@ export function formatBytes(n: number): string {
return `${v.toFixed(2)} ${units[i]}`;
}
-export function formatDate(s: string | null | undefined): string {
+function pad(n: number): string {
+ return n < 10 ? `0${n}` : `${n}`;
+}
+
+// Deterministic, timezone-independent date+time formatter.
+// Uses UTC getters so the server (UTC) and client (local TZ) render
+// byte-identical text, avoiding React hydration mismatches (error #425).
+// Output format: "YYYY-MM-DD HH:MM UTC".
+export function formatDateTime(s: string | null | undefined): string {
if (!s) return '—';
const d = new Date(s);
if (Number.isNaN(d.getTime())) return '—';
- return d.toLocaleString();
+ const date = `${d.getUTCFullYear()}-${pad(d.getUTCMonth() + 1)}-${pad(
+ d.getUTCDate(),
+ )}`;
+ const time = `${pad(d.getUTCHours())}:${pad(d.getUTCMinutes())}`;
+ return `${date} ${time} UTC`;
}
+
+// Date-only deterministic UTC formatter. Output format: "YYYY-MM-DD UTC".
+export function formatDateOnly(s: string | null | undefined): string {
+ if (!s) return '—';
+ const d = new Date(s);
+ if (Number.isNaN(d.getTime())) return '—';
+ return `${d.getUTCFullYear()}-${pad(d.getUTCMonth() + 1)}-${pad(
+ d.getUTCDate(),
+ )} UTC`;
+}
+
+// Backwards-compatible alias: existing call sites render date+time.
+export const formatDate = formatDateTime;