DateTime 提供豐富的時間運算方法,支援加減年/月/日/時/分/秒/毫秒/微秒,並能直接比較大小。本文系統整理常用操作、投票系統間隔檢查實例、時區陷阱與最佳實踐。
DateTime now = DateTime.Now;
// 加減年/月/日 (會處理月份天數差異、閏年)
DateTime nextYear = now.AddYears(1);
DateTime nextMonth = now.AddMonths(3);
DateTime nextWeek = now.AddDays(7);
DateTime tomorrow = now.AddDays(1);
DateTime yesterday = now.AddDays(-1);
// 加減時/分/秒/毫秒/微秒
DateTime oneHourLater = now.AddHours(1);
DateTime thirtyMinLater = now.AddMinutes(30);
DateTime oneMinAgo = now.AddMinutes(-1);
DateTime oneSecondLater = now.AddSeconds(1);
DateTime oneMsLater = now.AddMilliseconds(1);
DateTime oneTickLater = now.AddTicks(1); // 100 奈秒
// 通用 TimeSpan 加減
TimeSpan duration = TimeSpan.FromHours(2.5); // 2 小時 30 分
DateTime later = now + duration;
DateTime earlier = now - duration;
// 靜態工廠方法
DateTime specific = new DateTime(2024, 12, 25, 10, 30, 0);
DateTime fromParts = DateTime.Parse("2024-12-25 10:30:00");
DateTime dt1 = new DateTime(2024, 1, 1);
DateTime dt2 = new DateTime(2024, 12, 31);
// 直接比較 (運算子多載)
bool isBefore = dt1 < dt2; // true
bool isAfter = dt1 > dt2; // false
bool isEqual = dt1 == dt2; // false
bool isSameOrBefore = dt1 <= dt2; // true
// CompareTo 方法
int cmp = dt1.CompareTo(dt2); // -1 (dt1 < dt2), 0 (相等), 1 (dt1 > dt2)
// 靜態比較
int cmpStatic = DateTime.Compare(dt1, dt2); // 同上
// 日期部分比較 (忽略時間)
bool sameDay = dt1.Date == dt2.Date;
bool sameDay2 = dt1.Year == dt2.Year && dt1.Month == dt2.Month && dt1.Day == dt2.Day;
// TimeSpan 差值
TimeSpan diff = dt2 - dt1; // 相減得 TimeSpan
double totalDays = diff.TotalDays;
double totalHours = diff.TotalHours;
int days = diff.Days; // 整數天數
public class VoteThrottle
{
private readonly TimeSpan _minInterval;
public VoteThrottle(TimeSpan minInterval)
{
_minInterval = minInterval;
}
public bool CanVote(DateTime lastVoteTime, out TimeSpan remaining)
{
DateTime now = DateTime.UtcNow; // 建議使用 UTC 避免時區問題
DateTime nextAllowed = lastVoteTime + _minInterval;
if (now >= nextAllowed)
{
remaining = TimeSpan.Zero;
return true;
}
remaining = nextAllowed - now;
return false;
}
// 簡化版:僅回傳布林
public bool CanVoteSimple(DateTime lastVoteTime)
{
return DateTime.UtcNow >= lastVoteTime + _minInterval;
}
}
// 使用
var throttle = new VoteThrottle(TimeSpan.FromHours(24)); // 24 小時投一次
DateTime lastVote = DateTime.UtcNow.AddHours(-12); // 12 小時前投過
if (throttle.CanVote(lastVote, out var remaining))
{
Console.WriteLine("可以投票");
// 執行投票邏輯
}
else
{
Console.WriteLine($"尚需等待 {remaining.Hours} 小時 {remaining.Minutes} 分鐘");
}
// 僅加減日期,時間歸零
DateTime dateOnly = DateTime.Today; // 今天 00:00:00
DateTime nextWeekDate = dateOnly.AddDays(7);
// 取得本週一
DateTime today = DateTime.Today;
int daysUntilMonday = ((int)DayOfWeek.Monday - (int)today.DayOfWeek + 7) % 7;
DateTime thisMonday = today.AddDays(daysUntilMonday);
// 取得本月第一天/最後一天
DateTime firstDayOfMonth = new DateTime(today.Year, today.Month, 1);
DateTime lastDayOfMonth = firstDayOfMonth.AddMonths(1).AddDays(-1);
// 取得下個月同一天 (自動處理 31/30/28 天)
DateTime nextMonthSameDay = today.AddMonths(1);
// 危險:混合 Kind 比較
DateTime utc = DateTime.UtcNow; // Kind = Utc
DateTime local = DateTime.Now; // Kind = Local
// utc < local 結果不可預期!
// 安全:統一轉換後比較
DateTime utcNow = DateTime.UtcNow;
DateTime localNow = DateTime.Now;
bool safeCompare = utcNow >= localNow.ToUniversalTime(); // 正確
// 儲存建議:統一儲存 UTC
public class VoteRecord
{
public long VotedAtUtcTicks { get; set; } // 儲存 UTC Ticks
public DateTime VotedAtUtc => new DateTime(VotedAtUtcTicks, DateTimeKind.Utc);
public DateTime VotedAtLocal => VotedAtUtc.ToLocalTime();
}
// DateTimeOffset:內建 Offset,避免 Kind 混淆
var dto = DateTimeOffset.UtcNow;
var later = dto.AddHours(3); // 保留 Offset
// NodaTime:專業日期時間庫 (強烈推薦複雜業務)
using NodaTime;
var clock = SystemClock.Instance;
Instant now = clock.GetCurrentInstant();
ZonedDateTime taipei = now.InZone(DateTimeZoneProviders.Tzdb["Asia/Taipei"]);
var nextWeek = taipei.LocalDateTime.PlusWeeks(1).InZoneLeniently(taipei.Zone);
掌握 `DateTime` 運算細節,能正確實作排程、限流、統計、帳單等時間敏感業務邏輯,避開時區與邊界條件陷阱。