上篇提到DateTime的顯示格式,忽略了一個重要元素--時區。多時區用戶的項目的時間顯示問題常常被人詬病,一旦時間跟金錢掛鈎,出了問題就不是客服投訴罵兩句那么簡單了。那么究竟怎樣才能構建一個滿足多時區用戶的項目呢?這要先從時區說起。
時區知多少
如圖,全球被划分為24個時區,0時區為基准,每個時區相隔1小時,往東則加時間,往西則減時間,這種記時方式成為UTC(協調世界時)。比如北京在東邊第八個時區,簡稱東八區,而東八區可簡寫為“UTC +8”。
在人類科技尚不發達的早期,人們用天文學知識計算時區時間,稱為GMT(格林威治時間)。這種方式略有誤差,但日常生活並不影響,后來隨着科技的進步漸漸被UTC取代,但依然有人習慣性稱GMT時間,因此,對我們來說,UTC和GMT意思等同。
在.Net中,用DateTime的UtcNow屬性可快速獲取0時區的時間,以我們所處的“UTC +8”為例,兩者相差8小時:
Console .WriteLine( DateTime .UtcNow); // 2012/12/21 3:50:37
可定義時區的時間類-- DateTimeOffSet
DateTime類沒有時區信息,默認當前機器的時區。如果想指定時區,可使用DateTimeOffset類,時差用TimeSpan表示。比如,以下代碼分別獲取當前“UTC +2”和“UTC -5”的時間:
DateTimeOffset dt2 = new DateTimeOffset ( DateTimeOffset .UtcNow.DateTime, TimeSpan .FromMinutes(-300));
為了方便,我用小時計算時差,但更准確的的單位應該用分鍾。因為時區雖按小時划分,但有些城市地區所在的時區並不是整數,比如印度的新德里在東5.5區,加拿大的紐芬蘭在西3.5區,還有一個逆天的尼泊爾在東5.45區,因此把時差換算為分鍾更為准確。
注:本來第一個參數使用DateTime.UtcNow,結果會拋出異常。因為第一個參數必須是時區為0的DataTime,而通過DateTime類屬性獲取的時間會默認當前機器的時區,因此,改成DateTimeOffset.UtcNow.DateTime就沒有問題。
DateTime雖然默認了時區值,但無法獲取,如要訪問,需使用DateTimeOffset類的Offset屬性,代碼如下:
Console .WriteLine(dt.Offset); // 02:00:00
獲取用戶的時區信息
上面所述都是獲取服務器時區的相關信息,那么,如何獲取訪問我們網站的用戶的時區信息呢?很可惜,瀏覽器發送請求的時候並不帶自身的時區,不過幸運的是,javascript提供了用戶的時區信息。代碼如下,運行結果如圖。
alert(date);
如果你想獲取用戶在哪個時區,只能從字符串中拆分,不過javascript提供了函數可以直接獲取時差,單位為分鍾。
如何應對多時區的用戶
一個多時區的項目,如果在設計初考慮到時區,應將時間存為DateTime.UtcNow,這樣結合時差可得任意時區時間。如果一個項目已存成了DateTime.Now,結果半路殺出多時區問題,除了罵娘以外,只能將時間切換為0時區,再根據時差計算。通常獲取用戶時區的做法是--javascript獲取時差存入表單,並提交到服務器。
可是,並不是每次頁面訪問都有提交表單操作,如訪問首頁。如此一來必須要面對兩個選擇:
- 將時間獲取到頁面上,通過javascript根據時差換算。
- 將用戶時差保存在數據庫。
第一種麻煩且難以維護,容易造成重復代碼;第二種一旦碰到用戶時差改變,數據就沒了意義。最重要一點是數據真實性,因為javascript獲取的時間取自當前電腦,即該時間可隨意更改。且要命的是,我們通過時區獲取時差,時差得到時間,這個過程本身是不准確的,因為有夏令時的存在。
也就是說,如果時間顯示無關輕重,可采用以上應對方法。一旦時間和利益掛鈎,就會造成很大的問題。那么,究竟該如何應對多時區用戶的問題呢?下篇解答,未完待續。 ^_^
感謝園友匹配度的提醒和留言! ^_^