在日常生活中,“2025/05/28” 和 “2025-05-28” 可能只是兩種日期寫法的區別——前者看起來更像美式格式,后者則更標準化一些。但在編程世界里,這種寫法的不同,可能意味著完全不同的時間點。特別是在 JavaScript 中,這兩者居然會被解析成不同的一天!
這種現象最近在 Hacker News 上引發了不少關注,不少開發者都表示曾在這類問題上“踩坑”甚至 debug 到深夜。一位網友就評論說:“我調了半天的日期 bug,最后才發現是字符串寫法惹的禍。”還有人驚呼:“我們都以為是用戶格式輸錯,結果是 JavaScript 的鍋。”
對此,開發者 Brandon Dong 專門寫了一篇《Why are 2025/05/28 and 2025-05-28 different days in JavaScript?》文章深入分析了背后的原因,不僅揭示了 Date 構造函數令人費解的行為,還帶你回顧了一段瀏覽器與 ECMAScript 標準之間十多年來“反復橫跳”的歷史。接下來就讓我們一起看看,JavaScript 為什么會把明明寫著“2025 年 5 月 28 日”的兩個字符串,解析成兩個不一樣的時間點。
原文鏈接:https://brandondong.github.io/blog/javascript_dates/
作者 | Brandon Dong
翻譯 | 蘇宓
出品 | CSDN(ID:CSDNnews)
在搭建個人網站的過程中,我遇到了一個奇怪的現象:
console.log(new Date('2025/05/28').toDateString()); // Wed May 28 2025
console.log(new Date('2025-05-28').toDateString()); // Tue May 27 2025
// Bonus: (omit leading 0)
console.log(new Date('2025-5-28').toDateString()); // Wed May 28 2025
你在自己電腦上運行時可能會得到不同的結果!
發生了什么?
在 JavaScript 中,一個 Date 對象始終表示一個時間點(即自 Unix 紀元以來的毫秒數)。這一點在輸出完整時間字符串時更為明顯:
const date = new Date('2025/05/28');
console.log(date); // Wed May 28 2025 00:00:00 GMT-0700 (Pacific Daylight Time)
console.log(date.toDateString()); // Wed May 28 2025
在這種情況下,傳入的日期字符串被解釋為本地時區的時間戳。toDateString() 也相對于本地時間操作,因此我們得到了相同的日期。
而'2025-05-28' 的不同之處在于解析行為;該字符串被解釋為 UTC 時間,因此最終處于不同的時間點:
const date = new Date('2025-05-28');
console.log(date); // Tue May 27 2025 17:00:00 GMT-0700 (Pacific Daylight Time)
console.log(date.toDateString()); // Tue May 27 2025
為什么會有這種差異?
瀏覽器日期解析的奇妙旅程
在查閱了 Chrome/Firefox/Safari 的代碼和提交歷史后,我梳理出了一個時間線:
2009 年:各大瀏覽器支持解析各種日期時間格式。當字符串中沒有明確指定時區時,它們默認按本地時間解析,包括像 '2025/05/28' 這樣的日期格式。
同年晚些時候:即將發布的 ES5 標準提出了一種基于 ISO 8601 的新日期格式標準,包含一種純日期形式:'2025-05-28',還有另一種日期+時間形式:'2025-05-27T17:00-07:00'(其中時區偏移可以省略)
那么對于沒有偏移信息的純日期形式或省略偏移的日期時間形式,規范是怎么說的呢?答案是:字符串的時間可以被解釋為本地時間、UTC 時間或其他時區時間,取決于字符串的內容。
Firefox 是第一個實現這一要求的瀏覽器。他們選擇將僅日期形式解釋為 UTC 時間,將缺少偏移量的日期時間形式解釋為本地時間。這不僅導致 '2025/05/28' 和 '2025-05-28' 之間存在差異,還出現了令人驚訝的行為,例如:
console.log(new Date('2025-05-28')); // Tue May 27 2025 17:00:00 GMT-0700
console.log(new Date('2025-05-28T00:00')); // Wed May 28 2025 00:00:00 GMT-0700
接下來是 Chrome,它選擇對這兩種情況都使用本地時間。
然后是 Safari,但其解析邏輯有 bug:要求必須提供日期、時間和偏移字段。
2011 年年中發布的 ES5.1 進一步補充:如果缺少時區偏移,默認用 Z(即 UTC)。
Chrome 更新了其實現,對兩種情況都按 UTC 時間解析。
Safari 修復 bug,也改為使用 UTC 時間。
隨后有人指出這種規范本身存在一個 bug:指出 ISO 8601 實際上規定“無偏移的日期時間”應被解釋為本地時間。2015 年,ES6 將之前的說法替換為:如果省略時區偏移,日期時間應被解釋為本地時間。
Chrome 切換回對兩種情況都使用本地時間。
之后又有人抱怨 Chrome 在解析純日期格式時破壞了兼容性,Chrome 又改回 UTC。
Chrome 進一步向規范提交問題。經過討論后,達成折中方案。
純日期形式→ UTC(Firefox 風格)
缺偏移的日期時間形式→ 本地時間
ES7正式采用上述行為。Chrome 率先實現,隨后 Safari 也跟進。
到今天為止,這個規則仍在沿用:只要傳入的是合法的 ISO 日期字符串(如 '2025-05-28'),構造函數就會默認使用 UTC,否則一律用本地時間。
這種行為一直保持到今天,其中 Date 構造函數接受的每個可能的字符串都回退到本地時間,除了像 '2025-05-28' 這樣的有效 ISO 日期字符串。
看這個時間線,有趣的是,盡管它被設計為標準化格式,但從 2009 年發布到 2020 年初,主要瀏覽器在缺少偏移量的情況下從未有過一致的行為。同時,Chrome 在解析 '2025-05-28T00:00' 時搞笑地從本地→UTC→本地→UTC→本地來回切換。而這一切只是為了最終采用 Firefox 2009 年的行為,在我看來,這是所有行為中最不直觀的。
那Temporal 呢?
對于不知道的人來說,JavaScript Temporal 即將到來:這是一組新的日期和時間 API,旨在取代 Date 對象。
我們最初的整個日期解析問題源于時區的歧義,但在許多情況下,我們希望將僅日期的字符串完全視為日期。例如,當我說今年的圣誕節是 2025-12-25 時,我并不是指 2025-12-25T00:00:00.000Z 這個通用的時間點。
雖然 Date 只能表示后者,但 Temporal 提供了純日期(即沒有時區的日期)的選項。'2025-12-25' 就只是 '2025-12-25',完全避開了解析歧義的問題。
但是,如果確實想將僅日期的字符串解析為一個時間點呢?當字符串本身缺少時區時,Temporal 會選擇哪個時區?
答案是:根本不會讓你這么干。
Temporal 在這方面是嚴格錯誤(hard error):必須明確提供時區偏移或時區標識符,否則它就直接拋錯,根本不給你機會“犯老毛病”。
2025 全球產品經理大會
2025 年 8 月 15–16 日
北京·威斯汀酒店
2025 全球產品經理大會將匯聚互聯網大廠、AI 創業公司、ToB/ToC 實戰一線的產品人,圍繞產品設計、用戶體驗、增長運營、智能落地等核心議題,展開 12 大專題分享,洞察趨勢、拆解路徑、對話未來。
特別聲明:以上內容(如有圖片或視頻亦包括在內)為自媒體平臺“網易號”用戶上傳并發布,本平臺僅提供信息存儲服務。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.