将客户端JavaScript时钟与服务器date同步的最佳方法

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

任何想法这个任务最简单的方法? 我宁愿在没有服务器端编程的情况下解决它。

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

这两个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; } 

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

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

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

如果calcOffset()需要时间来执行,则可能会失去几秒钟的精度。 也许执行时间可以考虑到….

如果您担心在本地时间或服务器时间更改为夏令时或从夏令时更改时计算的偏移量错误,则您可以在每个时钟小时后重新计算一个小数点,系统将补偿日间节省时间的变化。 可能需要等到本地和服务器时钟都已经过了一个小时。

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

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

我试着为你解释:

  1. 我们有发送请求的ClientTime(例如2012年4月3日13:56:10.123)
  2. 您将ClientTime发送到服务器
  3. 我们有往返时间的请求,我叫它RequestTime (例如:它需要5秒钟)
  4. 在Server中,我们计算服务器和客户端之间的差异时间(例如: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

然后你可以在这个公式中find客户端的同步时间或服务器时间:

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

我显示简单的代码,但是当你想写它没有忘记使用UTCdate和时间函数…

服务器端(例如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+"}"); 

客户端(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); }); 

我发现上面的@ mehdi-yeganehalgorithm没有给出有用的结果,但是这个想法是合理的:使用NTPalgorithm(或者至less是它的一个弱版本)来同步服务器和客户端时钟。

这是我最后的实现,它使用服务器响应头,如果可用的额外的准确性(请纠正我,如果我错了,我自己的testing说这是相当准确的)。

浏览器端(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); 

注意

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

  • 如何同步一个JavaScript倒计时与服务器时间
  • 在多个设备之间同步JS时间
  • 值得注意的是,这个解决scheme已经提供了几次: https : //github.com/NodeGuy/ServerDate 。 这似乎是稍微多一点工作,但应提供更好的准确性。

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

如果我们更好地理解你实际上正在做的事情,这将有所帮助。

如果你只是想要一个时钟显示在服务器上的时间,然后调整到一定的时区,用偏移量客户端。 通过使用从服务器收到的date,在适用的时区中处理DST。 如果要确定延迟,则可能需要服务器上的一个小脚本来计算差异。 但是如上所述,这将有助于更好地理解问题。 如果精度只是一分钟,延迟似乎不那么重要。

感谢@Mehdi Yeganeh和@Fedearne。 我实现我的function使用逻辑和它的工作。

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

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

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