Normalizar el tiempo en el lado del cliente (javascript)

8

Pregunta:

¿Cómo normaliza las marcas de tiempo del lado del cliente en javascript para los usuarios cuyos relojes internos de la computadora están apagados? Tenga en cuenta que estoy lidiando con el tiempo en UTC.

Contexto:

Tengo una instancia de AWS ElasticSearch configurada con varias operaciones por lotes y estranguladas en el camino, lo que hace que las marcas de tiempo del lado del servidor no sean confiables (ya que los datos pueden estar fuera de servicio y el orden importa). Por lo tanto, necesito hacer que mis marcas de tiempo del lado del cliente sean más confiables.

Restricciones:

No puedo hacer ninguna solicitud del lado del servidor (necesito mantener las solicitudes HTTP al mínimo), pero puedo incluir una marca de tiempo del lado del servidor generada cuando mi javascript se carga por primera vez en el cliente.


Intento de solución:

Variables definidas externamente:

  • serverTimestamp - una marca de tiempo UTC (en milisegundos), generada en el lado del servidor cuando se carga el javascript.
  • getCookie - una función que obtiene un valor de cookie para una clave determinada (o una cadena vacía si no se encuentra).

La configuración de control de caché para el archivo es "public,max-age=300,must-revalidate"(por lo tanto, 5 minutos).

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

Problema con la solución:

Usando la getTimestampfunción anterior , la ejecución (new Date(getTimestamp())).getUTCDate()regresa al día siguiente para algunos usuarios, y getUTCHoursparece estar por todos lados en casos extremos. No puedo diagnosticar el problema por mi cuenta.

jperezov
fuente
1
¿Las marcas de tiempo no marcan la fecha de GMT, entonces puede traducir a su TZ local si lo desea?
enlace simbólico
En realidad estoy tratando de mantener todo en GMT / UTC. No quiero que esto sea específico para ninguna zona horaria: estoy tratando de normalizar la hora en zonas horarias / navegadores extraños.
jperezov
Use Date.toUTCString () en lugar de Date.toString (): stackoverflow.com/questions/17545708/…
enlace simbólico
1
No puede confiar en el tiempo del Cliente, ya que puede modificarse. Tendrá que confiar en el servidor, donde controlará el tiempo. En PHP, haría algo como<?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
¿Qué precisión necesita (ms, s, minutos?); y confías lo suficiente en el cliente? es decir, ¿qué es lo peor que le puede pasar a su sistema si un cliente manipula el javascript o publica datos incorrectos? NTP existe porque es difícil hacer una sincronización de tiempo distribuida con precisión, pero si no le importan demasiado los segundos y está más interesado en arreglar los relojes de los clientes que están apagados por un día más o menos, entonces la solución @symcbean probablemente sea suficiente. ¡Dudo porque dices "el orden importa"! Estoy interesado en su solución final, me encantaría que la publique como respuesta.
thinkOfaNumber

Respuestas:

1

Apéguese a las marcas de tiempo en el lado del cliente, no se moleste con las fechas / horas. Almacene el delta entre el lado del servidor y el reloj del cliente en el cliente. Envíe la marca de tiempo y delta del cliente con los mensajes y luego reconstruya la fecha / hora en el servidor.

Luego, puede tomar decisiones sensatas en el servidor sobre cómo lidiar con las fluctuaciones y los saltos en la sincronización del reloj.

symcbean
fuente
Oooh, esa es una idea interesante para salvar el delta entre los relojes. Tal vez pueda comenzar a guardar esa información para ver qué tan grandes pueden llegar a ser los deltas y con qué frecuencia ocurre este problema.
jperezov
0

No debe confiar en el Cliente por tiempo garantizado. Su JavaScript puede parecerse a algo como:

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

Si usa 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
fuente