A melhor maneira de sincronizar o relógio javascript do lado do cliente com a data do servidor

Eu tenho uma tarefa para mostrar relógio digital (com precisão de minutos) na página HTML em algum fuso horário fixo (MSK ou MSD – dependendo da data atual). Eu gostaria de evitar confiar no clock do sistema do cliente, portanto, é necessária alguma synchronization com o servidor. O servidor HTTP envia o header Date em cada resposta para que possamos enviar uma solicitação AJAX GET ou HEAD para qualquer URL do nosso site para obter a data do servidor, calcular a diferença com a data do cliente e usá-la ao atualizar o clock com setTimeout (). Existem outros problemas: a mudança de fuso horário para as configurações de luz do dia e a latência para conexões muito lentas.

Qualquer ideia para essa tarefa é a maneira mais simples? Eu prefiro resolvê-lo sem programação do lado do servidor.

você deve se lembrar do tempo do cliente entre readyState == 2 e readyState == 3 se você estiver usando ajax, porque a hora do servidor será definida em algum lugar entre o tempo sob solicitação e a resposta preparada

Estas duas funções Javascript devem fazer o truque para você.

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; } 

EDIT: removido “.replace (/ ^ (. ) [\ S \ S] /,” $ 1 “)”.

calcOffset () calcula o deslocamento do tempo do servidor e compensa o GMT / UTC.

getServerTime () para obter o deslocamento da hora local para corresponder aos servidores, usando o fuso horário local.

Se calcOffset () leva tempo para executar, você pode perder alguns segundos de precisão. Talvez o tempo de execução possa ser levado em conta …

Se você estiver preocupado com o deslocamento calculado se tornar incorreto quando a hora local ou do servidor mudar para ou a partir do horário de verão, você poderá recalcular um pouco após cada relógio, o sistema compensará as alterações no horário de verão. Pode ser necessário aguardar até que o relógio local e do servidor tenha passado da hora.

O exemplo só funciona no IE por causa de “Msxml2.XMLHTTP” eu acho …..

Você pode calcular o tempo exato com NTP (Network Time Protocol) em seus códigos,

Eu tento explicar para você:

  1. Temos ClientTime na solicitação de envio (por exemplo, 4/3/2012 13: 56: 10.123)
  2. Você envia o ClientTime ao servidor
  3. Temos tempo de ida e volta para solicitação, eu chamei de RequestTime (por exemplo: Demora 5 segundos)
  4. No servidor, calculamos o tempo de diferença entre servidor e cliente (por exemplo: ele ServerTime – ClientTime = ServerClientDifferenceTimeWithRequestTime), você deve agora essa diferença, incluindo o tempo de solicitação de ida e volta na etapa 3, então você deve remover o tempo de ida e volta da diferença
  5. Resposta de envio do servidor que inclui ServerClientDifferenceTimeWithRequestTime e ServerTime
  6. Temos tempo de ida e volta para resposta, eu chamei de ResponseTime (por exemplo: Demora 3 segundos)
  7. No cliente, calculamos o tempo de diferença entre o servidor e o cliente novamente (por exemplo: It ServerTime – ClientTime = ServerClientDifferenceTimeWithResponseTime), novamente: você deve agora essa diferença, incluindo o tempo de resposta de ida e volta na etapa 6
  8. Nós temos agora o tempo no cliente
  9. Você deve calcular equações simples no cliente:

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

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

Now - ClientTime = RquestTime + ResponseTime =>

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

Se você resolver isso, você achou isso:

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

e então você pode encontrar tempo sincronizado ou tempo do servidor no cliente com esta equação:

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

Eu mostro código simples, mas quando você quiser escrever, não se esqueça de usar as funções de data e hora UTC …

Lado do servidor (por exemplo, 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+"}"); 

Lado do Cliente (Javascript com 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); }); 

Descobri que o algoritmo de @ mehdi-yeganeh acima não me deu resultados úteis, mas a idéia é boa: usar o algoritmo NTP (ou pelo menos uma versão fraca dele) para sincronizar os relógios do servidor e do cliente.

Esta é a minha implementação final, ele usa os headers de resposta do servidor, se disponível para maior precisão (por favor me corrija se eu estiver errado, meus próprios testes dizem que isso é bem preciso).

lado do navegador (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))); } }); 

lado do servidor (php, mas poderia ser qualquer coisa):

Seu servidor na rota ‘GET / ntp’ deve retornar algo como:

 echo (string) round(microtime(true) * 1000); 

Se você tem PHP> 5.4, então você pode salvar uma chamada para microtime () e torná-la um pouco mais precisa com:

 echo (string) round($_SERVER['REQUEST_TIME_FLOAT'] * 1000); 

NOTA

Desta forma, pode ser visto como um tipo de gueto, existem algumas outras respostas do Stack Overflow que podem guiá-lo para uma solução melhor:

  • Como sincronizar uma contagem regressiva de javascript com a hora do servidor
  • Sincronizar o horário JS entre vários dispositivos
  • Notavelmente, esta solução foi oferecida algumas vezes: https://github.com/NodeGuy/ServerDate . Parece que este é um pouco mais de trabalho para configurar, mas deve fornecer uma melhor precisão.

Eu só solicito a atualização do servidor a cada 30s ou mais, se você precisar de precisão apenas para o minuto. Não confie no tempo do cliente, mas use o relógio do sistema para manter o relógio preciso entre as atualizações. Eu acho que você respondeu sua própria pergunta?

Ajudaria se compreendêssemos melhor o que você está realmente tentando fazer.

Se você simplesmente quiser que um relógio exiba a hora no servidor, que é ajustada para um determinado fuso horário, faça-o com correções. Lidar com DST nos fusos horários é aplicável usando a data que você recebe do servidor também. Se você quiser determinar a latência, provavelmente precisará de um script pequeno no servidor para calcular a diferença. Mas, como acima, ajudaria a entender melhor o problema. Se a precisão é apenas para o minuto, a latência parece menos crítica.

Graças a @Mehdi Yeganeh e @Fedearne. Eu implemento minha function para usar lógica e trabalho.

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

Eu iria sincronizar o tempo durante a boot com o Internet Time Server.

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