Normalizar o tempo no lado do cliente (javascript)

8

Questão:

Como você normaliza os carimbos de data e hora do lado do cliente em javascript para usuários cujos relógios do computador interno estão desativados? Observe que estou lidando com o tempo no UTC.

Contexto:

Eu tenho uma instância do AWS ElasticSearch configurada com várias operações em lote e otimizadas ao longo do caminho, tornando os carimbos de data / hora do lado do servidor não confiáveis ​​(já que os dados podem ficar fora de ordem e o pedido é importante). Portanto, preciso tornar meus carimbos de data e hora do cliente mais confiáveis.

Restrições:

Não posso fazer nenhuma solicitação do lado do servidor (preciso manter as solicitações HTTP no mínimo), mas posso incluir um carimbo de data e hora do servidor gerado quando meu javascript é carregado pela primeira vez no cliente.


Tentativa de solução:

Variáveis ​​definidas externamente:

  • serverTimestamp - um carimbo de data / hora UTC (em milissegundos), gerado no lado do servidor quando o javascript é carregado.
  • getCookie - uma função que obtém um valor de cookie para uma determinada chave (ou uma string vazia, se não for encontrada).

As configurações de controle de cache para o arquivo são "public,max-age=300,must-revalidate"(portanto, 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 com a solução:

Usando a getTimestampfunção acima , a execução (new Date(getTimestamp())).getUTCDate()está retornando no dia seguinte para alguns usuários e getUTCHoursparece estar em todo lugar em casos extremos. Não consigo diagnosticar o problema sozinho.

jperezov
fonte
1
Os carimbos de data e hora não marcam a data no GMT; você pode traduzir para a sua TZ local, se desejar?
symlink
Na verdade, estou tentando manter tudo em GMT / UTC. Não quero que isso seja específico de nenhum fuso horário - estou tentando normalizar o horário em fusos horários / navegadores estranhos.
jperezov
Use Date.toUTCString () em vez de Date.toString (): stackoverflow.com/questions/17545708/…
symlink
1
Você não pode confiar no tempo do cliente, pois ele pode ser alterado. Você terá que confiar no servidor, onde controla o tempo. Em PHP, você faria 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 31/12/19
1
De que precisão você precisa (ms, s, minutos?); e você confia no cliente o suficiente? ou seja, o pior que poderia acontecer ao seu sistema se um cliente manipulasse o javascript ou publicasse dados incorretos? O NTP existe porque é difícil fazer a sincronização de tempo distribuída com precisão, mas se você não se importa muito com segundos e está mais interessado em corrigir relógios de clientes que estão desativados em um dia ou mais, a solução @symcbean provavelmente é suficiente. Estou hesitante porque você declara "a ordem é importante"! Estou interessado em sua solução final. Gostaríamos de vê-lo como uma resposta.
thinkOfaNumber 24/01

Respostas:

1

Atenha-se a carimbos de data e hora no lado do cliente - não se preocupe com datas / horas. Armazene o delta entre o lado do servidor e o relógio do cliente. Envie o carimbo de data e hora do cliente e o delta com as mensagens e reconstrua a data / hora no servidor.

Em seguida, você pode tomar decisões sensatas ao lado do servidor sobre como lidar com jitter e saltos na sincronização do relógio.

symcbean
fonte
Oooh, é uma ideia interessante para salvar o delta entre os relógios. Talvez eu possa começar a salvar essas informações para ver o tamanho dos deltas e a frequência com que esse problema ocorre.
jperezov
0

Você não deve confiar no cliente por tempo garantido. Seu JavaScript pode ser 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);

Se estiver usando 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
fonte