计算两个日期之间的月份差异

Din*_*nah 121 .net c# datetime timespan

在C#/.NET TimeSpanTotalDays,TotalMinutes等等,但我无法弄清楚总月差的公式.每月可变天数和闰年一直让我失望.我如何获得TotalMonths

编辑抱歉没有更清楚:我知道我实际上无法得到这个,TimeSpan但我想使用TotalDays,TotalMinutes并将是一个很好的例子来表达我正在寻找...除了我想要得到总月.

示例:2009年12月25日 - 2009年10月6日= 2 TotalMonths.10月6日至11月5日等于0个月.11月6日,1个月.12月6日,2个月

Ada*_*son 216

你将无法从a获得TimeSpan,因为"月"是一个可变的度量单位.你必须自己计算它,你必须弄清楚你希望它是如何工作的.

例如,要日期喜欢July 5, 2009August 4, 2009产生一个月或零月区别?如果你说这应该产生一个,然后怎么样July 31, 2009August 1, 2009?是一个月?它只是Month日期值的差异,还是与实际的时间跨度更相关?确定所有这些规则的逻辑非常重要,因此您必须确定自己的规则并实施适当的算法.

如果您只想要几个月的差异 - 完全忽略日期值 - 那么您可以使用:

public static int MonthDifference(this DateTime lValue, DateTime rValue)
{
    return (lValue.Month - rValue.Month) + 12 * (lValue.Year - rValue.Year);
}
Run Code Online (Sandbox Code Playgroud)

请注意,这会返回相对差异,这意味着如果rValue大于lValue,则返回值将为负数.如果你想要一个绝对差异,你可以使用这个:

public static int MonthDifference(this DateTime lValue, DateTime rValue)
{
    return Math.Abs((lValue.Month - rValue.Month) + 12 * (lValue.Year - rValue.Year));
}
Run Code Online (Sandbox Code Playgroud)

  • 这似乎与Sql Server DateDiff(month,...)函数使用的逻辑相同.它还具有极其简洁,易于解释和理解的优点.我会解释如下...日历中你需要转多少页才能从一个日期转到另一个日期? (2认同)

Jon*_*eet 51

(我意识到这是一个老问题,但......)

在纯.NET中这是相对痛苦的.我推荐自己的Noda Time库,它专门用于这样的事情:

LocalDate start = new LocalDate(2009, 10, 6);
LocalDate end = new LocalDate(2009, 12, 25);
Period period = Period.Between(start, end);
int months = period.Months;
Run Code Online (Sandbox Code Playgroud)

(还有其他选择,例如,如果您只需要数月甚至数年,您可以使用Period period = Period.Between(start, end, PeriodUnits.Months);)


Rub*_*ias 28

也许你不想知道月份; 这段代码怎么样?


public static class DateTimeExtensions
{
    public static int TotalMonths(this DateTime start, DateTime end)
    {
        return (start.Year * 12 + start.Month) - (end.Year * 12 + end.Month);
    }
}

//  Console.WriteLine(
//     DateTime.Now.TotalMonths(
//         DateTime.Now.AddMonths(-1))); // prints "1"


  • 我不明白 * 100。应该是 * 12 吗? (2认同)

Hen*_*man 9

您必须先定义TotalMonths的含义.
一个简单的定义是将月份定为30.4天(365.25/12).

除此之外,任何包含分数的定义似乎都是无用的,更常见的整数值(日期之间的整月)也取决于非标准业务规则.


JDu*_*ley 8

你需要在日期之前自己解决这个问题.如何处理最后的存根天数将取决于您想要使用它的目的.

一种方法是计算月份,然后在结束时校正数天.就像是:

   DateTime start = new DateTime(2003, 12, 25);
   DateTime end = new DateTime(2009, 10, 6);
   int compMonth = (end.Month + end.Year * 12) - (start.Month + start.Year * 12);
   double daysInEndMonth = (end - end.AddMonths(1)).Days;
   double months = compMonth + (start.Day - end.Day) / daysInEndMonth;
Run Code Online (Sandbox Code Playgroud)


Mar*_*ker 8

我已经编写了一个非常简单的扩展方法DateTimeDateTimeOffset执行此操作.我希望它的工作方式与工作中的TotalMonths属性完全相同TimeSpan:即返回两个日期之间完整月份的计数,忽略任何部分月份.因为它基于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;
}
Run Code Online (Sandbox Code Playgroud)

它通过了所有这些单元测试用例:

// Simple comparison
Assert.AreEqual(1, new DateTime(2014, 1, 1).GetTotalMonthsFrom(new DateTime(2014, 2, 1)));
// Just under 1 month's diff
Assert.AreEqual(0, new DateTime(2014, 1, 1).GetTotalMonthsFrom(new DateTime(2014, 1, 31)));
// Just over 1 month's diff
Assert.AreEqual(1, new DateTime(2014, 1, 1).GetTotalMonthsFrom(new DateTime(2014, 2, 2)));
// 31 Jan to 28 Feb
Assert.AreEqual(1, new DateTime(2014, 1, 31).GetTotalMonthsFrom(new DateTime(2014, 2, 28)));
// Leap year 29 Feb to 29 Mar
Assert.AreEqual(1, new DateTime(2012, 2, 29).GetTotalMonthsFrom(new DateTime(2012, 3, 29)));
// Whole year minus a day
Assert.AreEqual(11, new DateTime(2012, 1, 1).GetTotalMonthsFrom(new DateTime(2012, 12, 31)));
// Whole year
Assert.AreEqual(12, new DateTime(2012, 1, 1).GetTotalMonthsFrom(new DateTime(2013, 1, 1)));
// 29 Feb (leap) to 28 Feb (non-leap)
Assert.AreEqual(12, new DateTime(2012, 2, 29).GetTotalMonthsFrom(new DateTime(2013, 2, 28)));
// 100 years
Assert.AreEqual(1200, new DateTime(2000, 1, 1).GetTotalMonthsFrom(new DateTime(2100, 1, 1)));
// Same date
Assert.AreEqual(0, new DateTime(2014, 8, 5).GetTotalMonthsFrom(new DateTime(2014, 8, 5)));
// Past date
Assert.AreEqual(6, new DateTime(2012, 1, 1).GetTotalMonthsFrom(new DateTime(2011, 6, 10)));
Run Code Online (Sandbox Code Playgroud)

  • 质朴但最好的解决方案.复制并粘贴.谢谢 (3认同)