Normalisieren Sie die Zeit auf der Clientseite (Javascript).

8

Frage:

Wie normalisieren Sie clientseitige Zeitstempel in Javascript für Benutzer, deren interne Computeruhren ausgeschaltet sind? Beachten Sie, dass ich mich mit Zeit in UTC beschäftige.

Kontext:

Ich habe eine AWS ElasticSearch-Instanz mit mehreren gestapelten und gedrosselten Vorgängen eingerichtet, wodurch serverseitige Zeitstempel unzuverlässig werden (da die Daten möglicherweise nicht in der richtigen Reihenfolge vorliegen und die Reihenfolge von Bedeutung ist). Ich muss daher meine clientseitigen Zeitstempel zuverlässiger machen.

Einschränkungen:

Ich kann keine serverseitigen Anforderungen stellen (HTTP-Anforderungen müssen auf ein Minimum beschränkt werden), aber ich kann einen serverseitigen Zeitstempel einfügen, der generiert wird, wenn mein Javascript zum ersten Mal auf den Client geladen wird.


Lösungsversuch:

Extern definierte Variablen:

  • serverTimestamp - Ein UTC-Zeitstempel (in Millisekunden), der serverseitig generiert wird, wenn das Javascript geladen wird.
  • getCookie - Eine Funktion, die einen Cookie-Wert für einen bestimmten Schlüssel erhält (oder eine leere Zeichenfolge, wenn sie nicht gefunden wird).

Die Cache-Steuerungseinstellungen für die Datei sind "public,max-age=300,must-revalidate"(also 5 Minuten).

const getTimestamp = (function() {
    // This cookie is set on the `unload` event, and so should be greater than
    // the server-side timestamp when set.
    /** @type {!number} */
    const cookieTimestamp = parseInt(getCookie("timestamp_cookie"), 10) || 0;
    // This timestamp _should_ be a maximum of 5 minutes behind on page load
    // (cache lasts 5 min for this file).
    /** @type {!number} */
    const storedTimestamp = cookieTimestamp > serverTimestamp ?
                            cookieTimestamp : serverTimestamp;
    return function () {
        /** @type {!number} */
        const timestamp = Date.now();
        // This timestamp should be, at a *maximum*, 5-6 minutes behind
        // (assuming the user doesn't have caching issues)
        /** @type {!number} */
        const backupTimestamp = storedTimestamp
            + parseFloat(window.performance.now().toFixed(0));
        // Now let's check to see if the user's clock is
        // either too fast, or too slow:
        if (
            // Timestamp is smaller than the stored one.
            // This means the user's clock is too slow.
            timestamp < backupTimestamp
            // Timestamp is more than 6 minutes ahead. User's clock is too fast.
            // (Using 6 minutes instead of 5 to have 1 minute of padding)
            || (timestamp - backupTimestamp) > 360000
        ) {
            return backupTimestamp;
        } else {
            // Seems like the user's clock isn't too fast or too slow
            // (or just maximum 1 minute fast)
            return timestamp;
        }
    }
})();

Problem mit der Lösung:

Mit der oben genannten getTimestampFunktion wird das Ausführen (new Date(getTimestamp())).getUTCDate()für einige Benutzer am nächsten Tag zurückgegeben und getUTCHoursscheint in Randfällen allgegenwärtig zu sein. Ich kann das Problem nicht alleine diagnostizieren.

jperezov
quelle
1
Markieren Zeitstempel nicht das Datum von GMT, dann können Sie auf Wunsch in Ihre lokale TZ übersetzen?
Symlink
Ich versuche eigentlich, alles in GMT / UTC zu halten. Ich möchte nicht, dass dies für eine Zeitzone spezifisch ist - ich versuche, die Zeit über seltsame Zeitzonen / Browser hinweg zu normalisieren.
jperezov
Verwenden Sie Date.toUTCString () anstelle von Date.toString (): stackoverflow.com/questions/17545708/…
symlink
1
Sie können sich nicht auf die Client-Zeit verlassen, da diese geändert werden kann. Sie müssen sich auf den Server verlassen, auf dem Sie die Zeit steuern. In PHP würden Sie so etwas wie<?php date_default_timezone_set('UTC'); $o = new StdClass; if(isset($_POST['get_time'])){ /* make sure AJAX get_time is set */ $o->time = time(); echo json_encode($o); /* now you have object with time property for javascript AJAX argument */ } ?>
StackSlave
1
Welche Genauigkeit benötigen Sie (ms, s, Minuten?); und vertraust du dem Kunden genug? dh was ist das Schlimmste, was Ihrem System passieren kann, wenn ein Client das Javascript manipuliert oder falsche Daten veröffentlicht? NTP existiert, weil es schwierig ist, eine zeitlich verteilte Synchronisation mit Genauigkeit durchzuführen. Wenn Sie sich jedoch nicht zu sehr für Sekunden interessieren und mehr daran interessiert sind, Client-Uhren zu reparieren, die um einen Tag oder so versetzt sind, reicht die @ symcbean-Lösung wahrscheinlich aus. Ich zögere, weil Sie sagen, "die Reihenfolge ist wichtig"! Ich bin an Ihrer endgültigen Lösung interessiert und würde mich freuen, wenn Sie sie als Antwort veröffentlichen.
thinkOfaNumber

Antworten:

1

Halten Sie sich an die Zeitstempel auf der Client-Seite - kümmern Sie sich nicht um Datum / Uhrzeit. Speichern Sie das Delta zwischen der Serverseite und der Client-Uhr auf dem Client. Senden Sie den Client-Zeitstempel und das Delta mit den Nachrichten und rekonstruieren Sie dann Datum und Uhrzeit auf dem Server.

Dann können Sie auf der Serverseite sinnvolle Entscheidungen darüber treffen, wie Sie mit Jitter und Sprüngen in der Uhrensynchronisation umgehen sollen.

Symcbean
quelle
Oooh, das ist eine interessante Idee, um das Delta zwischen den Uhren zu retten. Vielleicht kann ich anfangen, diese Informationen zu speichern, um zu sehen, wie groß die Deltas werden können und wie oft dieses Problem auftritt.
Jperezov
0

Sie sollten sich nicht für eine garantierte Zeit auf den Kunden verlassen. Ihr JavaScript könnte ungefähr so ​​aussehen:

var fd = new FormData, xhr = new XMLHttpRequest;
fd.append('get_time', '1'); xhr.open('POST', 'gettime.php');
xhr.onload = function(response){
  var obj = JSON.parse(response.responseText);
  console.log(obj.time);
}
xhr.send(fd);

Bei Verwendung von PHP:

<?php // gettime.php
date_default_timezone_set('UTC'); $o = new StdClass;
if(isset($_POST['get_time'])){
  $o->time = time();
  echo json_encode($o);
}
?>
StackSlave
quelle