客户端 javascript 时钟与服务器日期同步的最佳方法

我有一个任务显示数字时钟(分钟精度)在 HTML 页面在一些固定的时区(MSK 或 MSD-取决于当前日期)。我希望避免依赖客户端系统时钟,因此需要与服务器进行一些同步。HTTP 服务器在每个响应中发送 Date 头,这样我们就可以向站点的任何 URL 发送 AJAX GET 或 HEAD 请求来获取服务器日期,计算与客户端日期的差异,并在使用 setTimeout ()更新时钟时使用它。 还有其他问题存在: 时区切换到日光设置,延迟占用了非常慢的连接。

对这个任务有什么最简单的解决方法吗? 我希望不用服务器端编程就能解决。

79595 次浏览

如果你只要求精确到分钟,我只会每隔30秒左右请求服务器更新一次。完全不依赖客户端时间,而是使用他们的系统时钟来保持更新之间时钟的准确性。我想你已经回答了自己的问题?

如果我们能更好地理解你到底想做什么,会有帮助的。

如果您只是想要一个时钟来显示服务器上的时间,然后将其调整到特定的时区,那么可以在客户端使用偏移量进行此操作。在适用的时区处理 DST,也可以使用从服务器收到的日期。如果要确定延迟,可能需要服务器上的一个小脚本来计算差异。但如上所述,这将有助于更好地理解问题。如果精度仅限于分钟,那么延迟似乎就不那么重要了。

我将在初始化期间与 Internet 时间服务器同步的时间。

Http://tf.nist.gov/service/its.htm

如果要使用 ajax,应该记住 readyState = = 2和 readyState = = 3之间的客户机时间,因为服务器时间将在接收到请求和准备响应之间设置

这两个 Javascript 函数应该可以为您解决这个问题。

var offset = 0;
function calcOffset() {
var xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
xmlhttp.open("GET", "http://stackoverflow.com/", false);
xmlhttp.send();


var dateStr = xmlhttp.getResponseHeader('Date');
var serverTimeMillisGMT = Date.parse(new Date(Date.parse(dateStr)).toUTCString());
var localMillisUTC = Date.parse(new Date().toUTCString());


offset = serverTimeMillisGMT -  localMillisUTC;
}


function getServerTime() {
var date = new Date();


date.setTime(date.getTime() + offset);


return date;
}

编辑: 已删除“。替换(/^ (. )[ s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s/,“ $1”)”。

CalcOffset ()计算服务器时间的偏移量并补偿 GMT/UTC。

GetServerTime ()获取与服务器匹配的本地时间偏移量,使用本地时区。

如果 calcOffset ()执行时间较长,则可能会损失一些秒精度。也许行刑时间可以考虑在内。

如果你担心当地时间或者服务器时间改变到夏令时或者从夏令时改变到夏令时,你可以在每个时钟小时之后重新计算一下,系统会补偿夏令时的变化。 可能需要等到本地时钟和服务器时钟都过去一个小时之后。

这个例子只适用于 IE,因为“ Msxml2.XMLHTTP”我认为... ..。

你可以用代码中的 网络时间协议计算准确的时间,

我试着向你解释:

  1. 我们有发送请求的 ClientTime (例如4/3/201213:56:10.123)
  2. 将 ClientTime 发送到服务器
  3. 我们有 往返时间的请求,我叫它 请求时间(例如: 它需要5秒钟)
  4. 在服务器端,我们计算服务器和客户端之间的差异时间(例如: It ServerTime-ClientTime = ServerClientDivenceTimeWithRequestTime) ,现在应该在步骤3中计算这个差异,包括往返请求时间,然后应该从差异中删除往返时间
  5. 服务器发送包含 ServerClientReferenceTimeWithRequestTime 和 ServerTime 的响应
  6. 我们有 往返时间的响应,我把它称为 响应时间(例如: 它需要3秒钟)
  7. 在客户端,我们再次计算服务器和客户端之间的差异时间(例如: It ServerTime-ClientTime = ServerClientDivenceTimeWithResponseTime) : 现在应该在步骤6中计算包括往返响应时间在内的这个差异
  8. 我们现在有客户的时间
  9. 你应该在客户端计算简单的方程式:

X (SyncedTime) = Now + (ServerClientDifferenceTimeWithRequestTime - RquestTime)

X (SyncedTime) = Now + (ServerClientDifferenceTimeWithResponseTime - ResponseTime)

Now - ClientTime = RquestTime + ResponseTime = >

Now - (ServerClientDiffRq - RquestTime) = Now - (ServerClientDiffRs - ResponseTime)

如果你解开它,你会发现这个:

ResponseTime = (ServerClientDifferenceTimeWithRequestTime - Now + ClientTime + - ServerClientDifferenceTimeWithResponseTime )/2

然后你可以找到同步时间或服务器时间在客户端与这个等式:

X (SyncedTime) = Now + (ServerClientDifferenceTimeWithResponseTime - ResponseTime)

我展示了简单的代码,但是当你想写的时候不要忘记使用 UTC 日期和时间函数..。

服务器端(例如 php,c #) :

PHP:

header('Content-Type: application/json; charset=utf-8');
$clientTime = $_GET["ct"] * 1; //for php 5.2.1 or up: (float)$_GET["ct"];
$serverTimestamp = round(microtime(true)*1000); // (new DateTime())->getTimestamp();
$serverClientRequestDiffTime = $serverTimestamp - $clientTime;
echo "{\"diff\":$serverClientRequestDiffTime,\"serverTimestamp\":$serverTimestamp}";

C # :

long clientTime = long.Parse(Request.Form["ct"]);
long serverTimestamp = (DateTime.Now.Ticks-(new DateTime(1970,1,1) - DateTime.MinValue).Ticks) / 10000;
long serverClientRequestDiffTime = serverTimestamp - clientTime;
Response.Write("{\"diff\":"+serverClientRequestDiffTime+",\"serverTimestamp\":"+serverTimestamp+"}");

客户端(带 Jquery的 Javascript) :

var clientTimestamp = (new Date()).valueOf();
$.getJSON('http://yourhost.com/getdatetimejson/?ct='+clientTimestamp, function( data ) {
var nowTimeStamp = (new Date()).valueOf();
var serverClientRequestDiffTime = data.diff;
var serverTimestamp = data.serverTimestamp;
var serverClientResponseDiffTime = nowTimeStamp - serverTimestamp;
var responseTime = (serverClientRequestDiffTime - nowTimeStamp + clientTimestamp - serverClientResponseDiffTime )/2


var syncedServerTime = new Date((new Date()).valueOf() + (serverClientResponseDiffTime - responseTime));
alert(syncedServerTime);
});

我发现上面的@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);

注意

这种方式可能被视为一种贫民窟,有一些其他的堆栈溢出的答案,可以指导你找到一个更好的解决方案:

感谢@Mehdi Yeganeh 和@Federarne。我实现了我的函数,使用逻辑和它的工作。

Https://gist.github.com/ethaizone/6abb1d437dbe406fbed6

有点太晚了,但是希望这能帮到别人!

我有一个类似的要求,显示服务器时钟,而不管客户端的机器。 所以基本上,你只需要用到三个参数:

x = clientReqTimestamp = (new Date()).valueOf();  //Client Timestamp at request.
y = serverTimestamp;  //query your server for Unix Timestamp.
z = clientRespTimestamp = (new Date()).valueOf();  //Client Timestamp on receiving response.

然后进行以下计算:

var reqElapsed = Math.abs(y - x);  //time taken in milliseconds to hit the server
var respElapsed = Math.abs(z - y);  //time taken in milliseconds to get response from server
var serverNewTime = z + respElapsed;  // Voila! actual server time.

以下是实际操作中的完整代码:

<script>
       

var clientTimestamp = (new Date()).valueOf();
    

var Data = {
OperatorMobileNo: 'XXXXXXXXXX',
requestClientTime: clientTimestamp
};
$.ajax({
type: "POST",
url: serviceURLx + "/XXXX/GetServerDateTime/1.0",
dataType: "JSON",
data: JSON.stringify(Data),
contentType: "application/json; charset=utf-8",
success: function (responseData) {
debugger;
var responseJSON = JSON.parse(JSON.stringify(responseData));
if (responseJSON.ResponseCode === "000") {
    

var x = clientReqTimestamp = clientTimestamp;
// If server time is in seconds => multiply by 1000 to convert sec to milli
var y = serverTimestamp = responseJSON.Response.ServTimestamp * 1000;
var z = clientRespTimestamp = (new Date()).valueOf();
                

var reqElapsed = Math.abs(y - x);
var respElapsed = Math.abs(z - y);


var serverNewTime = z + respElapsed;
                

debugger;
//Init Server Clock
setInterval( function() {
debugger;
var servClockT = new Date(serverNewTime += 1000);
document.getElementById('serverClock').innerHTML = servClockT;
}, 1000);
}
else {
swal("", "Unable To Fetch Server Time!", "info");
console.log(responseJSON.ResponseCode);
}
},
error: function () {
}
});
</script>

如果我理解正确的问题,我们只有3个值

  1. ClientTimeWhenRequestSent
  2. 服务器时间
  3. 当收到回复时

如果假设请求和响应时间相等,我们可以计算 服务器和客户端之间的时间差:

const diff = serverTime - clientTimeWhenRequestSent
- (clientTimeWhenResponseReceived - clientTimeWhenRequestSent)/2;

并在客户的帮助下获得正确的时间

const correctClienTime =  (new Date()).valueOf() + diff;