VB6/VBA 提供 DateDiff(Interval, Start, End),C# 無內建對應方法。本文提供完整實作,支援秒至年等 8 種間隔單位。
public enum DateInterval
{
Second, Minute, Hour, Day, Week, Month, Quarter, Year
}
public static class DateTimeManager
{
///
/// 計算兩日期時間間隔
///
/// 間隔單位
/// 起始時間
/// 結束時間
/// 間隔值 (可為負,表示 start > end)
public static long DateDiff(DateInterval interval, DateTime startDate, DateTime endDate)
{
TimeSpan ts = endDate - startDate; // 自動處理正負
return interval switch
{
DateInterval.Second => (long)ts.TotalSeconds,
DateInterval.Minute => (long)ts.TotalMinutes,
DateInterval.Hour => (long)ts.TotalHours,
DateInterval.Day => ts.Days,
DateInterval.Week => ts.Days / 7,
DateInterval.Month => (long)Math.Floor(ts.TotalDays / 30.436875), // 平均月長
DateInterval.Quarter => (long)Math.Floor(ts.TotalDays / (30.436875 * 3)),
DateInterval.Year => (long)Math.Floor(ts.TotalDays / 365.2425), // 平均年長(含閏年)
_ => throw new ArgumentOutOfRangeException(nameof(interval))
};
}
}
原始碼直接用 30/365 除法不準確。改良版採用平均曆法常數:
若需「行事曆月份差」(如 1/31 到 2/1 算 1 月),需改用年月運算:
public static int MonthDiffCalendar(DateTime start, DateTime end)
{
return (end.Year - start.Year) * 12 + (end.Month - start.Month) + (end.Day >= start.Day ? 0 : -1);
}
var d1 = DateTime.Now;
var d2 = d1.AddYears(2).AddMonths(3).AddDays(10);
Console.WriteLine($"Days: {DateTimeManager.DateDiff(DateInterval.Day, d1, d2)}");
Console.WriteLine($"Hours: {DateTimeManager.DateDiff(DateInterval.Hour, d1, d2)}");
Console.WriteLine($"Months: {DateTimeManager.DateDiff(DateInterval.Month, d1, d2)}");
Console.WriteLine($"Years: {DateTimeManager.DateDiff(DateInterval.Year, d1, d2)}");
Console.WriteLine($"CalMonths:{MonthDiffCalendar(d1, d2)}"); // 行事曆月差
[TestFixture]
public class DateDiffTests
{
[TestCase(DateInterval.Day, "2024-01-01", "2024-01-10", 9)]
[TestCase(DateInterval.Hour, "2024-01-01 00:00", "2024-01-01 03:30", 3)]
[TestCase(DateInterval.Month, "2024-01-15", "2024-04-15", 3)] // 近似
[TestCase(DateInterval.Year, "2020-01-01", "2024-01-01", 4)]
public void DateDiff_BasicIntervals_ReturnsExpected(DateInterval interval, string s, string e, long expected)
{
Assert.AreEqual(expected, DateTimeManager.DateDiff(interval, DateTime.Parse(s), DateTime.Parse(e)));
}
[Test]
public void ReverseOrder_ReturnsNegative()
{
var d1 = DateTime.Parse("2024-01-10");
var d2 = DateTime.Parse("2024-01-01");
Assert.AreEqual(-9, DateTimeManager.DateDiff(DateInterval.Day, d1, d2));
}
}
TimeSpan 直接提供 TotalDays/TotalHours... 精確到毫秒。DateTime.Subtract 回傳 TimeSpan。Period.Between(start, end, PeriodUnits.Months | PeriodUnits.Days) 提供精確行事曆運算。