核心演算法
- 計算兩
DateTime 差值的 TimeSpan。
- 取總秒數
TotalSeconds(double)。
- 安全轉換為
int,處理超出 Int32 範圍情況。
- 決定較早/較晚時間為起訖點。
- 用
Random.Next(Math.Abs(seconds)) 產生隨機秒數偏移。
- 以
AddSeconds() 加到起始時間。
關鍵邊界處理
| 風險 | 原始碼問題 | 解決方案 |
| TotalSeconds > Int32.MaxValue | 直接 (int)d 會溢位回繞成負數 | 截斷為 Int32.MaxValue |
| TotalSeconds < Int32.MinValue | 同理溢位 | 截斷為 Int32.MinValue |
| Math.Abs(Int32.MinValue) | 拋出 OverflowException(因絕對值 2,147,483,648 > MaxValue) | 若 seconds == Int32.MinValue,改用 Int32.MinValue + 1 |
| Random 種子重複 | 短時間多次 new Random() 產生相同序列 | 提供多載接受外部 Random 實例 |
完整實作碼
using System;
public static class DateTimeHelper
{
///
/// 產生隨機日期時間(內部建立 Random,適合單次呼叫)
///
public static DateTime GetRandomTime(DateTime time1, DateTime time2)
=> GetRandomTime(time1, time2, new Random());
///
/// 產生隨機日期時間(接受外部 Random,適合迴圈/高頻呼叫)
///
public static DateTime GetRandomTime(DateTime time1, DateTime time2, Random random)
{
// 1. 計算差值(用 Ticks 避免 TimeSpan 建構溢位)
long ticksDiff = time1.Ticks - time2.Ticks;
double totalSeconds = ticksDiff / (double)TimeSpan.TicksPerSecond;
// 2. 安全轉 int
int totalSecs;
if (totalSeconds > int.MaxValue) totalSecs = int.MaxValue;
else if (totalSeconds < int.MinValue) totalSecs = int.MinValue;
else totalSecs = (int)totalSeconds;
// 3. 決定起訖
DateTime minTime, maxTime;
if (totalSecs > 0) { minTime = time2; maxTime = time1; }
else if (totalSecs < 0) { minTime = time1; maxTime = time2; }
else return time1; // 同一時間
// 4. 處理 Abs(Int32.MinValue) 例外
int maxVal = totalSecs;
if (totalSecs <= int.MinValue) maxVal = int.MinValue + 1;
// 5. 產生偏移並回傳
int offset = random.Next(Math.Abs(maxVal));
return minTime.AddSeconds(offset);
}
}
單元測試驗證邊界
using NUnit.Framework;
[TestFixture]
public class DateTimeHelperTests
{
[Test]
public void FullRange_RandomTimes_AlwaysWithinBounds()
{
var rand = new Random();
DateTime min = DateTime.MinValue;
DateTime max = DateTime.MaxValue;
for (int i = 0; i < 10000; i++)
{
DateTime r1 = DateTimeHelper.GetRandomTime(min, max, rand);
Assert.That(r1, Is.GreaterThanOrEqualTo(min).And.LessThanOrEqualTo(max));
DateTime r2 = DateTimeHelper.GetRandomTime(max, min, rand); // 參數順序互換
Assert.That(r2, Is.GreaterThanOrEqualTo(min).And.LessThanOrEqualTo(max));
}
}
[Test]
public void SameTime_ReturnsSameTime()
{
var now = DateTime.Now;
Assert.AreEqual(now, DateTimeHelper.GetRandomTime(now, now));
}
}
實務補充
- 精度需求: 若需毫秒級隨機,改用
ticksDiff 直接 random.NextLong()(.NET 6+)或自行實作 64 位元隨機。
- 密碼學安全: 需不可預測隨機時,改用
RandomNumberGenerator(System.Security.Cryptography)。
- 效能: 高頻呼叫務必重複使用
Random 實例,避免種子衝突。