一。new Date()
據說有 4 種方式:
new Date();
new Date(value);
new Date(dateString);
new Date(year, month[, day[, hour[, minutes[, seconds[, milliseconds]]]]]);
第 1 種用來取時間戳:
// get ts
var now = Date.now || function() {
return new Date().getTime();
};
第 2 種把時間戳轉 Date 對象:
// ts to Date
var ts2Date = function(ts) {
return new Date(ts);
};
第 3 種把日期時間字符串轉 Date 對象:
// string to Date
// eg. '2016/10/01 12:01:02'
// '2016/10/01'
var str2Date = function(str) {
return new Date(str);
};
第 4 種一般不用,因為第 4 種有一個很難受的問題(month 是0-11):
// set Date
// eg. 2016, 9, 1, 12, 1, 2
// 2016, 9, 1
var setDate = function(y, m, d, h, mm, s, ms) {
return new Date(y, m, d, h, mm, s, ms);
};
// getter/setter 中,month 是 0-11
var date = new Date(); // 今天是 2016-10-01
date.getMonth(); // 9
date.setMonth(10);
date.toString(); // Tue Nov 01 2016 11:46:58 GMT+0800 (China Standard Time)
第 4 種方法內部用的可能是 setter,所以 month 從 0 開始
比較有用的是第 3 種,因為一般需要處理的日期串都是 ISO 標準格式,比如 2016-10-01 12:01:02,但這在 JS 中是非標準的
二。日期時間串格式
非標準格式
先看這兩個東西:
// new 一個 0 區時間
new Date('2016-10-01'); // Sat Oct 01 2016 08:00:00 GMT+0800 (China Standard Time)
// new 一個當前時區時間
new Date('2016/10/01'); // Sat Oct 01 2016 00:00:00 GMT+0800 (China Standard Time)
第二個正確,第一個多了 8 小時,瀏覽器認為第一種傳入的是 0 區時間,就為東八區用戶私自加了 8 小時,分別等價於:
// Sat Oct 01 2016 08:00:00 GMT+0800 (China Standard Time)
new Date('01 October, 2016 GMT+0000');
// Sat Oct 01 2016 00:00:00 GMT+0800 (China Standard Time)
new Date('01 October, 2016 GMT+0800');
所以千萬不要用yyyy-MM-dd,就算非常確定想要 new 個 0 區時間,也不應該這麼做,因為別人可能看不懂,如果真想要 new 個 0 區時間,應該用其等價格式(帶有 GMT 時區標識的)
添上時間 'HH:mm:ss' 再試試:
// new 一個 0 區時間
// Sat Oct 01 2016 12:01:02 GMT+0800 (China Standard Time)
new Date('2016-10-01 12:01:02');
// new 一個當前時區時間
// Sat Oct 01 2016 12:01:02 GMT+0800 (China Standard Time)
new Date('2016/10/01 12:01:02');
竟然一致了,但是Safari(包括 IOS Safari)不支持yyyy-MM-dd HH:mm:ss 格式,Safari 從開始到現在(2016-10-01)都不支持這種格式,因為是非規範的,可能不打算支持
雖然 yyyy/MM/dd HH:mm:ss 也是非規範的,但因為歷史悠久,包括 IE6 在內的所有瀏覽器都支持該格式
所以,得到廣泛支持的非標準格式有兩個:
// eg. 2016/10/01 12:01:02
yyyy/MM/dd HH:mm:ss
// eg. 01 October, 2016 12:01:02 GMT+0800
week, dd month yyyy HH:mm:ss GMT±HHmm
前者默認當前時區時間,後者可以設置時區
標準格式
直到 ES5.1 15.9.1.15 Date Time String Format 才有了日期字符串的格式標準:
// 0 區時間
YYYY-MM-DDTHH:mm:ss.sssZ
// 東邊時間
YYYY-MM-DDTHH:mm:ss.sss+HH:mm
// 西邊時間
YYYY-MM-DDTHH:mm:ss.sss-HH:mm
這份規範是 2011 年 6 月的,所以兼容性可想而知,樣子比較奇怪,但恪守標準的 Safari 是支持的,例如:
// 0 區時間
// Sat Oct 01 2016 20:01:02 GMT+0800 (CST)
new Date('2016-10-01T12:01:02Z')
// 東 8 區時間
// Sat Oct 01 2016 12:01:02 GMT+0800 (CST)
new Date('2016-10-01T12:01:02+08:00')
同樣的,0 區時間還是會被私自加上時區偏移,那麼很容易想到這兩個:
new Date('2016-10-01T12:01:02+00:00')
new Date('2016-10-01T12:01:02-00:00')
它們全都等價於 Z,同樣會被本地化偏移
三。GMT 與 UTC
一般可以認為這兩個東西是相同的,都表示世界標準時間,而北京位於東八區,那麼:
// 一般可以認為
北京時間 === UTC+8 === GMT+8
UTC 與 GMT 在時區的概念上一致,但 UTC 強調精確,誤差更小,這個誤差是相對 UT 來說的
GMT
GMT(Greenwich Mean Time 格林威治標準時間)是最早定義的世界時間。1884 年,各國代表跑去華盛頓開會,對時區達成共識,以本初子午線為中心(位於 0 區),向東 1 到 11 區,向西 1 到 11 區,共用 12 區(叫「東西 12 區」),一共24 個時區
由 0 區發布標準時間,然後其它區加上對應的偏移量得到本地時間
UT
UT(Universal Time 世界時)是基於天體觀察計算出來的時間。UT 本身是一個廣泛的概念,其下包括 UT0,UT1,UT2 等。其中 UT0 是完全按照天體運行計算出來的時間,UT1 是在 UT0 的基礎上做了一些調整,UT2 是在 UT0 和 UT1 的基礎上又進行了一些調整。由於天體運行的一些不確定性 (比如地球的自轉並非勻速的,而是以複雜的方式進行著加速和減速),所以 UT 時間並不是均勻流過的。
UT 就是理論上的「正確」時間
UTC
UTC(Universal Time Coordinate 協調世界時)是基於原子時鐘的時間。什麼是原子時鐘?個人認為就是一個很小的,長度固定的,不可再分的時間段。所以 UTC 的時間是均勻的。為了能夠盡量減小和 UT 時間的誤差,UTC 引入了閏秒(在某些年份的最後一分鐘是 61 秒),以確保 UTC 是 UT1 之間的誤差在 0.9 秒之內。
UTC 認同 GMT 的時區概念,所以 UTC 相當於給 GMT 換了塊更高級的表,GMT 用的什麼表就不知道了
四。潛在問題
與北京時間相差最多的是西 12 區(只有幾個小島):
deltaH = (+8) - (-12) = 20
如果做了一個本地時間判斷,比如:
if (new Date() > new Date('2016/10/02')) {
alert('a new day');
}
那麼不同時區的用戶都將在手頭的設備顯示 2016 年 10 月 2 日 0 點 0 分非 0 毫秒時看到 a new day,這沒問題,但如果場景是這樣:
if (new Date() > new Date('2016/10/02')) {
alert('考試時間到,3 秒後回到首頁');
setTimeout(function() {
location.href = '/index.html';
}, 3000);
}
此時問題就大了,北京時間 2016 年 10 月 2 日 0 點 0 分非 0 毫秒,試卷狀態變更為 CLOSED,不再接受提交,那麼位於其它時區的用戶就發現問題了:
-
住在西 12 區的朋友還有 20 個小時慢慢答題,交卷時發現人家不要
-
住在東 12 區的朋友更慘,被提前 4 小時收卷了
當然,我們一般都是進頁面先問服務要狀態,以服務器時間為準,因為本地時間不可靠(用戶可以隨便修改設備時間),另一個原因就是上面提到的:時區的差異會讓位於其它時區的用戶很困惑
所以,即便非要用本地時間判斷,也應該這樣做:
if (new Date() > new Date('02 October, 2016 GMT+0800')) {
alert('考試時間到,3 秒後回到首頁');
setTimeout(function() {
location.href = '/index.html';
}, 3000);
}
拿用戶本地時間與北京時間比,至少在用戶設備時間的情況下,能保證狀態一致。當然,手動改時間的問題是沒有辦法避免的(比如以前的某些農場遊戲用的是本地時間),所以盡量不要把本地時間作為判斷依據
五。總結
本地時間是指設備所在時區時間
兼容性最好的時間串格式:
// 不支持時區
2016/10/01 12:01:02
yyyy/MM/dd HH:mm:ss
// 支持時區
new Date('01 October, 2016 GMT+0800')
week, dd month yyyy HH:mm:ss GMT±HHmm
ES5.1 標準時間串格式:
2016-10-01T12:01:02+08:00
YYYY-MM-DDTHH:mm:ss.sss±HH:mm
千萬不要用的時間串格式:
// 兼容性沒問題,但會被強制偏移,比如設備在北京就 +8 小時
yyyy-MM-dd
// Safari 全家都不支持,IE6-11 都不支持
yyyy-MM-dd HH:mm:ss
參考資料
-
前端工程研究:關於 JavaScript 中 Date 型別的常見地雷與建議作法:又發現一位 MVP 前輩
暫無評論,快來發表你的看法吧