I\'m looking for a way to synchronize time between clients with a good precision (let\'s say 0.5 seconds at least).
I exclude using jsontime or exploiting timestamp
If you just want to sync the timeclock on several computers, NTP themselves recommend setting up your own timeserver.
Set up an api endpoint on your server i.e.
http://localhost:3000/api/time
returns:
status: 200,
body: { time: {{currentTime}} }
How this is done will depend on the backend language that you're using.
Given such an endpoint, this JS snippet I threw together will
var offsets = [];
var counter = 0;
var maxTimes = 10;
var beforeTime = null;
// get average
var mean = function(array) {
var sum = 0;
array.forEach(function (value) {
sum += value;
});
return sum/array.length;
}
var getTimeDiff = function() {
beforeTime = Date.now();
$.ajax('/api/time', {
type: 'GET',
success: function(response) {
var now, timeDiff, serverTime, offset;
counter++;
// Get offset
now = Date.now();
timeDiff = (now-beforeTime)/2;
serverTime = response.data.time-timeDiff;
offset = now-serverTime;
console.log(offset);
// Push to array
offsets.push(offset)
if (counter < maxTimes) {
// Repeat
getTimeDiff();
} else {
var averageOffset = mean(offsets);
console.log("average offset:" + averageOffset);
}
}
});
}
// populate 'offsets' array and return average offsets
getTimeDiff();
You can use this computed offset (just add it to local time), to determine a common "universal" time from each client's context.
Resort to the good old ICMP Timestamp message scheme. It's fairly trivial to implement in JavaScript and PHP.
Here's an implementation of this scheme using JavaScript and PHP:
// browser.js
var request = new XMLHttpRequest();
request.onreadystatechange = readystatechangehandler;
request.open("POST", "http://www.example.com/sync.php", true);
request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
request.send("original=" + (new Date).getTime());
function readystatechangehandler() {
var returned = (new Date).getTime();
if (request.readyState === 4 && request.status === 200) {
var timestamp = request.responseText.split('|');
var original = + timestamp[0];
var receive = + timestamp[1];
var transmit = + timestamp[2];
var sending = receive - original;
var receiving = returned - transmit;
var roundtrip = sending + receiving;
var oneway = roundtrip / 2;
var difference = sending - oneway; // this is what you want
// so the server time will be client time + difference
}
}
Now for the sync.php
code:
<?php
$receive = round(microtime(true) * 1000);
echo $_POST["original"] . '|';
echo $receive . '|';
echo round(microtime(true) * 1000);
?>
I haven't tested the above code, but it should work.
Note: The following method will accurately calculate the time difference between the client and the server provided that actual time to send and receive messages is the same or approximately the same. Consider the following scenario:
Time Client Server
-------- -------- --------
Original 0 2
Receive 3 5
Transmit 4 6
Returned 7 9
Using this data, we can calculate:
Sending = Receive - Original = 5 - 0 = 5
Receiving = Returned - Transmit = 7 - 6 = 1
Roundtrip = Sending + Receiving = 5 + 1 = 6
As you can see from above, the sending and receiving times are calculated incorrectly depending upon how much the client and server are off sync. However, the roundtrip time will always be correct because we are first adding two units (receive + original), and then subtracting two units (returned - transmit).
If we assume that the oneway time is always half of the roundtrip time (i.e. the time to transmit is the time to receive, then we can easily calculate the time difference as follows):
Oneway = Roundtrip / 2 = 6 / 2 = 3
Difference = Sending - Oneway = 5 - 3 = 2
As you can see, we accurately calculated the time difference as 2 units. The equation for time difference is always sending - oneway
time. However, the accuracy of this equation depends upon how accurately you calculate the oneway time. If the actual time to send and receive the messages is not equal or approximately equal, you'll need to find some other way to calculate the one way time. However, for your purposes this should suffice.