クライアント側で時間を正規化する(JavaScript)


8

質問:

内部コンピューターのクロックがオフになっているユーザーのために、JavaScriptでクライアント側のタイムスタンプをどのように正規化しますか?私はUTCで時間を扱っていることに注意してください。

環境:

AWS ElasticSearchインスタンスをセットアップし、途中でいくつかのバッチ処理とスロットル処理を行って、サーバー側のタイムスタンプの信頼性を低くしています(データが乱れる可能性があるため、順序が重要です)。したがって、クライアント側のタイムスタンプの信頼性を高める必要があります。

制約:

サーバー側のリクエストを作成することはできませんが(HTTPリクエストを最小限に抑える必要があります)、JavaScriptが最初にクライアントに読み込まれたときに生成されるサーバー側のタイムスタンプを含めることができます。


試みられた解決策:

外部定義変数:

  • serverTimestamp -JavaScriptが読み込まれたときにサーバー側で生成されるUTCタイムスタンプ(ミリ秒単位)。
  • getCookie -指定されたキーのCookie値(または、見つからない場合は空の文字列)を取得する関数。

ファイルのキャッシュ制御設定は"public,max-age=300,must-revalidate"(つまり5分)です。

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

ソリューションの問題:

上記のgetTimestamp関数を使用すると、running (new Date(getTimestamp())).getUTCDate()は一部のユーザーに翌日を返し、getUTCHoursエッジケースではすべての場所にあるようです。自分で問題を診断することはできません。


1
タイムスタンプはGMTからの日付をマークしないので、必要に応じてローカルTZに変換できますか?
シンボリックリンク

私は実際にはすべてをGMT / UTCで維持しようとしています。これを特定のタイムゾーンに固有にしたくありません-奇妙なタイムゾーン/ブラウザー全体で時間を正規化しようとしています。
jperezov

代わりにDate.toStringの利用Date.toUTCString()():stackoverflow.com/questions/17545708/...
シンボリックリンク

1
クライアント時間は変更される可能性があるため、信頼することはできません。時間を制御するサーバーに依存する必要があります。PHPでは、次のようにします<?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
どの精度が必要ですか(ms、s、分?)。クライアントを十分に信頼していますか?つまり、クライアントがJavaScriptを操作したり、間違ったデータを投稿したりすると、システムに起こりうる最悪の事態は何ですか?NTPは、正確に分散時間同期を行うのが難しいために存在しますが、秒についてあまり気にせず、1日ほどずれているクライアントクロックの修正に関心がある場合は、@ symcbeanソリューションで十分です。あなたは「順序が重要」と言っているので私はためらっています!私はあなたの最終的な解決策に興味があります。あなたが答えとしてそれを投稿するのを見てみたいです。
thinkOfaNumber

回答:


1

クライアント側のタイムスタンプに固執する-日付/時刻を気にしないでください。サーバーサイドとクライアントのクライアントクロック間の差分を保存します。クライアントのタイムスタンプとデルタをメッセージとともに送信し、サーバーで日付/時刻を再構築します。

次に、クロック同期におけるジッターとジャンプの処理方法について、サーバーサイドで適切な決定を行うことができます。


おお、それは時計間のデルタを保存する面白いアイデアです。多分私はその情報を保存し始めて、デルタがどれくらいの大きさになることができるか、そしてこの問題がどのくらいの頻度で発生するかを見ることができます。
jperezov

0

保証された時間はクライアントに依存しないでください。JavaScriptは次のようになります。

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

PHPを使用している場合:

<?php // gettime.php
date_default_timezone_set('UTC'); $o = new StdClass;
if(isset($_POST['get_time'])){
  $o->time = time();
  echo json_encode($o);
}
?>
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.