隨機日期產生器可在您的瀏覽器中,從一個含首尾的 YYYY-MM-DD 範圍內均勻取樣產生最多 1,000 個日曆日期,輸出為純日期字串,您可以直接貼入 PostgreSQL 的 INSERT、COPY FROM 或 generate_series 呼叫中使用。由於取樣是在 UTC 整數日序數上進行,因此像 2024-02-29 這樣的閏日與其前後的日子擁有相同的選取資格,且不會因為當地日光節約時間的轉換而遺漏或重複任何日曆日期。輸出為不含時間與時區偏移的純日期,這正是 Postgres DATE 欄位所期待的格式,而含首尾的邊界意味著請求 2024-02-28 到 2024-03-01 時,可以合法地回傳 2 月 28 日、2 月 29 日或 3 月 1 日,而不會產生差一錯誤的意外。隨機性來自瀏覽器的 Web Crypto getRandomValues API,並採用拒絕取樣而非取模的捷徑,因此每個日期背後的 32 位元來源值數量相同,擁有相同的選取機率。重複模式為獨立抽取;唯一模式則執行稀疏的部分 Fisher-Yates 不放回選取,當要求的數量超過含首尾範圍的大小時,會直接拒絕執行而非默默地截斷。

generate random date in postgresql
輕鬆為 PostgreSQL 產生隨機日期

為什麼 PostgreSQL 隨機日期查詢會在日光節約時間附近飄移

多數團隊最先採用的做法,是從一個已知的時間戳記中減去某個隨機比例的天數:

SELECT current_date - (random() * 365)::int;

此模式適用於 DATE 算術,因為右側已經是整數天數,Postgres 會自動為您處理日曆運算。但當欄位是 TIMESTAMP 或 TIMESTAMPTZ,而做法被改寫為 now() - interval '1 day' * (random() * 30),或改寫為 date '2024-01-01' + (random() * 30) * interval '1 day' 時,問題就出現了。將一整數的 interval 加到一個 timestamp with time zone 上,可能會落在與起始時不同的牆上時鐘小時,因為 Postgres 儲存的是瞬間,並以工作階段的時區來呈現。在春前或秋後的星期日,一個逐步加上 24 小時的迴圈,可能會在同一個本地日期產生兩個時間戳記,或直接跳過一個日期——這正是測試人員想要暴露的錯誤,也是他們最不希望固定資料中出現的錯誤。

另一個常見的捷徑是使用 to_timestamp(random() * range)。它會回傳一個具備微秒精度且時區呈現取決於工作階段的值,因此在 psql 中顯示的日期,可能與位於另一時區的應用程式讀取同一欄位時得到的日期不同。在純 SQL 中一個較安全的模式是改用整數運算,並讓 Postgres 建構日期:

SELECT date '2024-01-01' + (random() * 366)::int;

這能避開 interval 機制,但仍依賴資料庫內部的偽隨機數產生器,後者並未公開為可設定種子、可稽核的來源,且難以跨環境重現。當需求是一份可貼入固定資料檔、或可跨機器比較的均勻取樣日期清單時,預先以另一個定義明確的工具來產生日期,通常更快且更容易推理。

瀏覽器版隨機日期產生器的回傳結果

隨機日期產生器是個單頁工具,接收起始日期、結束日期、介於 1 到 1,000 之間的數量,以及一個是否允許重複的旗標,然後以 Postgres DATE 字面語法所用的 YYYY-MM-DD 標準格式產生一份日期清單。兩個端點都可供選取,因此單日範圍是有效的,且必定回傳該日期。清單從含首尾的範圍中抽取,每個日期的選取機率相同,且實作上是計算完整的 UTC 天數,而非以小時相加,這正是該工具能承諾不受日光節約時間影響的根源。

日曆驗證遵循 ECMAScript Date 規範所實作的西曆行為。年份能被 4 整除者一般為閏年,世紀年除非能被 400 整除否則為平年,因此 2000-02-29 與 2024-02-29 為有效輸入,而 1900-02-29 與 2023-02-29 則會被拒絕。月份與日期的溢位也會被拒絕:例如 2025-04-31 這類輸入不會默默地進位為 5 月 1 日。支援的範圍為 0001-01-01 至 9999-12-31,且實作採用 setUTCFullYear 而非 Date.UTC 中舊式的兩位數年份解讀,因此 0001 到 0099 年會保留其字面意義,而不會被位移到二十世紀。

輸入或情境工具行為原因
單日範圍,任何重複設定回傳該日期含首尾的邊界,且至少有一個可選的日期
範圍跨越日光節約時間轉換不會跳過或重複日期以 UTC 日序數取樣,而非本地小時運算
唯一模式,數量超過可用天數回報錯誤,無結果稀疏的部分 Fisher-Yates 選取無法抽取超過可用數量的項目
數量 = 0、空白、負數或超過 1,000回報明確錯誤,不截斷每次產生上限為 1,000 個日期
類似 2025-04-31 的輸入判定為無效並拒絕嚴格的元件檢查,不默默進位

三步驟產生隨機日期範圍

  1. 挑選有效的起始日期與結束日期。 兩個端點皆含首尾,因此出現在起始欄位中的同一個日期也可以出現在輸出中。請使用嚴格的 YYYY-MM-DD 格式;像 2024-02-30、2025-04-31 或 2023-02-29 等值會以明確訊息拒絕,而非默默地被修正。單日範圍是有效的,且必定產生該日期。
  2. 輸入介於 1 到 1,000 之間的數量,並決定是否允許重複。 數量為空白、小數、零、負數或超過 1,000 時,會產生說明允許範圍且未進行截斷的錯誤。若同一日期可重複出現,請開啟重複模式;若只需要不重複的日期,請保持關閉。
  3. 產生清單、檢查顯示的範圍與數量,並複製您需要的值。 每個結果皆為純 YYYY-MM-DD 字串。編輯任一端點、更改數量或切換重複模式時,會清空先前的清單與任何先前的錯誤,因此以較舊設定產生的結果不會殘留在畫面上,誤導為符合目前的控制項。

為了讓步驟更具體,我們以一個會用到含首尾邊界的閏年範圍為例。2024-02-28 到 2024-03-01 的含首尾範圍內,剛好包含 3 個日曆日期:2024-02-28、2024-02-29 與 2024-03-01。每個日期皆可供選取,因此當數量 = 3 且唯一模式開啟時,產生器必定以某種順序回傳全部三者;當數量 = 1 且重複模式開啟時,三者中的任一個皆為有效的單一結果;而當數量 = 4 且唯一模式開啟時,產生器會回報錯誤,而非默默地捨棄其中之一。

將產生的日期載入 PostgreSQL

一旦取得清單,進入 Postgres 最簡單的路徑是將各值包進 VALUES 清單。以三個範例日期為例,INSERT 看起來像這樣:

INSERT INTO orders (placed_on) VALUES ('2024-02-28'), ('2024-02-29'), ('2024-03-01');

對於較大批次,generate_series 可將清單轉為列來源,而不需個別字面值:

INSERT INTO events (event_date, kind)SELECT d, 'signup'FROM generate_series(date '2024-02-28', date '2024-03-01', interval '1 day') AS d;

如果產生的清單剛好就是所需的列,使用 tab 分隔符的 COPY FROM STDIN 是最快的批次路徑:

COPY sample_dates (the_date) FROM stdin; 2024-02-28 2024-02-29 2024-03-01 \.

因為每個結果皆為不含時間與偏移的純日期,在資料列進入 DATE 欄位之前不需做任何時區轉換。對於 TIMESTAMP 欄位,該值可轉型為 the_date::timestamp,或在應用程式端附加 T00:00:00;當不同的工作階段的時區將其讀回時,日期本身並不會改變。

輸入限制、閏日與日曆驗證

此工具每次產生上限為 1,000 個日期,並拒絕默默地截斷,因此當數量為 1,500 時,會產生錯誤訊息而非較短的清單。唯一模式加上第二道上限:當要求的數量大於含首尾範圍內的天數時,工具會回報錯誤,而非回傳較少的列或悄悄切換為重複模式。單日範圍是有效的,且必定回傳該日期,但要從同一範圍反覆產生結果,必須啟用重複模式。

輸入驗證是嚴格的。YYYY-MM-DD 字串會透過在 UTC Date 物件上設定其年、月、日並讀回元件的方式來解析,這意味著像 2025-04-31 這樣的輸入會因為日元件無法在來回轉換中存活而被拒絕。支援 0001 到 9999 年,且實作刻意避開 Date.UTC 舊式的兩位數年份行為,因此像 0099 這樣的年份會保持 0099,而非被悄悄地改寫為 1999。閏日行為符合 ECMAScript 規範中所述的西曆規則,世紀年除非能被 400 整除否則視為平年。

隨機性本身來自瀏覽器的 Web Crypto getRandomValues API,透過無正負號 32 位元字,並以拒絕取樣將這些字對應到日的位置,以消除當範圍大小無法整除 2^32 時,直接取模所引入的模偏差。結果是含首尾範圍內的每個日期,背後的 32 位元來源值數量皆相同,因此每個日期出現的機率相等。此工具不使用 Math.random(),對於可重現的測試,建議的做法是將產生的清單保存為固定資料檔,而非在工具本身中尋求可設定種子的途徑。

Postgres 中隨機日期的常見使用情境

測試固件是最常見的原因。結帳流程測試可能會需要同一季內的十個訂單日期,排程測試可能會需要二十個相異的週間日期,而訂閱續訂測試可能會需要在數百列客戶資料中重複同一日期。duplicate-mode 切換可直接涵蓋第一與第三種情境,unique 模式則可在可用天數不足時直接拒絕大於天數的數量,而不會默默捨棄溢出的部分。

下一個層次是示範排程與寫作靈感。規劃隨機練習的團隊需要一份看起來自然、在時間區間內均勻分布、且沒有明顯人為間隔的日期清單;撰寫背景故事的作者也可以使用同一份清單作為虛構事件的錨點。這兩種情況都能受益於包含式邊界,因為起訖日期通常比中點更令人印象深刻。

展示與文件撰寫也能受惠。當 README 或簡報中顯示 SELECT * FROM orders WHERE placed_on BETWEEN ... 時,若範例列是實際範圍內的實際日期,閱讀起來會更清晰;能夠一鍵重新產生範例清單,也能讓截圖與 SQL 保持同步。這些用途都不涉及財務、法律、競賽、安全性或稽核層面的影響,因此該工具「將產生的日期用於一般實用工作」的指引即可適用;若用途涉及這類後果,採用具有留存證據的獨立稽核程序才是正確的做法。

若想更廣泛地了解包含式範圍抽樣及其背後的日曆運算,如何從任意範圍產生隨機日期 指南從更通用的角度介紹了同一個工具,當目標是不限於 Postgres 工作流程的日期時,是一份實用的參考資料。

若想進一步了解,請參閱 線上隨機 IP 位址產生器是否安全?