将月份差异添加到新日期

时间:2017-02-16 09:35:21

标签: c# .net date datetime

对于应用程序,我需要对日期进行一些计算。我发现如何在2个日期之间获得差异,但不知道如何将这些月份添加到另一个日期。

计算月份差异的函数:

public static decimal GetMonthsInRange(DateTime start, DateTime finish)
{
    var monthsApart = 
        Math.Abs(12 * (start.Year - finish.Year) + start.Month - finish.Month) - 1;

    decimal daysInStartMonth = DateTime.DaysInMonth(start.Year, start.Month);
    decimal daysInFinishMonth = DateTime.DaysInMonth(finish.Year, finish.Month);

    var daysApartInStartMonth = (daysInStartMonth - start.Day + 1) / daysInStartMonth;
    var daysApartInFinishMonth = finish.Day / daysInFinishMonth;

    return monthsApart + daysApartInStartMonth + daysApartInFinishMonth;
}

示例:

我得到的月份差异是4.5个月,我需要将其添加到另一个日期。但DateTime.AddMonths函数只接受整数。

我该怎么做?

GRTS, Nanou

4 个答案:

答案 0 :(得分:4)

显然,如果你想将这种差异添加到某些DateTime,你不应该计算月份的差异,因为这种差异可能更令人困惑,比如说4.33个月或类似的东西。

相反 - 只需计算天数差异(在具体示例中为(finish - start).TotalDays)并使用AddDays

答案 1 :(得分:0)

DateTime具有“AddMonths”和“AddDays”等功能。这些采用整数值。如果您有小数月,则应保留您计算出的天数的整数差异,并将该值添加为所需精度的天数。 ins/2

另外,你确实遇到了问题,因为你在两个月之间进行ABS计算(因此它可以两种方式工作),但你没有看到在月份的日期之前/之后进行相同的修改calcuations。

答案 2 :(得分:0)

您的GetMonthsInRange的工作原理如下:每天的重量与该月的天数成反比。所以一天(即24小时)的相同时差 - 如果它落在不同的月份 - 对应于不同的小数结果,根据到GetMonthsInRange奇怪的规则。

我说这很奇怪,但也许可能有一些合同/会计理由重新定义数月的差异。例如,如果想要根据月度金额计算时间等效期间 ...

如果您希望使用反向工程AddMonths,基于相同的规则,那么与该位置一致,您可以编写如下的扩展名

public static class Extensions
{
    public static DateTime AddMonths(this DateTime date, decimal months)
    {
        var start = date;
        decimal daysInStartMonth = DateTime.DaysInMonth(start.Year, start.Month);
        var daysApartInStartMonth = (daysInStartMonth - start.Day + 1) / daysInStartMonth;
        if (months <= daysApartInStartMonth)
        {
            return date.AddDays((double)(months * daysInStartMonth - 1));
        }
        var finish = date.AddMonths(1);
        int monthsApart = 0;
        if (months > daysApartInStartMonth + 1)
        {
            monthsApart = (int)(months - daysApartInStartMonth);
            finish = finish.AddMonths(monthsApart);

        }
        decimal daysInFinishMonth = DateTime.DaysInMonth(finish.Year, finish.Month);
        var startOfFinishMonth = new DateTime(finish.Year, finish.Month, 1).AddDays(-1);
        decimal remaining = months - daysApartInStartMonth - monthsApart;
        return startOfFinishMonth.AddDays((double)(remaining * daysInFinishMonth));
    }
}

测试这是否符合您的需求。我假设您希望GetMonthsInRangeAddMonths保持一致。

可能的测试用例生成器

        bool ok = true;
        for (int i = 0; i < 1000; i++)
        {
            var day1 = new DateTime(2017, 2, 16);
            var day2 = new DateTime(2017, 2, 16).AddDays(i);
            var test = Extensions.GetMonthsInRange(day1, day2);
            var res = day1.AddMonths(test);
            var check = DateTime.Compare(day2, res);
            if (check != 0)
            {
                ok = false;
                break;
            }
        }

答案 3 :(得分:0)

从技术上讲,您可以尝试在两个 AddMonth结果之间使用线性插值

private static DateTime AddMonths(DateTime source, double months) {
  int left = (int) Math.Floor(months);
  int right = (int) Math.Ceiling(months);

  double days = 
     (source.AddMonths(right) - source.AddMonths(left)).TotalDays * 
     (months - Math.Floor(months));

  return source.AddMonths(left).AddDays(days);
}

试验:

DateTime source = DateTime.Today;
double add = 1.0;

Console.Write($"{source} + {add} = {AddMonths(source, add)}");

add = 1.1;
Console.Write($"{source} + {add} = {AddMonths(source, add)}");

add = 1.5;
Console.Write($"{source} + {add} = {AddMonths(source, add)}");

add = 1.95;
Console.Write($"{source} + {add} = {AddMonths(source, add)}");

add = 2.0;
Console.Write($"{source} + {add} = {AddMonths(source, add)}");

结果:

16.02.2017 0:00:00 + 1    = 16.03.2017  0:00:00
16.02.2017 0:00:00 + 1.1  = 19.03.2017  2:24:00
16.02.2017 0:00:00 + 1.5  = 31.03.2017 12:00:00
16.02.2017 0:00:00 + 1.95 = 14.04.2017 10:48:00 
16.02.2017 0:00:00 + 2    = 16.04.2017  0:00:00

但是,请记住,几个月的长度不同[28..31]天,插值只是估算