两个日期之间月份的差异


334

如何计算C#中两个日期之间的月份差异?

DateDiff()C#中是否有等效于VB的方法。我需要找出相隔数年的两个日期之间的月份差异。文档说我可以这样使用TimeSpan

TimeSpan ts = date1 - date2;

但这给了我几天的数据。我不想将这个数字除以30,因为不是每个月都有30天,而且由于两个操作数的值彼此相距甚远,因此我怕除以30可能会给我一个错误的值。

有什么建议么?


27
定义“月份差异”,“ 2010年5月1日”和“ 2010年6月16日”之间的月份有什么区别?1.5、1或其他?
Cheng Chen

7
或者,要进一步强调这一点,2010年12月31日至2011年1月1日之间的月份有什么区别?根据白天的不同,可能相差仅1秒;您认为这相差一个月吗?
stakx-不再贡献2011年

下面是情况下,简单的短代码,你仍然无法得到答案,看到这个POST stackoverflow.com/questions/8820603/...
wirol

11
丹尼:1个月零15天。stakx:0个月零1天。关键是要获取月份部分。这对我来说似乎很明显,是一个很好的问题。
Kirk Woll '02

Answers:


462

假设月份中的一天是不相关的(即2011.1.1和2010.12.31之间的差异为1),则date1> date2给出正值,而date2> date1给出负值。

((date1.Year - date2.Year) * 12) + date1.Month - date2.Month

或者,假设您希望在两个日期之间获得大约“平均月份”数,则以下内容应该适用于所有非常巨大的日期差异。

date1.Subtract(date2).Days / (365.25 / 12)

注意,如果要使用后一种解决方案,则单元测试应说明应用程序设计用于的最大日期范围,并相应地验证计算结果。


更新(感谢Gary

如果使用“平均月份”方法,则“每年平均天数”所使用的精确度稍高一些,为365.2425


3
@Kurru-365/12只是一个月的平均长度(以天为单位)的近似度量。这是不准确的措施。对于较小的日期范围,可以容忍这种误差,但是对于很大的日期范围,这种误差可能会变得很严重。
亚当·拉尔夫

21
我认为有必要考虑Day组件。这样的事情 (date1.Year - date2.Year) * 12 + date1.Month - date2.Month + (date1.Day >= date2.Day ? 0 : -1)
DrunkCoder 2012年

2
@DrunkCoder它取决于给定系统的要求。在某些情况下,您的解决方案可能确实是最佳选择。例如,重要的是要考虑两个日期跨越一个月的31天,一个月的30天,2月的28天或2月的29天时发生的情况。如果公式结果满足系统要求,那么显然是正确的选择。如果不是,则需要其他的东西。
亚当·拉尔夫

6
除了亚当所说的,我花了很多年为Acturaries编写代码。有些计算方法是除以天数,然后四舍五入为30以得出月度数字。有时,计算假定每个日期从月份的第一天开始的月份,就相应地计算整个月份。计算日期时没有最佳方法。除非是要为其编写代码的客户,否则可以将其推后链,并可能由您的客户会计师进行澄清。
Binary Worrier 2013年

1
如果使用的是公历,则365.2425的天数会稍微准确一些。但是,通过DateTime.MaxValue(10000年1月1日),仅相差59天。另外,根据您的观点,一年的定义可能会有很大不同。en.wikipedia.org/wiki/Year
加里

206

这是返回a的综合解决方案DateTimeSpan,类似于aTimeSpan,只是它除了包含时间成分外还包括所有日期成分。

用法:

void Main()
{
    DateTime compareTo = DateTime.Parse("8/13/2010 8:33:21 AM");
    DateTime now = DateTime.Parse("2/9/2012 10:10:11 AM");
    var dateSpan = DateTimeSpan.CompareDates(compareTo, now);
    Console.WriteLine("Years: " + dateSpan.Years);
    Console.WriteLine("Months: " + dateSpan.Months);
    Console.WriteLine("Days: " + dateSpan.Days);
    Console.WriteLine("Hours: " + dateSpan.Hours);
    Console.WriteLine("Minutes: " + dateSpan.Minutes);
    Console.WriteLine("Seconds: " + dateSpan.Seconds);
    Console.WriteLine("Milliseconds: " + dateSpan.Milliseconds);
}

输出:

年:1
个月:5
天:27
小时:1
分钟:36
秒:50
毫秒:0

为了方便起见,我已将逻辑融入DateTimeSpan结构中,但是您可以CompareDates在适合的地方移动该方法。还要注意,哪个日期在另一个日期之前并不重要。

public struct DateTimeSpan
{
    public int Years { get; }
    public int Months { get; }
    public int Days { get; }
    public int Hours { get; }
    public int Minutes { get; }
    public int Seconds { get; }
    public int Milliseconds { get; }

    public DateTimeSpan(int years, int months, int days, int hours, int minutes, int seconds, int milliseconds)
    {
        Years = years;
        Months = months;
        Days = days;
        Hours = hours;
        Minutes = minutes;
        Seconds = seconds;
        Milliseconds = milliseconds;
    }

    enum Phase { Years, Months, Days, Done }

    public static DateTimeSpan CompareDates(DateTime date1, DateTime date2)
    {
        if (date2 < date1)
        {
            var sub = date1;
            date1 = date2;
            date2 = sub;
        }

        DateTime current = date1;
        int years = 0;
        int months = 0;
        int days = 0;

        Phase phase = Phase.Years;
        DateTimeSpan span = new DateTimeSpan();
        int officialDay = current.Day;

        while (phase != Phase.Done)
        {
            switch (phase)
            {
                case Phase.Years:
                    if (current.AddYears(years + 1) > date2)
                    {
                        phase = Phase.Months;
                        current = current.AddYears(years);
                    }
                    else
                    {
                        years++;
                    }
                    break;
                case Phase.Months:
                    if (current.AddMonths(months + 1) > date2)
                    {
                        phase = Phase.Days;
                        current = current.AddMonths(months);
                        if (current.Day < officialDay && officialDay <= DateTime.DaysInMonth(current.Year, current.Month))
                            current = current.AddDays(officialDay - current.Day);
                    }
                    else
                    {
                        months++;
                    }
                    break;
                case Phase.Days:
                    if (current.AddDays(days + 1) > date2)
                    {
                        current = current.AddDays(days);
                        var timespan = date2 - current;
                        span = new DateTimeSpan(years, months, days, timespan.Hours, timespan.Minutes, timespan.Seconds, timespan.Milliseconds);
                        phase = Phase.Done;
                    }
                    else
                    {
                        days++;
                    }
                    break;
            }
        }

        return span;
    }
}

2
@KirkWoll谢谢。但是,为什么是DateTimeSpan返回34天这个日期时间差实际上它是35 timeanddate.com/date/...
Deeptechtons

@Deeptechtons,不错的收获。您引起了我的注意的两个问题,这两个问题都与开始日期有关31,而日期“通过”了几天却少了几天。我已经颠倒了逻辑(因此从早到晚,反之亦然),现在在不修改当前日期的情况下累积了几个月(因此在几天之内就减少了几天的间隔)仍然不完全确定理想的结果进行比较时,应10/31/201211/30/2012。现在的结果是1一个月。
Kirk Woll 2012年

@KirkWoll,感谢您的更新,也许我在进行一些测试后还获得了一些确认信息:)
Deeptechtons 2012年

1
我为类似的问题写了一个答案stackoverflow.com/a/17537472/1737957,它测试了建议的答案(并发现其中大部分无效)。该答案是少数可行的方法之一(根据我的测试套件)。链接到我的答案的github。
jwg

@KirkWoll-对于起始日期的日期值大于截止日期的月或源日期是a日的极端情况,此答案似乎不起作用。尝试2020-02-292021-06-29-它返回“1Y4米1D”,但该值应该是“1Y4米0D”,对不对?
谜性,2015年

37

你可以做

if ( date1.AddMonths(x) > date2 )

这是如此简单,对我来说非常完美。我很惊讶地看到它在计算从1个月末到下个月月末的日期较少的日期时可以正常工作。例如。.1-31-2018+ 1个月= 2月28日
lucky.expert

这是更好的解决方案之一。
barnacle.m

真正简单有效的解决方案!建议的最佳答案。
Cedric Arnould

2
如果date1 = 2018-10-28和date2 = 2018-12-21怎么办?答案将为2。正确答案应为3。由于日期范围为3个月。如果我们只计算几个月而不考虑几天。所以这个答案是不正确的。
Tommix

更合乎逻辑的是:if ( date1.AddMonths(x).Month == date2.Month )那么您只需使用x +1作为月份计数
Tommix

34

如果您想要确切的完整月份数,请始终为正(2000-01-15,2000-02-14返回0),考虑到完整月份是指您在下个月的同一天(类似于年龄计算)

public static int GetMonthsBetween(DateTime from, DateTime to)
{
    if (from > to) return GetMonthsBetween(to, from);

    var monthDiff = Math.Abs((to.Year * 12 + (to.Month - 1)) - (from.Year * 12 + (from.Month - 1)));

    if (from.AddMonths(monthDiff) > to || to.Day < from.Day)
    {
        return monthDiff - 1;
    }
    else
    {
        return monthDiff;
    }
}

编辑原因:在某些情况下,旧代码不正确:

new { From = new DateTime(1900, 8, 31), To = new DateTime(1901, 8, 30), Result = 11 },

Test cases I used to test the function:

var tests = new[]
{
    new { From = new DateTime(1900, 1, 1), To = new DateTime(1900, 1, 1), Result = 0 },
    new { From = new DateTime(1900, 1, 1), To = new DateTime(1900, 1, 2), Result = 0 },
    new { From = new DateTime(1900, 1, 2), To = new DateTime(1900, 1, 1), Result = 0 },
    new { From = new DateTime(1900, 1, 1), To = new DateTime(1900, 2, 1), Result = 1 },
    new { From = new DateTime(1900, 2, 1), To = new DateTime(1900, 1, 1), Result = 1 },
    new { From = new DateTime(1900, 1, 31), To = new DateTime(1900, 2, 1), Result = 0 },
    new { From = new DateTime(1900, 8, 31), To = new DateTime(1900, 9, 30), Result = 0 },
    new { From = new DateTime(1900, 8, 31), To = new DateTime(1900, 10, 1), Result = 1 },
    new { From = new DateTime(1900, 1, 1), To = new DateTime(1901, 1, 1), Result = 12 },
    new { From = new DateTime(1900, 1, 1), To = new DateTime(1911, 1, 1), Result = 132 },
    new { From = new DateTime(1900, 8, 31), To = new DateTime(1901, 8, 30), Result = 11 },
};

为了避免引起其他人的困惑,我认为这种解决方案是不正确的。使用测试用例: new { From = new DateTime(2015, 12, 31), To = new DateTime(2015, 6, 30), Result = 6 } 测试将失败,因为结果是5
克里斯蒂安拔地

添加了我在这里提出的修复的快速要点
Cristian Badila

我不确定是否得到它,我的函数应该返回6:dotnetfiddle.net/MRZNnC
Guillaume86

我手动将测试用例复制到这里,这是一个错误。失败的规范应为:new { From = new DateTime(2015, 12, 31), To = new DateTime(2016, 06, 30), Result = 6 }。“错误”在于to.Day < from.Day代码中,它没有考虑到月份可以以不同的“月份”结尾。在这种情况下,从2015年12月31日,6月30日前2016年,6个完整月份即将过去了(自6月份以来30天),但你的代码将返回5
克里斯蒂安拔地

3
我认为这是预期的行为,或者至少是我确实预期的行为。我确定一个完整的月份是您到达当天(或本例中的下个月)的月份。
纪尧姆86

22

我通过MSDN在VB.NET中检查了此方法的用法,看来它有很多用法。C#中没有这样的内置方法。(即使这不是一个好主意),您也可以在C#中调用VB。

  1. 添加Microsoft.VisualBasic.dll到您的项目中作为参考
  2. 使用 Microsoft.VisualBasic.DateAndTime.DateDiff 在你的代码

7
您为什么认为这不是一个好主意?凭直觉,我猜该库是运行时的“另一个.NET库”。请注意,我在这里扮演魔鬼的拥护者,我也会这样做,因为它只是“感觉错了”(有点作弊),但是我想知道是否有任何令人信服的技术理由不这样做。
亚当·拉尔夫

3
@AdamRalph:完全没有理由不这样做。这些库是在100%托管代码中实现的,因此所有其他库都一样。唯一可以想到的区别是Microsoft.VisualBasic.dll必须加载模块,但是这样做所需的时间可以忽略不计。没有理由仅仅因为选择使用C#编写程序就欺骗了自己经过全面测试和有用的功能。(这也适用于类似的事情My.Application.SplashScreen。)
Cody Gray

3
如果您知道它是用C#编写的,您会改变主意吗?它是。按照相同的逻辑,使用System.Data和PresentationFramework也是作弊的,其中很大一部分是用C ++ / CLI编写的。
汉斯·帕桑

3
@AdamRalph:想起那个“奇怪的行李”的具体例子吗?还是您纯粹是假说的?是的,这可能会让您一些C#伙伴的思想陷入混乱,这些伙伴一直在编写大量的代码来完成您可以在正确的using语句中完成的工作,但是我怀疑会不会造成严重的损害。
科迪·格雷

1
@库迪·格雷:同意,这个例子很简单。这是我渴望避免的一种异常代码(从C#POV)调用而引入的额外代码“噪声”。在一个组织良好的团队中,无论如何这些事情都会在代码审查中处理,并且可以轻松避免。顺便说一句-我不是要攻击VB6 / VB.NET。我将这种方法描述为“奇怪”的原因仅在于,由于.NET POV 具有属性,因此没有理由DateAndTime.Year()存在。它的存在只是使VB.NET看起来更像VB6。作为一名前VB6程序员,我对此表示赞赏;-)DateTimeYear
Adam Ralph

10

要获得月份(包括开始和结束)的差异,而与日期无关:

DateTime start = new DateTime(2013, 1, 1);
DateTime end = new DateTime(2014, 2, 1);
var diffMonths = (end.Month + end.Year * 12) - (start.Month + start.Year * 12);

5
想象一下startend它们是相同的。然后得到1的结果。那怎么办?为什么将1加到结果中?谁在投票这个答案:-/?
paul 2014年

对于相同的日期,它将输出为1。基本上,它将计算包括开始和结束月份在内的所有月份。
希拉格2014年

3
对我来说听起来不像是两个项目之间的区别。2和2有什么区别?真的是1吗?我建议不同的是0
保罗


7

我只需要一些简单的方法来满足例如输入日期/年的工作日期,因此希望工作的年份和月份数不同。

public static YearsMonths YearMonthDiff(DateTime startDate, DateTime endDate) {
    int monthDiff = ((endDate.Year * 12) + endDate.Month) - ((startDate.Year * 12) + startDate.Month) + 1;
    int years = (int)Math.Floor((decimal) (monthDiff / 12));
    int months = monthDiff % 12;
    return new YearsMonths {
        TotalMonths = monthDiff,
            Years = years,
            Months = months
    };
}

.NET小提琴


4

您可以为.NET使用时间段库DateDiff类:

// ----------------------------------------------------------------------
public void DateDiffSample()
{
  DateTime date1 = new DateTime( 2009, 11, 8, 7, 13, 59 );
  DateTime date2 = new DateTime( 2011, 3, 20, 19, 55, 28 );
  DateDiff dateDiff = new DateDiff( date1, date2 );

  // differences
  Console.WriteLine( "DateDiff.Months: {0}", dateDiff.Months );
  // > DateDiff.Months: 16

  // elapsed
  Console.WriteLine( "DateDiff.ElapsedMonths: {0}", dateDiff.ElapsedMonths );
  // > DateDiff.ElapsedMonths: 4

  // description
  Console.WriteLine( "DateDiff.GetDescription(6): {0}", dateDiff.GetDescription( 6 ) );
  // > DateDiff.GetDescription(6): 1 Year 4 Months 12 Days 12 Hours 41 Mins 29 Secs
} // DateDiffSample

2

这是我为使我发现准确的月份有所不同而做出的贡献:

namespace System
{
     public static class DateTimeExtensions
     {
         public static Int32 DiffMonths( this DateTime start, DateTime end )
         {
             Int32 months = 0;
             DateTime tmp = start;

             while ( tmp < end )
             {
                 months++;
                 tmp = tmp.AddMonths( 1 );
             }

             return months;
        }
    }
}

用法:

Int32 months = DateTime.Now.DiffMonths( DateTime.Now.AddYears( 5 ) );

您可以创建另一个名为DiffYears的方法,并在while循环中应用与上述和AddYears完全相同的逻辑,而不是AddMonths。


2

这可以满足我的需求。在我的情况下,月的一天并不重要,因为它总是恰好是该月的最后一天。

public static int MonthDiff(DateTime d1, DateTime d2){
    int retVal = 0;

    if (d1.Month<d2.Month)
    {
        retVal = (d1.Month + 12) - d2.Month;
        retVal += ((d1.Year - 1) - d2.Year)*12;
    }
    else
    {
        retVal = d1.Month - d2.Month;
        retVal += (d1.Year - d2.Year)*12;
    }
    //// Calculate the number of years represented and multiply by 12
    //// Substract the month number from the total
    //// Substract the difference of the second month and 12 from the total
    //retVal = (d1.Year - d2.Year) * 12;
    //retVal = retVal - d1.Month;
    //retVal = retVal - (12 - d2.Month);

    return retVal;
}

2

最精确的方法是按月返回分数差异:

private double ReturnDiffereceBetweenTwoDatesInMonths(DateTime startDateTime, DateTime endDateTime)
{
    double result = 0;
    double days = 0;
    DateTime currentDateTime = startDateTime;
    while (endDateTime > currentDateTime.AddMonths(1))
    {
        result ++;

        currentDateTime = currentDateTime.AddMonths(1);
    }

    if (endDateTime > currentDateTime)
    {
        days = endDateTime.Subtract(currentDateTime).TotalDays;

    }
    return result + days/endDateTime.GetMonthDays;
}

2

这是一个至少对我有用的简单解决方案。它可能不是最快的,因为它在循环中使用了很酷的DateTime的AddMonth功能:

public static int GetMonthsDiff(DateTime start, DateTime end)
{
    if (start > end)
        return GetMonthsDiff(end, start);

    int months = 0;
    do
    {
        start = start.AddMonths(1);
        if (start > end)
            return months;

        months++;
    }
    while (true);
}

1
Public Class ClassDateOperation
    Private prop_DifferenceInDay As Integer
    Private prop_DifferenceInMonth As Integer
    Private prop_DifferenceInYear As Integer


    Public Function DayMonthYearFromTwoDate(ByVal DateStart As Date, ByVal DateEnd As Date) As ClassDateOperation
        Dim differenceInDay As Integer
        Dim differenceInMonth As Integer
        Dim differenceInYear As Integer
        Dim myDate As Date

        DateEnd = DateEnd.AddDays(1)

        differenceInYear = DateEnd.Year - DateStart.Year

        If DateStart.Month <= DateEnd.Month Then
            differenceInMonth = DateEnd.Month - DateStart.Month
        Else
            differenceInYear -= 1
            differenceInMonth = (12 - DateStart.Month) + DateEnd.Month
        End If


        If DateStart.Day <= DateEnd.Day Then
            differenceInDay = DateEnd.Day - DateStart.Day
        Else

            myDate = CDate("01/" & DateStart.AddMonths(1).Month & "/" & DateStart.Year).AddDays(-1)
            If differenceInMonth <> 0 Then
                differenceInMonth -= 1
            Else
                differenceInMonth = 11
                differenceInYear -= 1
            End If

            differenceInDay = myDate.Day - DateStart.Day + DateEnd.Day

        End If

        prop_DifferenceInDay = differenceInDay
        prop_DifferenceInMonth = differenceInMonth
        prop_DifferenceInYear = differenceInYear

        Return Me
    End Function

    Public ReadOnly Property DifferenceInDay() As Integer
        Get
            Return prop_DifferenceInDay
        End Get
    End Property

    Public ReadOnly Property DifferenceInMonth As Integer
        Get
            Return prop_DifferenceInMonth
        End Get
    End Property

    Public ReadOnly Property DifferenceInYear As Integer
        Get
            Return prop_DifferenceInYear
        End Get
    End Property

End Class

1

这来自我自己的库,将返回两个日期之间的月份差。

public static int MonthDiff(DateTime d1, DateTime d2)
{
    int retVal = 0;

    // Calculate the number of years represented and multiply by 12
    // Substract the month number from the total
    // Substract the difference of the second month and 12 from the total
    retVal = (d1.Year - d2.Year) * 12;
    retVal = retVal - d1.Month;
    retVal = retVal - (12 - d2.Month);

    return retVal;
}

1
这样行吗?我一直在纸上获得11,Jan-31-2014并且Dec-31-2013
Dave Cousineau 2014年

1

您可以拥有类似这样的功能。

例如,从2012/12/27到2012/12/29变为3天。同样,从2012/12/15到2013/01/15变为2个月,因为直到2013/01/14都是1个月。从15日开始第二个月。

如果您不想在计算中同时包括两天,则可以在第二个if条件中删除“ =”。也就是说,从2012/12/15到2013/01/15是1个月。

public int GetMonths(DateTime startDate, DateTime endDate)
{
    if (startDate > endDate)
    {
        throw new Exception("Start Date is greater than the End Date");
    }

    int months = ((endDate.Year * 12) + endDate.Month) - ((startDate.Year * 12) + startDate.Month);

    if (endDate.Day >= startDate.Day)
    {
        months++;
    }

    return months;
}

1

您可以使用以下扩展名: 代码

public static class Ext
{
    #region Public Methods

    public static int GetAge(this DateTime @this)
    {
        var today = DateTime.Today;
        return ((((today.Year - @this.Year) * 100) + (today.Month - @this.Month)) * 100 + today.Day - @this.Day) / 10000;
    }

    public static int DiffMonths(this DateTime @from, DateTime @to)
    {
        return (((((@to.Year - @from.Year) * 12) + (@to.Month - @from.Month)) * 100 + @to.Day - @from.Day) / 100);
    }

    public static int DiffYears(this DateTime @from, DateTime @to)
    {
        return ((((@to.Year - @from.Year) * 100) + (@to.Month - @from.Month)) * 100 + @to.Day - @from.Day) / 10000;
    }

    #endregion Public Methods
}

执行!

int Age;
int years;
int Months;
//Replace your own date
var d1 = new DateTime(2000, 10, 22);
var d2 = new DateTime(2003, 10, 20);
//Age
Age = d1.GetAge();
Age = d2.GetAge();
//positive
years = d1.DiffYears(d2);
Months = d1.DiffMonths(d2);
//negative
years = d2.DiffYears(d1);
Months = d2.DiffMonths(d1);
//Or
Months = Ext.DiffMonths(d1, d2);
years = Ext.DiffYears(d1, d2); 

1

这是使用VB.Net DateDiff仅用于年,月,日的更简洁的解决方案。您也可以在C#中加载DateDiff库。

date1必须为<= date2

VB.NET

Dim date1 = Now.AddDays(-2000)
Dim date2 = Now
Dim diffYears = DateDiff(DateInterval.Year, date1, date2) - If(date1.DayOfYear > date2.DayOfYear, 1, 0)
Dim diffMonths = DateDiff(DateInterval.Month, date1, date2) - diffYears * 12 - If(date1.Day > date2.Day, 1, 0)
Dim diffDays = If(date2.Day >= date1.Day, date2.Day - date1.Day, date2.Day + (Date.DaysInMonth(date1.Year, date1.Month) - date1.Day))

C#

DateTime date1 = Now.AddDays(-2000);
DateTime date2 = Now;
int diffYears = DateDiff(DateInterval.Year, date1, date2) - date1.DayOfYear > date2.DayOfYear ? 1 : 0;
int diffMonths = DateDiff(DateInterval.Month, date1, date2) - diffYears * 12 - date1.Day > date2.Day ? 1 : 0;
int diffDays = date2.Day >= date1.Day ? date2.Day - date1.Day : date2.Day + (System.DateTime.DaysInMonth(date1.Year, date1.Month) - date1.Day);

1

这是对柯克·沃尔的回答。我的信誉点尚不足,无法回复评论...

我喜欢Kirk的解决方案,打算毫不客气地撕掉它并在我的代码中使用它,但是当我仔细研究它时,我意识到它太复杂了。不必要的切换和循环,以及无用的公共构造函数。

这是我的重写:

public class DateTimeSpan {
    private DateTime _date1;
    private DateTime _date2;
    private int _years;
    private int _months;
    private int _days;
    private int _hours;
    private int _minutes;
    private int _seconds;
    private int _milliseconds;

    public int Years { get { return _years; } }
    public int Months { get { return _months; } }
    public int Days { get { return _days; } }
    public int Hours { get { return _hours; } }
    public int Minutes { get { return _minutes; } }
    public int Seconds { get { return _seconds; } }
    public int Milliseconds { get { return _milliseconds; } }

    public DateTimeSpan(DateTime date1, DateTime date2) {
        _date1 = (date1 > date2) ? date1 : date2;
        _date2 = (date2 < date1) ? date2 : date1;

        _years = _date1.Year - _date2.Year;
        _months = (_years * 12) + _date1.Month - _date2.Month;
        TimeSpan t = (_date2 - _date1);
        _days = t.Days;
        _hours = t.Hours;
        _minutes = t.Minutes;
        _seconds = t.Seconds;
        _milliseconds = t.Milliseconds;

    }

    public static DateTimeSpan CompareDates(DateTime date1, DateTime date2) {
        return new DateTimeSpan(date1, date2);
    }
}

用法1,几乎相同:

void Main()
{
    DateTime compareTo = DateTime.Parse("8/13/2010 8:33:21 AM");
    DateTime now = DateTime.Parse("2/9/2012 10:10:11 AM");
    var dateSpan = new DateTimeSpan(compareTo, now);
    Console.WriteLine("Years: " + dateSpan.Years);
    Console.WriteLine("Months: " + dateSpan.Months);
    Console.WriteLine("Days: " + dateSpan.Days);
    Console.WriteLine("Hours: " + dateSpan.Hours);
    Console.WriteLine("Minutes: " + dateSpan.Minutes);
    Console.WriteLine("Seconds: " + dateSpan.Seconds);
    Console.WriteLine("Milliseconds: " + dateSpan.Milliseconds);
}

用法2,类似:

void Main()
{
    DateTime compareTo = DateTime.Parse("8/13/2010 8:33:21 AM");
    DateTime now = DateTime.Parse("2/9/2012 10:10:11 AM");
    Console.WriteLine("Years: " + DateTimeSpan.CompareDates(compareTo, now).Years);
    Console.WriteLine("Months: " + DateTimeSpan.CompareDates(compareTo, now).Months);
    Console.WriteLine("Days: " + DateTimeSpan.CompareDates(compareTo, now).Days);
    Console.WriteLine("Hours: " + DateTimeSpan.CompareDates(compareTo, now).Hours);
    Console.WriteLine("Minutes: " + DateTimeSpan.CompareDates(compareTo, now).Minutes);
    Console.WriteLine("Seconds: " + DateTimeSpan.CompareDates(compareTo, now).Seconds);
    Console.WriteLine("Milliseconds: " + DateTimeSpan.CompareDates(compareTo, now).Milliseconds);
}

1

就我而言,需要计算从开始日期到下个月的这一天的前一天或从月初到月底的完整月份。


例如:从2018年1月1日到2018年1月31日是一个完整的月
Ex2:从5/1/2018年到4/2/2018是一个完整的月

所以基于这是我的解决方案:

public static DateTime GetMonthEnd(DateTime StartDate, int MonthsCount = 1)
{
    return StartDate.AddMonths(MonthsCount).AddDays(-1);
}
public static Tuple<int, int> CalcPeriod(DateTime StartDate, DateTime EndDate)
{
    int MonthsCount = 0;
    Tuple<int, int> Period;
    while (true)
    {
        if (GetMonthEnd(StartDate) > EndDate)
            break;
        else
        {
            MonthsCount += 1;
            StartDate = StartDate.AddMonths(1);
        }
    }
    int RemainingDays = (EndDate - StartDate).Days + 1;
    Period = new Tuple<int, int>(MonthsCount, RemainingDays);
    return Period;
}

用法:

Tuple<int, int> Period = CalcPeriod(FromDate, ToDate);

注意:在我的情况下,需要计算完整月份后的剩余天数,因此,如果不是您的情况,则可以忽略天数结果,甚至可以将方法返回值从元组更改为整数。


1
public static int PayableMonthsInDuration(DateTime StartDate, DateTime EndDate)
{
    int sy = StartDate.Year; int sm = StartDate.Month; int count = 0;
    do
    {
        count++;if ((sy == EndDate.Year) && (sm >= EndDate.Month)) { break; }
        sm++;if (sm == 13) { sm = 1; sy++; }
    } while ((EndDate.Year >= sy) || (EndDate.Month >= sm));
    return (count);
}

此解决方案用于租赁/订阅计算,其中差异并不意味着相减,而是在于这两个日期内的跨度。


1

有3种情况:同年,上一年和其他年份。

如果一个月中的某天没关系...

public int GetTotalNumberOfMonths(DateTime start, DateTime end)
{
    // work with dates in the right order
    if (start > end)
    {
        var swapper = start;
        start = end;
        end = swapper;
    }

    switch (end.Year - start.Year)
    {
        case 0: // Same year
            return end.Month - start.Month;

        case 1: // last year
            return (12 - start.Month) + end.Month;

        default:
            return 12 * (3 - (end.Year - start.Year)) + (12 - start.Month) + end.Month;
    }
}

1

我编写了一个函数来完成此任务,因为其他方式对我不起作用。

public string getEndDate (DateTime startDate,decimal monthCount)
{
    int y = startDate.Year;
    int m = startDate.Month;

    for (decimal  i = monthCount; i > 1; i--)
    {
        m++;
        if (m == 12)
        { y++;
            m = 1;
        }
    }
    return string.Format("{0}-{1}-{2}", y.ToString(), m.ToString(), startDate.Day.ToString());
}

请用英语回答(对比任何发明的语言...)
kleopatra 2013年

为什么不只是startDate.AddMonths(monthCount).ToShortDateString()?无论如何,这并不能回答最初提出的问题!
TabbyCool 2014年

哦,对不起@TabbyCool,此代码在我的程序中效果很好!程序员规则说:先编写代码,然后进行优化!tanx对您的评论:)
reza akhlaghi 2014年

1

我对2个日期之间的月份总差额的理解有一个整数和一个小数部分(日期很重要)。

不可或缺的部分是完整的月份差异。

对我而言,小数部分是开始月份和结束月份之间的天百分比(到一个月的整天)之差。

public static class DateTimeExtensions
{
    public static double TotalMonthsDifference(this DateTime from, DateTime to)
    {
        //Compute full months difference between dates
        var fullMonthsDiff = (to.Year - from.Year)*12 + to.Month - from.Month;

        //Compute difference between the % of day to full days of each month
        var fractionMonthsDiff = ((double)(to.Day-1) / (DateTime.DaysInMonth(to.Year, to.Month)-1)) -
            ((double)(from.Day-1)/ (DateTime.DaysInMonth(from.Year, from.Month)-1));

        return fullMonthsDiff + fractionMonthsDiff;
    }
}

使用此扩展,结果如下:

2/29/2000 TotalMonthsDifference 2/28/2001 => 12
2/28/2000 TotalMonthsDifference 2/28/2001 => 12.035714285714286
01/01/2000 TotalMonthsDifference 01/16/2000 => 0.5
01/31/2000 TotalMonthsDifference 01/01/2000 => -1.0
01/31/2000 TotalMonthsDifference 02/29/2000 => 1.0
01/31/2000 TotalMonthsDifference 02/28/2000 => 0.9642857142857143
01/31/2001 TotalMonthsDifference 02/28/2001 => 1.0

1

关于这一点,没有很多明确的答案,因为您一直在假设事情。

该解决方案假设您要保存一个月中的某天进行比较,从而计算出两个月之间的日期(这意味着在计算中考虑了该月中的某天)

例如,如果您的日期为2012年1月30日,那么2012年2月29日将不是一个月,而是2013年3月1日。

它已经过相当彻底的测试,可能会在我们使用时稍后进行清理,但是这里:

private static int TotalMonthDifference(DateTime dtThis, DateTime dtOther)
{
    int intReturn = 0;
    bool sameMonth = false;

    if (dtOther.Date < dtThis.Date) //used for an error catch in program, returns -1
        intReturn--;

    int dayOfMonth = dtThis.Day; //captures the month of day for when it adds a month and doesn't have that many days
    int daysinMonth = 0; //used to caputre how many days are in the month

    while (dtOther.Date > dtThis.Date) //while Other date is still under the other
    {
        dtThis = dtThis.AddMonths(1); //as we loop, we just keep adding a month for testing
        daysinMonth = DateTime.DaysInMonth(dtThis.Year, dtThis.Month); //grabs the days in the current tested month

        if (dtThis.Day != dayOfMonth) //Example 30 Jan 2013 will go to 28 Feb when a month is added, so when it goes to march it will be 28th and not 30th
        {
            if (daysinMonth < dayOfMonth) // uses day in month max if can't set back to day of month
                dtThis.AddDays(daysinMonth - dtThis.Day);
            else
                dtThis.AddDays(dayOfMonth - dtThis.Day);
        }
        if (((dtOther.Year == dtThis.Year) && (dtOther.Month == dtThis.Month))) //If the loop puts it in the same month and year
        {
            if (dtOther.Day >= dayOfMonth) //check to see if it is the same day or later to add one to month
                intReturn++;
            sameMonth = true; //sets this to cancel out of the normal counting of month
        }
        if ((!sameMonth)&&(dtOther.Date > dtThis.Date))//so as long as it didn't reach the same month (or if i started in the same month, one month ahead, add a month)
            intReturn++;
    }
    return intReturn; //return month
}

1

基于上面完成的出色的DateTimeSpan工作,我对代码进行了一些标准化。这似乎工作得很好:

public class DateTimeSpan
{
  private DateTimeSpan() { }

  private DateTimeSpan(int years, int months, int days, int hours, int minutes, int seconds, int milliseconds)
  {
    Years = years;
    Months = months;
    Days = days;
    Hours = hours;
    Minutes = minutes;
    Seconds = seconds;
    Milliseconds = milliseconds;
  }

  public int Years { get; private set; } = 0;
  public int Months { get; private set; } = 0;
  public int Days { get; private set; } = 0;
  public int Hours { get; private set; } = 0;
  public int Minutes { get; private set; } = 0;
  public int Seconds { get; private set; } = 0;
  public int Milliseconds { get; private set; } = 0;

  public static DateTimeSpan CompareDates(DateTime StartDate, DateTime EndDate)
  {
    if (StartDate.Equals(EndDate)) return new DateTimeSpan();
    DateTimeSpan R = new DateTimeSpan();
    bool Later;
    if (Later = StartDate > EndDate)
    {
      DateTime D = StartDate;
      StartDate = EndDate;
      EndDate = D;
    }

    // Calculate Date Stuff
    for (DateTime D = StartDate.AddYears(1); D < EndDate; D = D.AddYears(1), R.Years++) ;
    if (R.Years > 0) StartDate = StartDate.AddYears(R.Years);
    for (DateTime D = StartDate.AddMonths(1); D < EndDate; D = D.AddMonths(1), R.Months++) ;
    if (R.Months > 0) StartDate = StartDate.AddMonths(R.Months);
    for (DateTime D = StartDate.AddDays(1); D < EndDate; D = D.AddDays(1), R.Days++) ;
    if (R.Days > 0) StartDate = StartDate.AddDays(R.Days);

    // Calculate Time Stuff
    TimeSpan T1 = EndDate - StartDate;
    R.Hours = T1.Hours;
    R.Minutes = T1.Minutes;
    R.Seconds = T1.Seconds;
    R.Milliseconds = T1.Milliseconds;

    // Return answer. Negate values if the Start Date was later than the End Date
    if (Later)
      return new DateTimeSpan(-R.Years, -R.Months, -R.Days, -R.Hours, -R.Minutes, -R.Seconds, -R.Milliseconds);
    return R;
  }
}

当与比较CompareDates(x, y),其中x={01/02/2019 00:00:00}y={01/05/2020 00:00:00}然后Months给了我2
Bassie

1

这个简单的静态函数可以计算两个Datetime之间的月份比例,例如

  • 1.1。至31.1。= 1.0
  • 1.4。至15.4。= 0.5
  • 16.4。至30.4。= 0.5
  • 1.3。至1.4。= 1 + 1/30

该函数假定第一个日期小于第二个日期。为了处理负的时间间隔,可以通过在开始处引入符号和变量交换来轻松修改函数。

public static double GetDeltaMonths(DateTime t0, DateTime t1)
{
     DateTime t = t0;
     double months = 0;
     while(t<=t1)
     {
         int daysInMonth = DateTime.DaysInMonth(t.Year, t.Month);
         DateTime endOfMonth = new DateTime(t.Year, t.Month, daysInMonth);
         int cutDay = endOfMonth <= t1 ? daysInMonth : t1.Day;
         months += (cutDay - t.Day + 1) / (double) daysInMonth;
         t = new DateTime(t.Year, t.Month, 1).AddMonths(1);
     }
     return Math.Round(months,2);
 }

0

能够计算两个月中两个日期之间的差是一件很合理的事情,并且在许多业务应用程序中都需要。此处提供评论的几个编码人员,例如-“ 2010年5月1日”和“ 2010年6月16日之间的月份有什么区别,2010年12月31日至2011年1月1日之间的月份有什么区别?”-无法理解业务应用程序的基础知识。

这是上述2条评论的答案-2010年5月1日至2010年6月16日之间的月数为1个月,2010年12月31日至2011年1月1日之间的月数为0。如上面的编码人员所建议的那样,将它们计算为1.5个月零1秒将是非常愚蠢的。

从事信用卡,抵押贷款处理,税务处理,租金处理,每月利息计算以及其他各种业务解决方案工作的人都会同意。

问题在于,这种功能不包含在C#或VB.NET中。Datediff仅考虑年份或月份组成部分,因此实际上是没有用的。

以下是一些实际的示例,这些示例说明了您需要并可以正确计算出的月份:

您的短期租金为2月18日至23月8日。你在那里呆了几个月?答案很简单-6个月

您有一个银行帐户,每个月末都会计算并支付利息。您在6月10日存入钱,然后取出10月29日(同年)。您对多少个月感兴趣?很简单的答案-4个月(同样,多余的日子也没关系)

在业务应用程序中,大多数时候,当您需要计算月份时,是因为您需要基于人类如何计算时间来了解“完整”月份;并非基于某些抽象/无关的想法。


5
这是会计不是数学的原因之一。在会计中,结果取决于您的计算方式。在2012.11.30和2012.12.01之间,有0或1/30或1/31或1或2个月,具体取决于您的要求。日期是排他性的还是包含性的?您是否要求交叉,感动或通过的月份数?您要向上舍入,向下舍入还是精确舍入?
quetzalcoatl

3
现在,向业务人员或会计师进行解释,他们将为您带来困惑的外观。总是“对他们是如此明显,以至于他们当然是指X,Y和Z,您怎么可能有不同的看法?” 现在请几个商人,并设法使他们就该主题达成一致。会计师更有可能同意,因为在某个时候,他们将使用数学来检查他们可能偶然将同一时期两次相加的选项,等等。即使您的计算示例也是有争议的且与地区相关,或者假设它们显然是无效的额外的业务规则,例如忽略多余的日子。
quetzalcoatl

2
-1您假设所有软件都是“业务应用程序”。没有提到有关代码的目的。您还假设所有“业务应用程序”都具有相同的规则,这绝对是不正确的。
Jesse Webb 2014年

0

使用ToString(format)和Duration(long ms)扩展了Kirks结构

 public struct DateTimeSpan
{
    private readonly int years;
    private readonly int months;
    private readonly int days;
    private readonly int hours;
    private readonly int minutes;
    private readonly int seconds;
    private readonly int milliseconds;

    public DateTimeSpan(int years, int months, int days, int hours, int minutes, int seconds, int milliseconds)
    {
        this.years = years;
        this.months = months;
        this.days = days;
        this.hours = hours;
        this.minutes = minutes;
        this.seconds = seconds;
        this.milliseconds = milliseconds;
    }

    public int Years { get { return years; } }
    public int Months { get { return months; } }
    public int Days { get { return days; } }
    public int Hours { get { return hours; } }
    public int Minutes { get { return minutes; } }
    public int Seconds { get { return seconds; } }
    public int Milliseconds { get { return milliseconds; } }

    enum Phase { Years, Months, Days, Done }


    public string ToString(string format)
    {
        format = format.Replace("YYYY", Years.ToString());
        format = format.Replace("MM", Months.ToString());
        format = format.Replace("DD", Days.ToString());
        format = format.Replace("hh", Hours.ToString());
        format = format.Replace("mm", Minutes.ToString());
        format = format.Replace("ss", Seconds.ToString());
        format = format.Replace("ms", Milliseconds.ToString());
        return format;
    }


    public static DateTimeSpan Duration(long ms)
    {
        DateTime dt = new DateTime();
        return CompareDates(dt, dt.AddMilliseconds(ms));
    }


    public static DateTimeSpan CompareDates(DateTime date1, DateTime date2)
    {
        if (date2 < date1)
        {
            var sub = date1;
            date1 = date2;
            date2 = sub;
        }

        DateTime current = date1;
        int years = 0;
        int months = 0;
        int days = 0;

        Phase phase = Phase.Years;
        DateTimeSpan span = new DateTimeSpan();

        while (phase != Phase.Done)
        {
            switch (phase)
            {
                case Phase.Years:
                    if (current.AddYears(years + 1) > date2)
                    {
                        phase = Phase.Months;
                        current = current.AddYears(years);
                    }
                    else
                    {
                        years++;
                    }
                    break;
                case Phase.Months:
                    if (current.AddMonths(months + 1) > date2)
                    {
                        phase = Phase.Days;
                        current = current.AddMonths(months);
                    }
                    else
                    {
                        months++;
                    }
                    break;
                case Phase.Days:
                    if (current.AddDays(days + 1) > date2)
                    {
                        current = current.AddDays(days);
                        var timespan = date2 - current;
                        span = new DateTimeSpan(years, months, days, timespan.Hours, timespan.Minutes, timespan.Seconds, timespan.Milliseconds);
                        phase = Phase.Done;
                    }
                    else
                    {
                        days++;
                    }
                    break;
            }
        }

        return span;
    }
}

0
  var dt1 = (DateTime.Now.Year * 12) + DateTime.Now.Month;
  var dt2 = (DateTime.Now.AddMonths(-13).Year * 12) + DateTime.Now.AddMonths(-13).Month;
  Console.WriteLine(dt1);
  Console.WriteLine(dt2);
  Console.WriteLine((dt1 - dt2));
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.