不同Node版本導致的Date建構函式問題及解決方法

發表於2018-07-06

近期在封裝時間選擇元件的單元測試時,為了構造出Date物件,直接使用了預設Date建構函式。自己本地開發,測試均無問題,push遠端後,某個小夥伴在本地跑測試用例時,卻無法通過,具體報錯如下:

不同Node版本導致的Date建構函式問題及解決方法

通過截圖資訊,可以初步判斷由於Date建構函式返回了不同日期導致,抱著好奇的態度查閱個各種資料後,竟然發現一個小小的日期建構函式裡面大有文章,平時自己寫起來都是淺嘗輒止,沒有深入瞭解過。下面將詳細介紹這個破案過程,以免各位看客後續重蹈覆轍。

問題排查

按照一貫做法,出問題後先自己本地跑了一次測試用例,沒有任何問題,初步就可以定位是開發環境問題。於是乎就看了下小夥伴nodejs版本號,版本號為6.10.0,而自己本地node版本號為10.3.0,於是在不同nodejs命令列下直接執行如下測試用例。

執行結果,

Node 6.10.0:

Node 10.3.0:

到此基本確認了該問題是由Nodejs環境導致的問題。但是為什麼會有這樣的問題呢,跟著我繼續深入探祕下Date建構函式。

深入分析

結合問題,提煉出以下小示例,以供深入分析Date建構函式:

nodejs 10.3.0執行結果:

nodejs 6.10.0執行結果:

為什麼在不同環境下Nodejs的解析行為不一樣呢?這就要提下JS中涉及到時間的相關規範了。

相關規範

ISO8601標準[參考5]

該標準指定了如果為指定偏移時間就預設為當前時間。

不同Node版本導致的Date建構函式問題及解決方法

[ES5 規範][參考6]

指出瞭如果沒有指定偏移量,預設偏移量為Z。

不同Node版本導致的Date建構函式問題及解決方法

[ES6 規範][參考7]

為了和ISO8601標準一致,又對該規範做了更改,如果時區偏移量不存在,日期時間將被解釋為本地時間。

不同Node版本導致的Date建構函式問題及解決方法

原始碼分析

為了確認該問題是由於不同規範導致的,我們就需要看下V8原始碼裡面的實現了。 獲取不同node版本對應的v8版本號,如下圖所示:

檢視 v8 的不同版本下git提交記錄可看到在6.6版本上已經增加了對ES6規範的支援 ,實現瞭如果時區偏移量不存在,日期時間將被解釋為本地時間的效果。

不同Node版本導致的Date建構函式問題及解決方法

問題總結

回頭看文章開頭的用的日期建構函式導致的bug,就可以解釋”1995-12-17T00:00:00″ 在低版本下輸出1995-12-17T08:00:00,而高版本下輸出1995-12-17T00:00:00的問題了。

通過上述規範和原始碼,低版本由於會加預設偏移量Z,預設就解析成0時區的時間,而我們在東八區,所以最終我們本地的時間是1995-12-17T08:00:00,高版本下由於沒有Z,預設會解析成本地時間,輸出結果最終就是1995-12-17T00:00:00。

問題解決方案就是隻需要加上時間偏移量即可,如下new Date(‘1995-12-17T03:24:00+08:00’)。

經驗教訓

由於瀏覽器的差異和不一致,強烈建議不要 使用Date建構函式解析日期字串(並且Date.parse它們是等價的)。

儘可能使用“YYYY / MM / DD”作為日期字串,或者使用年月時分秒的建構函式來構造Date物件,他們得到普遍地支援。有了這種格式,所有的時間都是本地的。

除非您知道自己在做什麼,否則請避免使用帶有連字元號的日期(”YYYY-MM-DD”),只有較新的瀏覽器支援它們。

參考

[1]https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Date/parse

[2]https://codereview.chromium.org/1229903004

[3]https://stackoverflow.com/questions/2587345/why-does-date-parse-give-incorrect-results/20463521#20463521

[4]https://stackoverflow.com/questions/19278797/inconsistant-date-parsing-with-missing-timezone/19279013#19279013

[5]https://en.wikipedia.org/wiki/ISO_8601

[6]http://www.ecma-international.org/ecma-262/5.1/#sec-15.9.1.15

[7]http://www.ecma-international.org/ecma-262/6.0/#sec-date-time-string-format

相關文章