錯亂的時間 - 為何我的網頁無法顯示伺服器時間

2018-01-11

Lost in time

首頁圖  Image source

 

0. 楔子

一開始以為很簡單的系統時間顯示, 沒想到處理起來才發現其中隱藏很多技術問題.

這篇主要會說明在實做發現的問題以及解法, 以及最後發現的可行實做的解法供大家做參考.
 

0.1 問題定義

期望結果:有一個php js編寫的網頁, 希望可以透過網頁操作修改系統時間, 並將網頁上的時間更新.

已知問題:目前可以透過網頁修改系統時間, 但無法將系統時間更新至網頁顯示. 之前沒有發現這個問題是因為, 系統和瀏覽器都在同一台電腦, 這次的問題就是發生在系統和瀏覽器在不同電腦上.

實做時間功能:系統時間是對系統下linux 指令 date , 瀏覽器時間則是用moment.js 的 moment() 函式取得

欲解決之問題:如何讓網頁顯示伺服器的時間

建議解法:計算兩者時間差, 使用moment().add() 完成系統時間的計算

 

1. What is moment.js

就是一個已經寫好的javascript提供許多容易理解以及實用的函數供網頁開發者處理時間顯示,轉換等功能

如果需要處理時區資訊還有另外一個姊妹作 moment-timezone.js

moment.js

    使用範例  /  基本入門

moment-timezone.js

    使用範例  /  基本入門

 

2. 實戰篇

這邊不介紹實做細節,因為這都不是最後採用的解法,但因為這個過程中學到不少東西,所以就把有用的一些資訊整理紀錄下來作分享.

2.1 moment().add(兩地時間差,'milliseconds')

  • 系統的date 也可以調整輸出格式(更多細節
  • moment() 直接使用就是顯示瀏覽器的時間, 至於如何調整時間顯示格式則可透過moment().format() (更多細節
  • time format:
        hh (01~12) vs HH (00~23) 如果有使用am/pm 要注意使用的時間格式
  • timezone format :
        z / zz 這個會回傳時區縮寫
        Z ZZ 這個才是會回傳已中央標準時間(GMT)-07:00 ~07:00 
  • 時差計算:(更多細節
        moment().diff(Moment|String|Number|Date|Array);
  • js date and moment object conversion: 
        建議把javascript date 使用 toIsoString() 轉換格式後再配合moment的物件或是函式使用
  • 更多其他有趣使用plugin 可以參考 這裡

2.2 moment.tz(伺服器時區).format()

  • 如果有使用到時區資訊請使用moment-timezone-with-data / moment-timezone-with-data-20xx-20xx,否則timezone資訊找不到會造成時間無法顯示
  • javascript使用上有呼叫先後順序, 錯誤的擺放引用順序會產生物件或是函式未定義的情況發生
    <script src="/path/to/my/js/moment.js"></script>
    <script src="/path/to/my/js/moment-timezone.js"></script>
    

     

3. 真 ‧ 解法

其實這裡解法並非唯一

php解法說明  (參考連結

  • php 可以直接取得系統時間資訊 (參考連結
  • php 可以將值傳到javascript上 (參考連結
    var currenttime = '<? print date("F d, Y H:i:s", time())?>';
    var serverdate=new Date(currenttime);

     

js 解法說明 (參考連結

  • javascript 可以透過網頁特殊欄位取得系統時間資訊
    完整欄位資訊可以透過getAllResponseHeaders取得 (範例
  • 整體分成三個步驟
    1. fetchServerTime:取得系統時間計算時間差
    2. timerHandler:目前顯示時間=目前時間moment()+時間差
    3. updateDisplay :更新目前顯示時間

 

4. 後記 - 問題探索過程摘要

當然對於一個之前非網頁專門工程師來說, 中間當然要借助不少開發者工具,或是線上沙盒幫忙, 最後才會知道說原來兩邊說的時間是不一樣的. linux 指令 date:這個毫無疑問可以取得伺服器時間, 而moment.js 的 moment() 則是回報瀏覽器的時間這會因為使用者所在時區或自行設定的時間不同而有所不同. 剛開始的確是按照建議的方式去執行,並依照建議將linux date得到的結果透過toISOString(), 但結果是時間會因為時區計算錯誤而導致顯示時間和想要設定的時間差了幾個小時大致慘況是這樣

網頁角度:
瀏覽器目前所在時區為GMT+08:00
目前顯示的時間為 2018/01/10 17:36 GMT+08:00
伺服器目前時區為 GMT-04:00 而時間又是調整過成為 2018/01/10 09:22 那瀏覽器會怎解釋呢?

瀏覽器會先把 要設定的時間轉為一致的時區再做計算 (-4 → +8)
2018/01/10 09:22 (-04:00) → 2018/01/10 21:22 (+08:00)
瀏覽器就會看到 2018/01/10 21 :22 GMT+08:00

使用者角度:

看到時間從 09:22 變成 21:22
使用者就崩潰了 >< (為什麼跟我設定的時間不一樣)

於是我又開始往更麻煩的地方去想:時間差=兩地時間差+兩地時差
過程中還有使用到系統時區資訊將最後顯示到的網頁時間重新調整

var dispDate=moment(iso格式時間).tz(系統時區).format(時間格式)

但是這次又變成不會更新了!!原因是我的系統時間只會再有變更或是頁面重新載入時才會更新, 除此之外並不會自動更新
其實要自動更新並不是不行, 而是目前使用的方法每一次對系統詢問或是設定至少都需要0.2ms,所以每秒都要更新的時間, 這樣是不切實際的

所以到頭來才發現
1.要取得系統時間不麻煩
2.取得時間方法不唯一

知道這兩點剩下就輕鬆了 surprise

常用資源

1. w3cshools - js

2. w3cshools - php

3. online php sandbox

4. jsfiddle

5. php manual

標籤
javascript
php
symfony
linux
date
time synchronization

留言

mark on 2018/01/11, 週四 - 16:28

這篇算是筆記, 所以除非是有寫過網頁經驗的人,不然應該很少有人對這類文章有興趣.

不過還是謝謝你的回饋yes

sean on 2018/01/11, 週四 - 17:21

Wow, it's very cool yes

The first thing comes to my mind when reading the problem you encountered
is that javascript runs when rendering on client side. (?)

Thus, it seems not possible GET server time at this moment.

I may try first using PHP way to attach server time info responding this request,
and js can rendering it out~

Is it possible js actively send request to server during client rendering ?

Dear sean,

Ha ! Actually I'm just a js newbie. 

Yes. It's possible.  setInterval is the solution, it can send request based on the timer you set.

You can also write your 'send request' as a self-invoking function when the script loaded.

However, the real problem would be

"Can user wait while js actively send request over and over again ?"

 

Hope this helps.

我帮你顶上博文榜首,下次你也要给我留言,记得礼尚往来呃!wink

謝謝Michelle同學的支持

留言當然是沒有問題的yes, 但就怕留言是文不對題 cheeky

jonas on 2018/01/12, 週五 - 09:57

嘿 馬克,感謝你發了一篇深度文yes

想(因為現在沒悄悄話功能,已跟工程師許願)跟你說一個小部分:就是這篇的「常用資源」裡面的文字w3school改成w3schools比較好。

因為w3school 是大陸版,不一樣唷~
http://w3school.com.cn/html/index.asp

增加悄悄话功能,也是我想说的。另外,如果系统能提示有评论或留言就好了,不然还要一篇篇去刷,好麻烦。

 

Jonas 同學好細心

謝謝你的提醒,已依照建議修改內容

也希望你繼續支持我的部落格smiley