Last active
December 21, 2024 11:43
-
-
Save ethaizone/6abb1d437dbe406fbed6 to your computer and use it in GitHub Desktop.
Sync server time to client browser with JS. Implement follow Network Time Protocol.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Original from http://stackoverflow.com/questions/1638337/the-best-way-to-synchronize-client-side-javascript-clock-with-server-date | |
// Improved by community and @jskowron | |
// Synchronize client-side clock with server time using an approximation of NTP | |
let serverTimeOffset = null; | |
function getServerTime(callback) { | |
if (serverTimeOffset === null) { | |
const scripts = document.getElementsByTagName("script"); | |
const currentScriptURL = scripts[scripts.length - 1].src; | |
const clientTimestamp = Date.now(); // Client timestamp before request | |
const xhr = new XMLHttpRequest(); | |
xhr.open("HEAD", `${currentScriptURL}?noCache=${clientTimestamp}`, true); | |
xhr.onload = function () { | |
if (xhr.readyState === 4 && xhr.status === 200) { | |
const serverDateHeader = xhr.getResponseHeader("Date"); | |
if (serverDateHeader) { | |
const serverTimestamp = Date.parse(serverDateHeader); | |
const nowTimeStamp = Date.now(); // Timestamp after response | |
// Calculate offset | |
serverTimeOffset = serverTimestamp - ((clientTimestamp + nowTimeStamp) / 2); | |
// Return synchronized time | |
const syncedDate = new Date(Date.now() + serverTimeOffset); | |
if (callback) callback(syncedDate); | |
} else { | |
console.error("Server did not return a valid Date header."); | |
} | |
} else { | |
console.error("Failed to fetch server time:", xhr.statusText); | |
} | |
}; | |
xhr.onerror = function () { | |
console.error("Network error while fetching server time."); | |
}; | |
xhr.send(null); | |
} else { | |
// Return cached offset-adjusted time | |
const syncedDate = new Date(Date.now() + serverTimeOffset); | |
if (callback) callback(syncedDate); | |
} | |
} |
@jskowron Thank you for be patient with me. Actually at that time I'm very busy until point that my brain isn't working properly.
Today I read your message carefully again and I agreed with you so I improved code.
Hope that next year will be happy years for everyone and thank you for who helped to contribute in this little code snippet.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@ethaizone
First, I have read the Wikipedia, but regardless, it does not change the fact that your code returns wrong values. Just test it. Set your machine clock to the wrong time by 60 seconds, while keeping the server time right, and observe that your code returns ~120 seconds instead of 60.
And now, returning to the theoretical argument: in Wikipedia article the times are t0, t1, t3, t4. These can be translated in your code to:
Then, quoting the equation from Wikipedia, the server time offset is:
Hence, this is exaclty what is in my final equation from the previous post.
However, while citing the exact equations from your code, the result is this:
First, it does not use clientTimestamp at all. Second, it returns the difference doubled.
Just change the serverTimeOffset equation in your code to:
serverTimeOffset = serverTimestamp - (clientTimestamp + nowTimeStamp) / 2
and everything will be good.
Also, look at the comments of the Stack Overflow answer you have linked to. They say the equation is wrong as well.
Regardless, you don't have to believe them nor me, just check.