JavaScriptをサーバーの時刻に合わせる

( サーバー応答の遅延分を修正版 / ダイナミックロード関数との組み合わせ )




Sample





  サーバーとクライアントの時差とサーバー呼び出し遅延秒数を加算して修正 (時差修正レポート有り)




  (時差修正レポート無し)



JavaScript で setByServer(false) を実行後、変数 serverNow で修正後の時間を、
変数 sabun で(new Date()).getTime()との差分のミリ秒を、取り出してください。
なぜ、これが必要なのか?

JavaScriptのDate()は便利ですが、
new Date()した時に得られる時間がローカルマシンの時刻なので
見る人によってバラバラの時間が表示されてしまう、という
欠点があります。

もし、たとえばそのページを見るマシンの日付が1980年だっ
たら、作ったカレンダーも1980年が「現在」になってしま
うというわけです。

このままでは、時刻が異なる無数のマシンが接続するWeb上では
表示される時刻が信用できないバラバラなものになってしまうので
大事な場面にはとうてい使えないわけです。が、かといって、
サーバーにいちいち繰り返しつなぐのも面倒でJavaScriptの
さくさく感も捨て難い、

ということで、
何をしようとしているのか?
というと...

  1. サーバーのtime(unixなら1970.1.1からの秒数)を取得して
      ( perlなら time  や PHPなら time()  )

  2. そのtimeを変数へ埋め込んだjsファイルをサーバーから書き出す

  3. そのjsファイルを読み込んだHTML内で、ローカルマシンの時刻とサーバーのtimeの時差と
     サーバーへ送ったリクエストの返事が返ってくるまでの遅延時間の片道分を加えた修正時刻を

  4. JavaScriptの変数で取得する。

  5. あとは、この差分をnew Date()に加算すればいつでも同期できる。


こうすることで、どんなに日付の狂ったマシンでアクセスさ
れても サーバーの連続した一意な時間に合わせた時刻が
JavaScriptで表示されるようにしたいということです。


これを実現するための準備はHTML内へ
<script src="http://game.gr.jp/svmix/js/setservertime.js"></script>
を一行書き加えることと、同期したい時に、
setByServer()という関数を実行するだけです。

あとは、修正後の時間は、変数 serverNowで、
差分のミリ秒は、変数 sabunで取り出すことができます。


* 試しにマシンの日付設定を1990年などに変更してみてからテストすると良くわかります 



このページのHTMLは下記の通り。



<script src="http://game.gr.jp/svmix/js/setservertime.js"></script>

<form>

  <input type=button onclick="setByServer(true)" value="setByServer(true)"><br><br>
  サーバーとクライアントの時差とサーバー呼び出し遅延秒数を加算して修正<br><br><br>

  <input type=button onclick="setByServer()" value="setByServer() 時差修正レポート無し"><br><br>
  サーバーとクライアントの時差とサーバー呼び出し遅延秒数を加算して修正 (時差修正レポート無し)<br>
 
</form>




http://game.gr.jp/svmix/js/setservertime.js の中身は下記の通りmod_perlでjavascriptを出力している。 #!/usr/local/bin/perl $time = time ; print <<__JS__; Content-type:application/x-javascript /*===============ページ構築後外部js取得用関数 UseFree */ /*====================================================== Win n4 n6 moz e4 e5 e6, Mac n4 n6 moz e4.5 e5, Linux n4 n6 moz ======================================================== 書式 dynamicLoad('外部jsファイル名') 使用例 dynamicLoad('http://game.gr.jp/test.js') 通常は外部jsファイルはページ構築後にロードできないが この関数は上記のブラウザで、たとえばPerlのrequireなど のように必要な時に呼び出して使えるようにするためのもの。 これによって、必要な最小限のソースだけを必要な時に ロードして使えるようになる。 Support http://game.gr.jp/js/ =======================================================*/ function dynamicLoad(jsFileName){ if (document.images){ var now = new Date() var getData = jsFileName+'?nc='+now.getTime() if( document.all ){ if( navigator.userAgent.indexOf("Win")!=-1 ) eval(document.all('dynld')).src=getData else if( navigator.userAgent.indexOf("Mac")!=-1 ){ document.body.insertAdjacentHTML('BeforeEnd' ,'<scr'+'ipt src="'+getData+'"><scr'+'ipt/>') } } else if(document.getElementById) { document.getElementById('dynld').innerHTML ='<scr'+'ipt src="'+getData+'"><scr'+'ipt/>' } else { var datasrc = new Image() datasrc.src = getData location.href = datasrc.src } } } document.write('<scr'+'ipt id="dynld"></scr'+'ipt>') /*================ページ構築後外部js取得用関数ここまで*/ /*=======================サーバー応答の遅延時間を修正 */ var msgFlg = false // メッセージ有りはtrue var startTime = 0 // getservernow.js の読み込み開始時間 A var endTime = 0 // getservernow.js の読み込み終了時間 B var loseTime = 0 // B-A サーバー応答の遅延時間(1/1000sec) C var loseTimeHalf = 0 // C / 2 遅延時間片道分(1/1000sec以下切捨) D var serverTime = 0 // onGetServerTime()時のサーバー時間 E var zisa = 0 // E-A 時差 F var serverNow = 0 // 修正後の現在時間 var localNow = 0 var sabun = 0 // new Date()を修正するための差分 function setByServer(msgflg){ msgFlg = msgflg startTime = new Date() dynamicLoad('http://game.gr.jp/svmix/js/getservernow.js') } function onGetServerTime(server_now){ serverTime = server_now endGet() //計測終了 startTime = 0 endTime = 0 } function endGet(){ endTime = new Date() chkLoseTime() } function chkLoseTime(){ zisa = (serverTime - startTime.getTime()) loseTime = (endTime.getTime() - startTime.getTime()) loseTimeHalf = Math.floor(loseTime/2) setByLoseTime() } function setByLoseTime(){ localNow = new Date().getTime() serverNow = localNow + zisa + loseTimeHalf ; if(msgFlg){ var msg= '====================================時差修正レポート====================================' + '\\n接続先サーバー名 : game.gr.jp' + '\\n\\n\\nA クライアントの時間 は ' +(new Date(startTime))+' [ ' + startTime.getTime() + ' ]' + '\\nE サーバー側の時間 は ' +(new Date(serverTime))+' [ ' + serverTime + ' ]' + '\\n\\nF 上記の時差(E-A) は '+zisa+' ミリ秒でした。' + '\\n\\nC ngetservernow.jsを呼び出してから受信するまでの秒数(往復にかかった時間)は '+loseTime+'ミリ秒 なので、' + '\\n\\nD 遅延時間をC/2(片道分)の '+loseTimeHalf+' ミリ秒とします。' + '\\n\\n----------------------------------------------------------------------------------------' + '\\n\\n以上の結果により、クライアントの現在時刻(new Date()).getTime()'+' [ ' + localNow + ' ] へ ' + '\\n時差F[ ' + zisa + ' ] と 遅延秒数D[ ' + loseTimeHalf + ' ] を加算して修正しました。' + '\\n\\n修正後の時間は' +(new Date(serverNow))+' [ ' + serverNow + ' ]' + '\\n\\n\\nこの修正後の時間は JavaScript から 変数 serverNow で取り出してください。' + '\\nまた、(new Date()).getTime()との差分は 変数 sabun で取り出してください。' alert(msg) } } __JS__
http://game.gr.jp/svmix/js/getservernow.js の中身は下記の通りmod_perlでjavascriptを出力している。 #!/usr/local/bin/perl $time = time ; print <<__JS__; Content-type:application/x-javascript /*=============================================================================== * server_now は、「1013065961000」といった1970/1/1からのミリ秒数を整数で返す * server_now_oj は、サーバー側現在時刻オブジェクト * onGetServerTime(server_now) はクライアント側で読み込み時起動する関数呼び出し *------------------------------------------------------------------------------- */ var server_now = $time * 1000 ; var server_now_oj = new Date(server_now) ; onGetServerTime(server_now) __JS__