68

HTMLページに固定タイムゾーン(現在の日付に応じてMSKまたはMSD)でデジタル時計(分精度)を表示するタスクがあります。クライアントのシステム クロックに依存したくないので、サーバーとの同期が必要です。HTTP サーバーは各応答で Date ヘッダーを送信するため、AJAX GET または HEAD 要求をサイトの任意の URL に送信してサーバーの日付を取得し、クライアントの日付との差を計算し、setTimeout() でクロックを更新するときにそれを使用できます。他にも問題が残っています: 昼光設定のタイムゾーンの切り替え、非常に遅い接続の原因となる遅延。

このタスクの最も簡単な方法はありますか? サーバー側のプログラミングなしで解決したいと思います。

4

9 に答える 9

41

これらの 2 つの Javascript 関数でうまくいくはずです。

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

編集: ".replace(/^(. )[\s\S] /,"$1")" を削除しました。

calcOffset() は、サーバー時刻からのオフセットを計算し、GMT/UTC を補正します。

getServerTime() を使用して、ローカル タイムゾーンを使用して、サーバーに一致するローカル タイム オフセットを取得します。

calcOffset() の実行に時間がかかる場合、数秒の精度が失われる可能性があります。たぶん、実行時間を考慮に入れることができます....

現地時間またはサーバー時間のいずれかが夏時間に変更されたとき、または夏時間から変更されたときに計算されたオフセットが間違っていることが心配な場合は、1 時間ごとに少しずつ再計算することができます。システムは夏時間の変更を補正します。ローカル クロックとサーバー クロックの両方が 1 時間を過ぎるまで待つ必要がある場合があります。

「Msxml2.XMLHTTP」のため、この例はIEでのみ機能すると思います.....

于 2009-10-29T01:24:52.507 に答える
29

上記の@mehdi-yeganehのアルゴリズムでは有用な結果が得られなかったことがわかりましたが、アイデアは適切です.NTPアルゴリズム(または少なくともその弱いバージョン)を使用してサーバーとクライアントのクロックを同期させます。

これは私の最終的な実装です。サーバーの応答ヘッダーを使用して精度を高めます (間違っている場合は修正してください。私自身のテストでは、これは非常に正確です)。

ブラウザ側 (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)));
    }
});

サーバー側(php、しかし何でもかまいません):

ルート「GET /ntp」のサーバーは、次のようなものを返す必要があります。

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

PHP > 5.4 を使用している場合は、microtime() への呼び出しを保存して、次のようにしてもう少し正確にすることができます。

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

ノート

この方法は一種のゲットーのように見えるかもしれませんが、より良い解決策に導く可能性のあるスタック オーバーフローの回答が他にもいくつかあります。

于 2014-04-09T17:06:44.800 に答える
12

ajaxを使用する場合は、readyState==2とreadyState==3の間のクライアント時間を覚えておく必要があります。これは、サーバーの時間が、要求の受信時間と応答の準備時間の間のどこかに設定されるためです。

于 2009-10-28T18:00:49.927 に答える
0

@Mehdi Yeganeh と @Fedearne に感謝します。ロジックとその機能の両方を使用するように関数を実装します。

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

于 2016-03-08T11:22:03.267 に答える
0

1 分単位の精度が必要な場合は、30 秒ごとにサーバーに更新を要求するだけです。クライアントの時刻にまったく依存しないでください。ただし、システム クロックを使用して、更新間でクロックを正確に保ちます。あなた自身の質問に答えたと思いますか?

あなたが実際に何をしようとしているのかをよりよく理解できれば助かります。

時計にサーバー上の時間を表示させ、特定のタイムゾーンに調整するだけの場合は、オフセットを使用してクライアント側で行います。サーバーから受信した日付も使用して、適用可能なタイムゾーンで DST を処理します。レイテンシーを判断したい場合は、サーバー上に違いを計算するための小さなスクリプトが必要になるでしょう。しかし、上記のように、問題をよりよく理解するのに役立ちます。精度が分単位である場合、レイテンシーはあまり重要ではないように思われます。

于 2009-10-28T16:24:39.310 に答える
-3

初期化中に時刻を Internet Time Server と同期します。

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

于 2009-10-28T16:51:55.950 に答える