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

n0s*_*n0s 57 javascript ajax timezone clock-synchronization

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

对这项任务的任何想法都是最简单的方法吗?我更愿意在没有服务器端编程的情况下解决它.

Fed*_*rne 38

这两个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;
}
Run Code Online (Sandbox Code Playgroud)

编辑:删除".replace(/ ^(.)[\ s\S] /,"$ 1")".

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

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

如果calcOffset()花费时间执行,则可能会失去一些秒精度.也许可以考虑执行时间....

如果您担心当本地时间或服务器时间更改为夏令时或从夏令时变为计算的偏移时,您可以在每个时钟小时后重新计算一个litle,系统将补偿dayligt节省时间的变化.可能需要等到本地和服务器时钟都过了一小时.

该示例仅适用于IE,因为"Msxml2.XMLHTTP"我认为......

  • 从"GET"到"HEAD"的更改方法将导致http服务器不返回正文.这可能会加快请求速度. (9认同)
  • 注意`Date.parse(new Date().toUTCString());`创建一个日期对象,将其转换为UTC字符串,然后将其解析回日期并返回自纪元以来的毫秒数.它可以用更简单的`(new Date())替换.getTime();`或`new Date()*1 (4认同)
  • @RobG或`Date.now()` (2认同)

Meh*_*neh 34

您可以使用代码中的NTP(网络时间协议)计算确切时间,

我试着为你解释一下:

  1. 我们在发送请求时有ClientTime(例如2012年4月3日13:56:10.123)
  2. 您将ClientTime发送到服务器
  3. 我们有请求的往返时间,我称之为RequestTime(例如:需要5秒)
  4. 在服务器中,我们计算服务器和客户端之间的差异时间(例如:It ServerTime - ClientTime = ServerClientDifferenceTimeWithRequestTime),您现在应该在步骤3中包括往返请求时间这个差异然后您应该从差异中删除往返时间
  5. 服务器发送包含ServerClientDifferenceTimeWithRequestTime和ServerTime的响应
  6. 我们有响应的往返时间,我称之为ResponseTime(例如:需要3秒)
  7. 在客户端,我们再次计算服务器和客户端之间的差异时间(例如:It ServerTime - ClientTime = ServerClientDifferenceTimeWithResponseTime),再次:您现在应该在步骤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}";
Run Code Online (Sandbox Code Playgroud)

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+"}");
Run Code Online (Sandbox Code Playgroud)

客户端(Javascript与Jquery):

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);
});
Run Code Online (Sandbox Code Playgroud)

  • http://en.wikipedia.org/wiki/Network_Time_Protocol它的ntp协议..我用它的时钟同步算法 (3认同)
  • Downvoted,因为客户端的公式是错误的.正确的公式是syncedServerTime = clientTime +(data.Diff +(data.ServerTime - nowTimeStamp))/ 2 (3认同)
  • 我修复了您遇到的一些错误,但这实际上不起作用。如果客户端时钟关闭,这不会给出服务器看到的正确时间。 (2认同)
  • @Mehdi:它应该是 (serverClientRequestDiffTime - nowTimeStamp + clientTimestamp + serverClientResponseDiffTime)/2 因为 serverClientResponseDiffTime 是计算服务器端 (2认同)

Akt*_*tau 26

我发现上面的@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)));
    }
});
Run Code Online (Sandbox Code Playgroud)

服务器端(php,但可以是任何东西):

您在'GET/ntp'路线上的服务器应该返回如下内容:

echo (string) round(microtime(true) * 1000);
Run Code Online (Sandbox Code Playgroud)

如果您的PHP> 5.4,那么您可以保存对microtime()的调用并使其更准确:

echo (string) round($_SERVER['REQUEST_TIME_FLOAT'] * 1000);
Run Code Online (Sandbox Code Playgroud)

注意

这种方式可能被视为一种贫民区,还有一些其他Stack Overflow答案可以指导您找到更好的解决方案:

  • http://github.com/nicksardo/GoTime是另一个类似于ServerDate的JS库,但具有与Websockets一起使用的处理程序,这大大提高了典型http请求的精度. (3认同)

小智 9

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