Я обнаружил, что алгоритм @ mehdi-yeganeh выше не дал мне полезных результатов, но идея ясна: использовать алгоритм NTP (или, по крайней мере, его слабую версию) для синхронизации сервера и клиента часы.
Это моя последняя реализация, она использует заголовки ответа сервера, если они доступны, для дополнительной точности (пожалуйста, исправьте меня, если я ошибаюсь, мои собственные тесты говорят, что это довольно точно).
сторона браузера (javascript):
// the NTP algorithm
// t0 is the client's timestamp of the request packet transmission,
// t1 is the server's timestamp of the request packet reception,
// t2 is the server's timestamp of the response packet transmission and
// t3 is the client's timestamp of the response packet reception.
function ntp(t0, t1, t2, t3) {
return {
roundtripdelay: (t3 - t0) - (t2 - t1),
offset: ((t1 - t0) + (t2 - t3)) / 2
};
}
// calculate the difference in seconds between the client and server clocks, use
// the NTP algorithm, see: http://en.wikipedia.org/wiki/Network_Time_Protocol#Clock_synchronization_algorithm
var t0 = (new Date()).valueOf();
$.ajax({
url: '/ntp',
success: function(servertime, text, resp) {
// NOTE: t2 isn't entirely accurate because we're assuming that the server spends 0ms on processing.
// (t1 isn't accurate either, as there's bound to have been some processing before that, but we can't avoid that)
var t1 = servertime,
t2 = servertime,
t3 = (new Date()).valueOf();
// we can get a more accurate version of t2 if the server's response
// contains a Date header, which it generally will.
// EDIT: as @Ariel rightly notes, the HTTP Date header only has
// second resolution, thus using it will actually make the calculated
// result worse. For higher accuracy, one would thus have to
// return an extra header with a higher-resolution time. This
// could be done with nginx for example:
// http://nginx.org/en/docs/http/ngx_http_core_module.html
// var date = resp.getResponseHeader("Date");
// if (date) {
// t2 = (new Date(date)).valueOf();
// }
var c = ntp(t0, t1, t2, t3);
// log the calculated value rtt and time driff so we can manually verify if they make sense
console.log("NTP delay:", c.roundtripdelay, "NTP offset:", c.offset, "corrected: ", (new Date(t3 + c.offset)));
}
});
на стороне сервера (php, но может быть любым):
Ваш сервер на маршруте 'GET / ntp' должен вернуть что-то вроде:
echo (string) round(microtime(true) * 1000);
Если у вас PHP> 5.4, вы можете сохранить вызов microtime () и сделать его немного более точным с помощью:
echo (string) round($_SERVER['REQUEST_TIME_FLOAT'] * 1000);
Примечание
Этот способ может рассматриваться как разновидность гетто, есть и другие ответы о переполнении стека, которые могут помочь вам найти лучшее решение: