Как вычислить фактическое различие месяцев (календарный год не приближение) между двумя данными датами в C#?

Пример: учитывая две даты ниже, конец всегда больше, чем или равен для запуска

запустите = 2001 01 января

закончите = 2002 15 марта

Таким образом, с 2001 01 января в конец февраля 2002 года

месяцы = 12 + 2 = 14

На 2002 март

15/30 = 0.5

таким образом, общий итог является различием 14,5 месяцев.

Очень легко удаться вручную, но как я кодирую его изящно? В данный момент у меня есть комбинация большого количества из если еще и циклы с условием продолжения для достижения, что я хочу, но я верю, там существуют простые решения.

Обновление: вывод должен быть точным (не приближение), например: если запускают 2001 01 января и заканчивают 2001 16 апреля, вывод должен быть 1 + 1 + 1 = 3 (для Jan, февраль и март) и 16 / 31 = 0,516 месяца, таким образом, общее количество 3.516.

Другой пример был бы то, если я запускаю на 2001 5 июля и конце на 2002 10 июля, вывод должен быть 11-месячным в конец июня 2002, и (31-5)/31 = 0.839 и 10/31 = 0,323 месяца, таким образом, общее количество равняется 11 + 0.839 + 0.323 = 12.162.

Я расширил код Josh Stodola и код Hightechrider:

public static decimal GetMonthsInRange(this IDateRange thisDateRange)
{
    var start = thisDateRange.Start;
    var finish = thisDateRange.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;
}
7
задан Jeff 20 July 2010 в 07:02
поделиться

8 ответов

Раньше я давал int ответ, а затем понял, что вы просили для более точного ответа. Я устал, поэтому удалил и лег спать. Столько всего, что я не мог заснуть! Почему-то меня этот вопрос очень беспокоил, и мне пришлось его решать. Итак, поехали ...

static void Main(string[] args)
{
    decimal diff;

    diff = monthDifference(new DateTime(2001, 1, 1), new DateTime(2002, 3, 15));
    Console.WriteLine(diff.ToString("n2")); //14.45

    diff = monthDifference(new DateTime(2001, 1, 1), new DateTime(2001, 4, 16));
    Console.WriteLine(diff.ToString("n2")); //3.50

    diff = monthDifference(new DateTime(2001, 7, 5), new DateTime(2002, 7, 10));
    Console.WriteLine(diff.ToString("n2")); //12.16

    Console.Read();
}

static decimal monthDifference(DateTime d1, DateTime d2)
{
    if (d1 > d2)
    {
        DateTime hold = d1;
        d1 = d2;
        d2 = hold;
    }

    int monthsApart = Math.Abs(12 * (d1.Year-d2.Year) + d1.Month - d2.Month) - 1;
    decimal daysInMonth1 = DateTime.DaysInMonth(d1.Year, d1.Month);
    decimal daysInMonth2 = DateTime.DaysInMonth(d2.Year, d2.Month);

    decimal dayPercentage = ((daysInMonth1 - d1.Day) / daysInMonth1)
                          + (d2.Day / daysInMonth2);
    return monthsApart + dayPercentage;
}

Теперь мне будут сладкие сны. Спокойной ночи :)

10
ответ дан 6 December 2019 в 21:09
поделиться

То, что вы хотите, вероятно, является чем-то близким к этому ... что в значительной степени следует за вашим объяснением того, как это вычислить:

var startofd1 = d1.AddDays(-d1.Day + 1);
var startOfNextMonthAfterd1 = startofd1.AddMonths(1);      // back to start of month and then to next month
int daysInFirstMonth = (startOfNextMonthAfterd1 - startofd1).Days;
double fraction1 = (double)(daysInFirstMonth - (d1.Day - 1)) / daysInFirstMonth;     // fractional part of first month remaining

var startofd2 = d2.AddDays(-d2.Day + 1);
var startOfNextMonthAfterd2 = startofd2.AddMonths(1);      // back to start of month and then to next month
int daysInFinalMonth = (startOfNextMonthAfterd2 - startofd2).Days;
double fraction2 = (double)(d2.Day - 1) / daysInFinalMonth;     // fractional part of last month

// now find whole months in between
int monthsInBetween = (startofd2.Year - startOfNextMonthAfterd1.Year) * 12 + (startofd2.Month - startOfNextMonthAfterd1.Month);

return monthsInBetween + fraction1 + fraction2;

NB Это не было хорошо протестировано, но оно показывает, как чтобы справиться с подобными проблемами, найдя хорошо известные даты в начале месяцев вокруг проблемных значений, а затем отработав их.

Циклы для вычисления даты и времени - всегда плохая идея: см. http://www.zuneboards.com/forums/zune-news/38143-cause-zune-30-leapyear-problem-isolated.html

2
ответ дан 6 December 2019 в 21:09
поделиться

В зависимости от того, как именно вы хотите, чтобы ваша логика работала, это, по крайней мере, даст вам приличное приближение:

// 365 days per year + 1 day per leap year = 1461 days every 4 years
// But years divisible by 100 are not leap years
// So 1461 days every 4 years - 1 day per 100th year = 36524 days every 100 years
// 12 months per year = 1200 months every 100 years
const double DaysPerMonth = 36524.0 / 1200.0;

double GetMonthsDifference(DateTime start, DateTime finish)
{
    double days = (finish - start).TotalDays;
    return days / DaysPerMonth;
}
1
ответ дан 6 December 2019 в 21:09
поделиться

Один из способов сделать это - вы немного увидите вокруг is:

private static int monthDifference(DateTime startDate, DateTime endDate)
{
    int monthsApart = 12 * (startDate.Year - endDate.Year) + startDate.Month - endDate.Month;
    return Math.Abs(monthsApart);
}

Однако вам нужны «неполные месяцы», которых это не дает. Но какой смысл сравнивать яблоки (январь / март / май / июль / август / октябрь / декабрь) с апельсинами (апрель / июнь / сентябрь / ноябрь) или даже бананами, которые иногда являются кокосами (февраль)?

Альтернатива - импортировать Microsoft.VisualBasic и сделать следующее:

    DateTime FromDate;
    DateTime ToDate;
    FromDate = DateTime.Parse("2001 Jan 01");
    ToDate = DateTime.Parse("2002 Mar 15");

    string s = DateAndTime.DateDiff (DateInterval.Month, FromDate,ToDate, FirstDayOfWeek.System, FirstWeekOfYear.System ).ToString();

Однако еще раз:

Возвращаемое значение для DateInterval.Month рассчитывается чисто из года и месяца запчастей аргументов

[Источник]

1
ответ дан 6 December 2019 в 21:09
поделиться

Просто улучшил ответ Джоша

    static decimal monthDifference(DateTime d1, DateTime d2)
    {
        if (d1 > d2)
        {
            DateTime hold = d1;
            d1 = d2;
            d2 = hold;
        }

        decimal monthsApart = Math.Abs((12 * (d1.Year - d2.Year)) + d2.Month - d1.Month - 1);


        decimal daysinStartingMonth = DateTime.DaysInMonth(d1.Year, d1.Month);
        monthsApart = monthsApart + (1-((d1.Day - 1) / daysinStartingMonth));

        //  Replace (d1.Day - 1) with d1.Day incase you DONT want to have both inclusive difference.



        decimal daysinEndingMonth = DateTime.DaysInMonth(d2.Year, d2.Month);
        monthsApart = monthsApart + (d2.Day / daysinEndingMonth);


        return monthsApart;
    } 
1
ответ дан 6 December 2019 в 21:09
поделиться

каркас как объект TimeSpan, который является результатом вычитания двух дат.

вычитание уже рассматривает различные варианты февраля (28/29 дней в месяц), так что, на мой взгляд, это лучшая практика. после того, как вы его получите, вы можете отформатировать его так, как вам больше нравится

        DateTime dates1 = new DateTime(2010, 1, 1);
        DateTime dates2 = new DateTime(2010, 3, 15);
        var span = dates1.Subtract(dates2);
        span.ToString("your format here");
-1
ответ дан 6 December 2019 в 21:09
поделиться

Это должно вам помочь вам нужно пойти:

DateTime start = new DateTime(2001, 1, 1);
DateTime finish = new DateTime(2002, 3, 15);
double diff = (finish - start).TotalDays / 30;
-1
ответ дан 6 December 2019 в 21:09
поделиться
    private Double GetTotalMonths(DateTime future, DateTime past)
    {
        Double totalMonths = 0.0;

        while ((future - past).TotalDays > 28 )
        {
            past = past.AddMonths(1);
            totalMonths += 1;
        }

        var daysInCurrent = DateTime.DaysInMonth(future.Year, future.Month);
        var remaining = future.Day - past.Day;

        totalMonths += ((Double)remaining / (Double)daysInCurrent);
        return totalMonths;
    }
-1
ответ дан 6 December 2019 в 21:09
поделиться
Другие вопросы по тегам:

Похожие вопросы: