Fixed the timeskew second display

This commit is contained in:
Nik Afiq 2025-11-11 23:16:21 +09:00
parent 8e157cc3ad
commit 2023e12658

View File

@ -11,16 +11,14 @@ import { API_ENDPOINT } from "../api/endpoint";
* Positive offset => client is AHEAD by that many ms.
*/
const TIME_URL_ENDPOINT = API_ENDPOINT.v1.TIME;
export function useTimeSkew(opts?: {
endpoint?: string; // e.g., "/api/time" or `${import.meta.env.BASE_URL}api/time`
intervalMs?: number; // how often to recheck; default 5 min
samples?: number; // take N measurements and median; default 1
enabled?: boolean; // allow turning off; default true
intervalMs?: number;
samples?: number;
enabled?: boolean;
}) {
const {
intervalMs = 5 * 60_000,
samples = 1,
samples = 3, // ✅ small median sampling
enabled = true,
} = opts || {};
@ -31,15 +29,24 @@ export function useTimeSkew(opts?: {
const measureOnce = async () => {
const t0 = Date.now();
const res = await fetch(TIME_URL_ENDPOINT, { cache: "no-store" });
const res = await fetch(TIME_URL_ENDPOINT, {
cache: "no-store",
headers: { Accept: "application/json" },
});
const t1 = Date.now();
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
if (typeof data?.now !== "number") {
// Robust parsing: number or string, seconds or ms
const data = await res.json() as any;
let serverMs = typeof data?.now === "number" ? data.now : Number(data?.now);
if (!Number.isFinite(serverMs)) {
throw new Error("Bad time payload (expecting { now: number })");
}
const serverMs = data.now;
// Heuristic: if it's too small to be ms epoch, treat as seconds
if (serverMs < 1e12) { // ~Sat Sep 09 2001 ms epoch
serverMs = serverMs * 1000; // seconds -> ms
}
const rtt = t1 - t0;
const offset = Math.round(((t0 + t1) / 2) - serverMs);
return { offset, rtt };
@ -48,20 +55,14 @@ export function useTimeSkew(opts?: {
const measure = async () => {
try {
setError(null);
if (samples <= 1) {
const { offset, rtt } = await measureOnce();
setSkewMs(offset);
setRttMs(rtt);
return;
}
// multiple samples → median offset, min RTT (more stable)
const offsets: number[] = [];
const rtts: number[] = [];
for (let i = 0; i < samples; i++) {
const N = Math.max(1, samples);
for (let i = 0; i < N; i++) {
const { offset, rtt } = await measureOnce();
offsets.push(offset);
rtts.push(rtt);
if (i < samples - 1) await new Promise(r => setTimeout(r, 120)); // tiny gap
if (i + 1 < N) await new Promise(r => setTimeout(r, 120));
}
offsets.sort((a, b) => a - b);
const median = offsets[Math.floor(offsets.length / 2)];
@ -75,14 +76,11 @@ export function useTimeSkew(opts?: {
useEffect(() => {
if (!enabled) return;
measure(); // initial check
measure(); // initial
if (intervalMs > 0) {
timerRef.current = window.setInterval(measure, intervalMs);
}
return () => {
if (timerRef.current) window.clearInterval(timerRef.current);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
return () => { if (timerRef.current) window.clearInterval(timerRef.current); };
}, [intervalMs, samples, enabled]);
return { skewMs, rttMs, error, recheck: measure };