在 JavaScript 中產生隨機日期的可靠做法,是把每一個日曆日視為整數的 UTC 日序數,而非以本地午夜的 Date 物件加上每天 24 小時,然後透過瀏覽器的 Web Crypto getRandomValues API 在該序數範圍內抽樣。Random Date Generator 正是採用這種做法:它會驗證 YYYY-MM-DD 範圍、將兩個端點轉換為相對於 1970-01-01 的整日位置、從該範圍均勻抽取整數日數,並以 UTC 取得器格式化結果,使輸出在日光節約時間切換附近不會多出或漏掉一天。兩個端點皆可被選中、所有閏日與世紀年規則都嚴格執行,整個過程都在你的瀏覽器中完成。你的日期與產生的清單不會被上傳。本文將說明自行撰寫的 JavaScript 程式碼為何容易出錯、如何改用瀏覽器工具,以及將輸出用於測試資料、範例行程表或隨機提示產生器時應注意的事項。

為何天真的 JavaScript 隨機日期程式碼會出錯
最常見的 JavaScript 隨機日期程式碼看似合理:在本地午夜建立兩個 Date 物件、將每個轉為毫秒、把 Math.random() 縮放至該範圍,再把結果加回去。輸出技術上是一個 Date,但在實際工作中有三個地方會悄悄出錯。
第一,日光節約時間的 bug。JavaScript 的 Date 運算是以本地時間進行,因此加上 86,400,000 毫秒不一定剛好推進一個日曆日。在時鐘往前調的那天,結果會落在 01:00,顯示的日期會跳過一個日曆日;在時鐘往後調的那天,結果會落在 23:00,導致日期重複。官方的 ECMAScript Date 規範將此行為描述為預期的本地時間運算,而非瑕疵。乾淨的修正方式是在 UTC 中進行運算。
第二,模數偏差。使用 Math.floor(Math.random() * (max - min + 1)) + min 把隨機浮點數對應到範圍時,在範圍大小無法整除底層刻度時,最高值的權重會稍低。以 32 位元整數作為來源時,這種偏差通常可忽略,但在需要可證明均勻性的競賽、稽核或測試情境中,這足以讓結果不合格。
第三,閏年與溢位驗證。JavaScript 的 Date 建構式會靜默處理無效日期:傳入 4 月 31 日會變成 5 月 1 日而非拋出例外,而以舊式兩位數年份 Date.UTC 解析的 YYYY-MM-DD 字串,會將 0001 到 0099 年偏移到 1900 年代。任何使用 new Date("1900-02-29") 的人最終會得到 3 月 1 日,而非錯誤訊息。
瀏覽器工具透過以整數 UTC 日序數運作、從 Web Crypto 抽樣,並在抽樣前嚴格驗證輸入,從而迴避了上述三個問題。
如何使用瀏覽器工具產生隨機日期
- 在瀏覽器中開啟 Random Date Generator。
- 以 YYYY-MM-DD 格式輸入有效的起始日期與結束日期;兩個端點皆可被選中,因此從 2024-02-28 到 2024-03-01 的範圍可能會回傳 2 月 28 日、該閏日或 3 月 1 日。
- 輸入想要的日期數量,從 1 到 1,000。空白值、小數、零、負數或超過 1,000 的數值會產生錯誤,而不是靜默截斷的清單。
- 決定是否允許同一日期出現多次。對於獨立抽取,請保留重複選項;若要嚴格的無重複清單,請關閉重複。
- 點擊 Generate。結果會以穩定的 YYYY-MM-DD 清單呈現,你可以複製、貼上或記錄下來。
- 編輯任一端點、變更數量或切換重複選項會清除先前的清單與任何先前的錯誤,因此過時的結果不會在不對應的控制項下殘留。
- 在不重複模式下,請勿請求超過含整日範圍實際包含天數的日期。單日範圍每次都會回傳該單一日期,而若要從單日範圍取得重複結果,則必須使用重複模式。
工具在瀏覽器內部做了什麼
所有運算都在本地進行。以 YYYY-MM-DD 格式輸入的嚴格字串,會透過 setUTCFullYear、setUTCMonth 與 setUTCDate 解析到一個 UTC Date 物件,再以 UTC 取得器讀回以確認該日曆日確實存在。其 UTC 毫秒值會除以正好 86,400,000 來取得相對於 1970-01-01 的整數日位置。抽樣在這些整數位置上進行,選中的序數會以 UTC 取得器格式化,因此輸出的 YYYY-MM-DD 與所選序數一致,與瀏覽器的本地時區無關。使用 setUTCFullYear 而非 Date.UTC,可保留字面年份 0001 到 0099,而不會將它們偏移到二十世紀。
隨機性來自 crypto.getRandomValues,使用的是無號 32 位元字,而非 Math.random。該工具會拒絕任何落入尾端尾巴(其長度不是範圍大小的倍數)的 32 位元值,以去除會讓某些日期多出一個可能來源的模數偏差,然後僅對接受的值套用模數。每個被接受的序數因此對應相同數量的底層 32 位元值。當被拒絕的尾巴連續被命中過多次時,有界的安全防護會回報隨機來源失敗。此拒絕抽樣技術的完整細節記載於 W3C Web Cryptography API 規範,而 UTC 日序數的模式遵循 ECMAScript Date Objects 章節。
當允許重複時,每次抽取都會獨立地從整個含端點範圍中抽樣,因此同一日期可以合理地出現兩次。當關閉重複時,產生器會執行稀疏的部分 Fisher-Yates 不放回選擇:每個合資格的日期在每一步都保持均勻可選,先前選中的位置會被移除,記憶體使用量隨所要求的數量成長,而非隨著數百萬天的範圍成長。
抽樣模式:重複 vs 不重複
| 特性 | 啟用重複 | 關閉重複 |
|---|---|---|
| 選擇方式 | 每次抽取獨立從整個含端點範圍中抽樣 | 稀疏的部分 Fisher-Yates 不放回選擇 |
| 均勻性 | 每次抽取在範圍內所有日期上均勻 | 每次抽取在仍未被選中的日期上均勻 |
| 重複值 | 在範圍較小時可能且預期會出現 | 會被拒絕;數量大於範圍會回傳錯誤 |
| 記憶體 | O(count) 次隨機抽取 | O(count) 個位置存放在稀疏集合中 |
| 最適用於 | 獨立挑選、展示、隨機提示 | 為行程表或測試資料挑選 N 個不同的日期 |
兩種模式共用同一個底層的無偏差 Web Crypto 來源與同一套嚴格的 YYYY-MM-DD 驗證,因此唯一的差別在於所選集合是否允許重複。
有效日期範圍與輸入規則
產生器支援從 0001-01-01 到 9999-12-31 的民用日期。它會拒絕任何在格里高利曆上不存在的輸入,而非將其往前或往後遞移。下表顯示哪些會通過、哪些會被回報為無效。
| 輸入範例 | 結果 | 原因 |
|---|---|---|
| 2024-02-29 | 接受 | 2024 年為格里高利曆的閏年 |
| 2000-02-29 | 接受 | 2000 年可被 400 整除 |
| 1900-02-29 | 拒絕 | 1900 年為世紀年但不可被 400 整除 |
| 2023-02-29 | 拒絕 | 2023 年不可被 4 整除 |
| 2025-04-31 | 拒絕 | 4 月只有 30 天;工具不會靜默遞延至 5 月 |
| 0001-01-01 | 接受 | 最早支援的日期,保留為第 1 年 |
| 9999-12-31 | 接受 | 最晚支援的日期 |
| 0100-02-29 | 拒絕 | 第 100 年為世紀年但不可被 400 整除 |
起始日期不得晚於結束日期。單日範圍有效,且必定回傳該日期;若要從單日範圍取得重複結果,必須使用重複模式。
隨機抽樣日期的實際用途
產生的日期清單適用於價值在於多樣性而非意義的非權威性工作。常見情境包括:以逼真但明顯為合成的日期填入測試資料、建立隨機寫作練習、草擬看起來合理的範例行程表、為日期選擇器 UI 提供示範資料,以及為內容產生器建立提示。輸出並非預測、預約、法律截止日、工作日曆、假日清單、時區轉換或時間戳記;它只是一個以均勻方式抽樣的 YYYY-MM-DD 形式民用日期。如果你需要工作日、週末休假、封鎖日期或組織特定的假日,請另行透過 date list generator 或你自己的日曆規則進行篩選。
對於可重現的軟體測試,請儲存本次執行所產生的日期,或使用你自己帶有種子的測試產生器。Web Crypto 在此介面上刻意無法植入種子,因此該工具適合一般實用工作,而不適合需要確定性隨機來源的情境。
若抽樣涉及財務、法律、競賽、安全或稽核後果,請採用具有獨立監督與留存證據的明文程序。每次抽取等機率並不代表特定一次執行看起來間距均勻,而且重複模式可能合理地重複數值,因此請據此規劃,而非反其道而行。
如需更深入的瞭解,請參閱 How to Use a Random IP Address Generator Safely。
如需更深入的瞭解,請參閱 How to Generate Balanced Random Teams Locally。