我已经写了一个在
DateTime
和
DateTimeOffset
上非常简单的扩展方法来做这件事。我希望它能够像
TimeSpan
的
TotalMonths
属性一样工作:即返回两个日期之间完整月份的计数,忽略任何不足一个月的部分。因为它基于
DateTime.AddMonths()
,所以它尊重不同的月份长度,并返回人们理解的月份期间。
(很遗憾,你无法将其实现为 TimeSpan 的扩展方法,因为它不能保留使用的实际日期知识,而对于月份,它们很重要。)
代码和测试都在
GitHub 上可用。 该代码非常简单:
public static int GetTotalMonthsFrom(this DateTime dt1, DateTime dt2)
{
DateTime earlyDate = (dt1 > dt2) ? dt2.Date : dt1.Date;
DateTime lateDate = (dt1 > dt2) ? dt1.Date : dt2.Date;
// Start with 1 month's difference and keep incrementing
// until we overshoot the late date
int monthsDiff = 1;
while (earlyDate.AddMonths(monthsDiff) <= lateDate)
{
monthsDiff++;
}
return monthsDiff - 1;
}
而且它通过了所有这些单元测试用例:
Assert.AreEqual(1, new DateTime(2014, 1, 1).GetTotalMonthsFrom(new DateTime(2014, 2, 1)));
Assert.AreEqual(0, new DateTime(2014, 1, 1).GetTotalMonthsFrom(new DateTime(2014, 1, 31)));
Assert.AreEqual(1, new DateTime(2014, 1, 1).GetTotalMonthsFrom(new DateTime(2014, 2, 2)));
Assert.AreEqual(1, new DateTime(2014, 1, 31).GetTotalMonthsFrom(new DateTime(2014, 2, 28)));
Assert.AreEqual(1, new DateTime(2012, 2, 29).GetTotalMonthsFrom(new DateTime(2012, 3, 29)));
Assert.AreEqual(11, new DateTime(2012, 1, 1).GetTotalMonthsFrom(new DateTime(2012, 12, 31)));
Assert.AreEqual(12, new DateTime(2012, 1, 1).GetTotalMonthsFrom(new DateTime(2013, 1, 1)));
Assert.AreEqual(12, new DateTime(2012, 2, 29).GetTotalMonthsFrom(new DateTime(2013, 2, 28)));
Assert.AreEqual(1200, new DateTime(2000, 1, 1).GetTotalMonthsFrom(new DateTime(2100, 1, 1)));
Assert.AreEqual(0, new DateTime(2014, 8, 5).GetTotalMonthsFrom(new DateTime(2014, 8, 5)));
Assert.AreEqual(6, new DateTime(2012, 1, 1).GetTotalMonthsFrom(new DateTime(2011, 6, 10)));