function syncTime() { const startTime = performance.now(); const xhr = new XMLHttpRequest(); xhr.open("GET", "/cdn-cgi/trace", true); xhr.onreadystatechange = () => { if (xhr.readyState === 4) { const endTime = performance.now(); const clientTimestamp = Date.now(); // Get the date from the cloudflare response const match = xhr.responseText.match(/^ts=([0-9\.]+)/m); if (!match) { console.error("Unable to get remote time"); return; } const serverTimestamp = Number.parseFloat(match[1]) * 1000; console.log("Client time:", clientTimestamp); console.log("Server time:", serverTimestamp); // Find the resource timing entry for our request const entry = performance .getEntriesByType("resource") .find((entry) => entry.name.endsWith("/cdn-cgi/trace") && entry.startTime >= startTime); // Calculate time offset if (entry) { // When we received the response headers is when the server timestamp was generated // So we need to adjust the current time by the amount of time that has passed since then const timeSinceResponse = endTime - entry.responseStart; // Client time when response headers were received const clientTimeAtResponse = clientTimestamp - timeSinceResponse; // Offset is difference between server time and client time at that moment g_TimeOffset = Math.floor(serverTimestamp - clientTimeAtResponse); console.log("Request time:", entry.responseStart - entry.requestStart); console.log("Time to response start:", entry.responseStart - entry.startTime); console.log("Time for response:", timeSinceResponse); } else { const roundTripTime = endTime - startTime; // Estimate that the server timestamp was captured halfway through the request // This is more accurate than assuming fixed travel times in both directions const estimatedCurrentServerTime = serverTimestamp + roundTripTime / 2; g_TimeOffset = Math.floor(estimatedCurrentServerTime - clientTimestamp); } console.log("Offset:", g_TimeOffset); console.log("Corrected time:", clientTimestamp + g_TimeOffset); console.log("Sanity check:", clientTimestamp + g_TimeOffset - serverTimestamp); } }; xhr.send(); }