Skip to content

Instantly share code, notes, and snippets.

@ethaizone
Last active December 21, 2024 11:43
Show Gist options
  • Save ethaizone/6abb1d437dbe406fbed6 to your computer and use it in GitHub Desktop.
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.
// 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);
}
}
@ethaizone
Copy link
Author

@jskowron Maybe you should read this one first. https://en.wikipedia.org/wiki/Network_Time_Protocol#Clock_synchronization_algorithm
It created base on this algorithm to fix incorrect time setting in client side like clock resetted back to first second of unix timestamp and round trip network delay.

I'm not sure what you explain above consider about these cases or not but TBH. I'm not who create this implementation. It's from SO topic at first line of code. https://stackoverflow.com/a/15785110/1162506 You can check URL and ask author for that. I think that is better place to discuss for improvement.

In my case, I try to recheck it now and I still think it's still look acceptable to use except someone plan to update content inside wikipedia.

@jskowron
Copy link

@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:

t0 = clientTimestamp
t1 = serverTimestamp
t2 = serverTimestamp
t3 = nowTimeStamp

Then, quoting the equation from Wikipedia, the server time offset is:

\theta = ((t1 - t0) + (t2 - t3) ) / 2 = 
    = (serverTimestamp - clientTimestamp + serverTimestamp - nowTimeStamp) / 2 =
    = serverTimestamp  - clientTimestamp / 2 - nowTimeStamp / 2

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:

serverTimeOffset = (serverClientResponseDiffTime - responseTime) = 
    = serverClientResponseDiffTime - (serverClientRequestDiffTime - nowTimeStamp + clientTimestamp - serverClientResponseDiffTime ) / 2 =
    = serverClientResponseDiffTime - (serverTimestamp - clientTimestamp - nowTimeStamp + clientTimestamp - nowTimeStamp + serverTimestamp) / 2 =
    = (nowTimeStamp - serverTimestamp) - (2 serverTimestamp - 2 nowTimeStamp) / 2 =
    = 2 * (nowTimeStamp - serverTimestamp)  --- i.e., wrong

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.

@ethaizone
Copy link
Author

ethaizone commented Dec 21, 2024

@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