測試資料生成、模擬排程、隨機抽獎等情境常需要在兩個日期時間之間產生隨機值。本文提供一個簡潔、可重用的靜態方法實作。
TimeSpan 差異,取得總天數。Random.Next(0, totalDays) 產生 0 到總天數 (不含) 的隨機整數。///
/// 在指定日期區間內產生隨機日期 (含起始日,不含結束日)
///
/// 起始日期時間
/// 結束日期時間 (需大於起始時間)
/// 隨機日期,時間部分歸零 (00:00:00)
public static DateTime RandomTime(DateTime startTime, DateTime endTime)
{
if (endTime <= startTime)
throw new ArgumentException("結束時間必須大於起始時間", nameof(endTime));
// 建議將 Random 設為 static 欄位避免短時間重複實例化導致相同種子
// static readonly Random _rnd = new Random();
Random rd = new Random(); // 簡易示範,生產環境請改用 static 欄位
TimeSpan span = endTime - startTime;
int randomDays = rd.Next(0, span.Days); // 0 ~ Days-1
// 從結束時間往前減去隨機天數,並將時分秒設為 0
DateTime result = endTime.AddDays(-randomDays)
.Date; // .Date 等同 AddHours(0).AddMinutes(0).AddSeconds(0)
return result;
}
DateTime start = new DateTime(2024, 1, 1);
DateTime end = new DateTime(2024, 12, 31);
for (int i = 0; i < 5; i++)
{
DateTime rnd = RandomTime(start, end);
Console.WriteLine(rnd.ToString("yyyy-MM-dd"));
}
/* 輸出範例
2024-03-15
2024-09-22
2024-01-08
2024-11-30
2024-06-17
*/
若需包含時分秒的完整隨機 DateTime:
public static DateTime RandomDateTime(DateTime start, DateTime end)
{
if (end <= start) throw new ArgumentException("end must > start");
long startTicks = start.Ticks;
long endTicks = end.Ticks;
long range = endTicks - startTicks;
// Random.Next 無法直接處理 long,需自行映射
byte[] buffer = new byte[8];
using (var rng = System.Security.Cryptography.RandomNumberGenerator.Create())
{
rng.GetBytes(buffer);
}
long randomTicks = startTicks + (Math.Abs(BitConverter.ToInt64(buffer, 0)) % range);
return new DateTime(randomTicks);
}
ThreadLocal<Random> 或 lock 保護共用實例,或改用 System.Security.Cryptography.RandomNumberGenerator。rd.Next(0, span.Days + 1)。TimeSpan 再向量化運算。