La meilleure façon de synchroniser l'horloge javascript côté client avec la date du serveur

j'ai une tâche pour montrer l'horloge numérique (avec précision de minutes) sur la page HTML dans un fuseau horaire fixe (MSK ou MSD - selon la date courante). Je voudrais éviter de compter sur l'horloge du système client, donc une certaine synchronisation avec le serveur est nécessaire. Le serveur HTTP envoie l'en-tête de Date dans chaque réponse afin que nous puissions envoyer une requête AJAX GET ou HEAD à n'importe quelle URL de notre site pour obtenir la date du serveur, calculer la différence avec la date du client et l'utiliser lors de la mise à jour de l'horloge avec setTimeout(). Il y a d'autres les problèmes demeurent: le changement de fuseau horaire pour les réglages de jour, la latence expliquant les connexions très lentes.

une idée de cette tâche la plus simple? Je préfère le résoudre sans programmation côté serveur.

51
demandé sur n0s 2009-10-28 19:20:26

7 réponses

vous devez vous souvenir du temps client entre readyState==2 et readyState= = = 3 Si vous allez utiliser ajax, parce que le temps serveur sera mis quelque part entre le temps sur la demande reçue et la réponse préparée

9
répondu barbuza 2009-10-28 18:00:49

ces deux fonctions Javascript devraient faire l'affaire pour vous.

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: removed".remplacer.(^/( ) [\s\S] /,"$1")".

calcOffset () calcule le décalage de l'heure du serveur et compense pour GMT/UTC.

getServerTime() pour obtenir le décalage horaire local pour correspondre aux serveurs, en utilisant le fuseau horaire local.

si calcOffset () prend temps d'exécution vous pourriez perdre quelques secondes de précision. Peut-être que le temps d'exécution pourrait être pris en compte....

si vous êtes inquiet que le décalage calculé devenant faux lorsque soit l'heure locale ou l'heure du serveur changer à ou à partir de l'heure d'été, vous pourriez recalculer un peu après chaque heure d'horloge, le système compensera pour les changements dans l'heure d'été dayligt. Il peut être nécessaire d'attendre que l'horloge locale et celle du serveur aient Passé l'heure.

l'exemple ne fonctionne dans IE qu'à cause de" Msxml2.XMLHTTP " je pense.....

38
répondu Fedearne 2009-10-30 09:08:02

vous pouvez calculer l'heure exacte avec NTP (Network Time Protocol) dans vos codes,

j'essaie de vous expliquer:

  1. Nous avons ClientTime sur l'Envoi de la Demande (par exemple 4/3/2012 13:56:10.123)
  2. vous envoyez ClientTime au serveur
  3. Nous avons Round trip time pour la demande, je l'ai appelé RequestTime (pour exemple: il faut 5 secondes)
  4. dans le serveur, nous calculons la différence de temps entre le serveur et le Client (par exemple: It ServerTime-ClientTime = ServerClientDifferenceTimeWithRequesttime), vous devriez maintenant cette différence en incluant le temps de demande D'aller-retour à l'étape 3 alors vous devriez supprimer le temps d'aller-retour de la différence
  5. Serveur d'Envoyer une réponse qui incluent ServerClientDifferenceTimeWithRequesttime et ServerTime
  6. nous avons Round trip time pour la réponse, je l'ai appelé ResponseTime (par exemple: Il faut 3 secondes)
  7. dans client, nous calculons à nouveau la différence de temps entre le serveur et le Client( par exemple: It ServerTime-ClientTime = ServerClientDifferenceTimeWithResponsetime), encore une fois: vous devriez maintenant cette différence en incluant le temps de réponse aller-retour à l'étape 6
  8. nous avons maintenant le temps dans le Client
  9. Vous devez calculer des équations simples dans le client:

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

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

Now - ClientTime = RquestTime + ResponseTime =>

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

si vous le résolvez vous avez trouvé ceci:

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

et ensuite vous pouvez trouver le temps de synchronisation ou le serveur temps dans le client avec cette équation:

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

j'affiche le code simple mais quand vous voulez l'écrire n'oubliez pas d'utiliser les fonctions date et heure UTC...

côté serveur (par exemple 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+"}");

côté Client (Javascript avec 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);
});
30
répondu Mehdi Yeganeh 2014-07-11 12:33:26

j'ai trouvé que l'algorithme de @mehdi-yeganeh ci-dessus ne m'a pas donné de résultats utiles, mais l'idée est bonne: utiliser L'algorithme NTP (ou au moins une version faible de celui-ci) pour synchroniser le serveur et les horloges du client.

c'est mon implémentation finale, elle utilise les en-têtes de réponse du serveur si disponibles pour une précision supplémentaire (corrigez-moi si je me trompe, mes propres tests disent que c'est assez précis).

côté navigateur (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)));
    }
});

côté serveur (php, mais pourrait être n'importe quoi):

votre serveur à la route 'GET / ntp' devrait retourner quelque chose comme:

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

si vous avez PHP >5.4, alors vous pouvez enregistrer un appel à microtime () et le rendre un peu plus précis avec:

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

NOTE

cette façon pourrait être considérée comme une sorte de ghetto, il y a d'autres réponses de débordement de pile qui pourraient vous guider vers un meilleur solution:

22
répondu Aktau 2017-05-23 12:34:50

Je ne demanderais la mise à jour du serveur que toutes les 30 secondes environ, si vous avez besoin de précision à la minute près. Ne vous fiez pas du tout à l'heure du client, mais utilisez leur horloge système pour garder l'horloge précise entre les mises à jour. Je pense que vous avez répondu à votre propre question?

ça nous aiderait de mieux comprendre ce que vous essayez de faire.

Si vous voulez simplement une horloge pour afficher l'heure sur le serveur, qui est ensuite ajusté à un certain le fuseau horaire, le faire côté client avec des décalages. Gérer DST dans les timezones il est applicable en utilisant la date que vous recevez du serveur aussi bien. Si vous voulez déterminer la latence, vous aurez probablement besoin d'un petit script sur le serveur pour calculer la différence. Mais comme ci-dessus, cela aiderait à mieux comprendre le problème. Si la précision n'est qu'à la minute près, la latence semble moins critique.

0
répondu snicker 2009-10-28 16:44:30

merci à @ Mehdi Yeganeh et @Fedearne. J'implémente ma fonction pour utiliser à la fois la logique et le travail.

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

0
répondu EThaizone Jo 2016-03-08 11:22:03

Je synchroniserais le temps lors de l'initialisation avec le serveur de temps Internet.

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

-3
répondu Cheok Yan Cheng 2009-10-28 16:51:55