如何在c#中计算两个日期之间的月差?

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

TimeSpan ts = date1 - date2;

但这里的数据是以天为单位的。我不想把这个数字除以30,因为不是每个月都是30天,而且两个操作数的值相差很大,所以我担心除以30可能会得到错误的值。

有什么建议吗?


当前回答

能够计算以月为单位的两个日期之间的差异是一件非常符合逻辑的事情,并且在许多业务应用程序中都需要它。这里的几个程序员提供了一些评论,比如“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日到8月23日住在短租公寓里。你在那里呆了几个月?答案很简单——6个月

你有一个银行账户,每月月底计算并支付利息。您在6月10日存入,同年10月29日取出。你有多少个月的利息?非常简单的答案——4个月(额外的日子也没关系)

在业务应用中,大多数情况下,当您需要计算月份时,是因为您需要根据人类计算时间的方式了解“完整的”月份;而不是基于一些抽象/无关的想法。

其他回答

你可以使用野田时间https://nodatime.org/

LocalDate start = new LocalDate(2010, 1, 5);
LocalDate end = new LocalDate(2012, 6, 1);
Period period = Period.Between(start, end, PeriodUnits.Months);
Console.WriteLine(period.Months);

能够计算以月为单位的两个日期之间的差异是一件非常符合逻辑的事情,并且在许多业务应用程序中都需要它。这里的几个程序员提供了一些评论,比如“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日到8月23日住在短租公寓里。你在那里呆了几个月?答案很简单——6个月

你有一个银行账户,每月月底计算并支付利息。您在6月10日存入,同年10月29日取出。你有多少个月的利息?非常简单的答案——4个月(额外的日子也没关系)

在业务应用中,大多数情况下,当您需要计算月份时,是因为您需要根据人类计算时间的方式了解“完整的”月份;而不是基于一些抽象/无关的想法。

我写了一个函数来完成这个,因为其他的方法都不适合我。

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());
}

这是对Kirk Woll的回答的回应。我还没有足够的声望点来回复评论……

我喜欢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);
}

Usage2类似:

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);
}

如果您想要完整月份的确切数目,总是正的(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 },
};