使用JavaScript Date轉換時間要小心
最近在工作上遇到的一個特殊的問題,當時為了找這個 bug 花了很久時間一直找不到,一直到同事發現透過 Chrome 開發者工具切換時區後才出現;只能說 Chrome dev tool 真的好用。
簡單描述一下問題狀況:
在使用了某個 3rd party library 的 DateRangePicker 來封裝成自己要使用的 component, 而選取後的日期以字串的格式回傳給上一層 component, 而上層 component 也可以傳入已選取的日期區間,所以需要可以解析日期區間的字串成為開始與結束兩個時間。
DateRange String: 03/01/2024 - 03/31/2024 |
但因為 DateRangePicker 接受的 value 格式是一個有 start, end 的物件
{ |
所以需要把上層傳入的字串,轉換成開始與結束日期的字串,再透過 new Date()
轉換為 Date 格式。但是我個人很不習慣美國的時間格式 mm/dd/yyyy
,所以還是喜歡轉成 yyyy-mm-dd
的字串格式來處理。結果就因為這樣做,出現了奇怪的 bug。
value = 03/01/2024 - 03/31/2024 |
Bug 重現的影片
Bug 的原因
最主要的原因是,當使用 new Date()
傳入 dateString 轉換時,遇到 yyyy-mm-dd
的格式,會再根據使用者當地的 timezone 在轉換對應的時間。
// e.g. 2024-03-01 |
這樣可以發現,當使用者時區在 GMT -01:00 ~ -11:00 的時候,轉換後的日期會少一天。
- 父元件傳入的日期,轉換後少一天且設定到 DateRangePicker 元件上
- 觸發了 DateRangePicker 元件上 change 的事件
- 將少了一天的 date range 在傳給父元件
- 父元件收到新的 date range value 後,又更新日期區間再次傳入子元件
- 又回到步驟 1 的情況
所以在這樣的無窮迴圈下,可以看到日期不斷地倒數,似乎生命也在倒數了….XD
可以到 stackblitz 上查看範例,或是直接看 preview,只是需要透過 Chrome Dev Tool 的 Sensor 來更換 Location,然後重整一下頁面檢查一下自己的 location 是否有切換成功。可以透過 new Date().getTimezoneOffset()
來檢查回傳的 timezone offset 如果大於 0,就表示切換成功可以來重現問題。
結論:
轉換時間的處理,可以的話再找個第三方套件來吧!不然就是使用 new Date()
時,乖乖地使用美國日期格式 MM/dd/yyyy
比較能避免意外狀況發生。