Різниця в місяцях між двома датами


334

Як обчислити різницю в місяцях між двома датами в C #?

Чи є еквівалент методу VB DateDiff()в C #. Мені потрібно знайти різницю в місяцях між двома датами, які розрізняються роками. У документації написано, що я можу використовувати TimeSpan:

TimeSpan ts = date1 - date2;

але це дає мені дані за Дні. Я не хочу ділити це число на 30, тому що не кожен місяць становить 30 днів, і оскільки два значення операнда досить відокремлені один від одного, я боюся, що розділення на 30 може дати мені неправильне значення.

Будь-які пропозиції?


27
Визначте "різницю в місяцях", яка різниця в місяцях між "1,2010 травня" та "16 червня 2010 року"? 1,5, 1 чи щось інше?
Чен Чен

7
Або, щоб наголосити далі на цьому, яка різниця у місяцях між 31 грудня 2010 року та 1 січня 2011 року? Залежно від денного часу це може бути різницею лише в 1 секунду; Ви б вважали це різницею в один місяць?
stakx - більше не вносить дописи

Ось простий і короткий код у разі, ви все ще не можете отримати відповідь, см цьому POST stackoverflow.com/questions/8820603 / ...
wirol

11
Денні: 1 місяць і 15 днів. стакс: 0 місяців і 1 день. Справа в тому, щоб отримати компонент місяця . Це здається мені досить очевидним і є гарним питанням.
Кірк Волл

Відповіді:


462

Припустимо, що день місяця не має значення (тобто різниця між 2011.1.1 та 2010.12.31 дорівнює 1), а дата1> дата2 дає позитивне значення, а дата2> дата1 - негативне значення

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

Або, якщо припустити, що ви хочете отримати приблизну кількість «середніх місяців» між двома датами, наступні повинні працювати для всіх, але дуже величезних відмінностей у датах.

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

Зверніть увагу: якщо ви використовували останнє рішення, тоді ваші одиничні тести повинні зазначати найширший діапазон дат, з яким ваша програма призначена для роботи, і відповідно підтверджувати результати обчислення.


Оновлення (завдяки Гері )

Якщо використовується метод "середніх місяців", трохи більш точне число, яке слід використовувати для "середньої кількості днів на рік", становить 365,2425 .


3
@Kurru - 365/12 - це лише приблизна міра середньої тривалості місяця в днях. Це неточний захід. Для невеликих діапазонів дат цю неточність можна допустити, але для дуже великих діапазонів дат ця неточність може стати значною.
Адам Ральф

21
Я думаю, що необхідно враховувати компонент Дня. Щось подібне (date1.Year - date2.Year) * 12 + date1.Month - date2.Month + (date1.Day >= date2.Day ? 0 : -1)
DrunkCoder

2
@DrunkCoder це залежить від вимог даної системи. У деяких випадках рішення може бути найкращим вибором. Наприклад, важливо врахувати, що відбувається, коли дві дати тривають 31 день місяця, 30 денний місяць, 28 день лютого або 29 лютого. Якщо результати вашої формули дають те, що вимагає система, то, очевидно, це правильний вибір. Якщо ні, то потрібно щось інше.
Адам Ральф

6
На друге, що сказав Адам, я витратив роки на написання коду для Актурарі. Деякі підрахунки були розділені на кількість днів, округлившись до 30, щоб отримати місячну цифру . Іноді підрахунок місяців, що передбачаються, що кожна дата починається з першого місяця, підраховуйте цілі місяці відповідно . Немає кращого методу, коли мова йде про підрахунок дат. Якщо ви не клієнт, для якого ви пишете код, підніміть цю ланцюг назад і уточніть її, можливо, бухгалтером.
Бінарний занепокоєння

1
365.2425 - трохи точніша кількість днів у григоріанському календарі, якщо саме цим ви користуєтесь. Однак, за DateTime.MaxValue (1 січня 10000 року) це лише різниця приблизно за 59 днів. Крім того, визначення року може сильно відрізнятися залежно від вашої точки зору en.wikipedia.org/wiki/Year .
Гері

207

Ось комплексне рішення для повернення a DateTimeSpan, схожого на a TimeSpan, за винятком того, що воно включає всі компоненти дати, крім компонентів часу.

Використання:

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/2012з 11/30/2012. Зараз результат - 1місяць.
Кірк Волл

@KirkWoll спасибі за оновлення, можливо, у мене з’явилося ще кілька дозволів, дозвольте мені підтвердити це після деяких тестів. Хороша робота :)
Deeptechtons

1
Я написав відповідь stackoverflow.com/a/17537472/1737957 на подібне запитання, в якому перевіряв запропоновані відповіді (і виявив, що більшість з них не працює). Ця відповідь одна з небагатьох, яка спрацьовує (за моїм тестовим набором). Посилання на github на мою відповідь.
jwg

@KirkWoll - ця відповідь, здається, не працює для кращих випадків, коли з дати має значення дня, що перевищує місяць місяця, або коли вихідною датою є високосний день. Спробуйте 2020-02-29до 2021-06-29- вона повертає «1y ого 1d», але значення має бути «1y ого 0d», вірно?
Енігмативність

37

Ви могли б зробити

if ( date1.AddMonths(x) > date2 )

Це так просто і працює ідеально для мене. Я був приємно здивований, побачивши, що це працює за призначенням під час підрахунку дати з кінця 1 місяця до дати в кінці наступного місяця, у якої менше днів. Наприклад .. 1-31-2018 + 1 місяць = 28 лютого 218
lucky.expert

Це одне з кращих рішень.
barnacle.m

Дійсно просте та ефективне рішення! Найкраща запропонована відповідь.
Седрік

2
Що робити, якщо дата1 = 2018-10-28 та дата2 = 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.
Крістіан Баділа

Доданий короткий зміст із запропонованим тут
Крістіан Баділа

Я не впевнений, що я його отримую, моя функція повертається 6 як слід: dotnetfiddle.net/MRZNnC
Guillaume86

Я скопіював сюди тестовий випадок вручну, і він має помилку. Несправні специфікації повинні бути: new { From = new DateTime(2015, 12, 31), To = new DateTime(2016, 06, 30), Result = 6 }. "Помилка" полягає в to.Day < from.Dayкоді, який не враховує, що місяці можуть закінчуватися в інший "день місяця". У цьому випадку з 31 грудня 2015 року до 30 червня 2016 року пройде 6 повних місяців (з червня - 30 днів), але ваш код повернеться 5.
Крістіан Баділа

3
На мою думку, це очікувана поведінка, ну, або я принаймні очікую цього. Я уточнив, що цілий місяць - це коли ви досягнете того ж дня (або наступного місяця, як у цьому випадку).
Guillaume86

22

Я перевірив використання цього методу в VB.NET через MSDN і, здається, він має багато звичок. У C # немає такого вбудованого методу. (Навіть це не дуже гарна ідея) ви можете зателефонувати на VB в C #.

  1. Додайте Microsoft.VisualBasic.dllдо свого проекту як орієнтир
  2. використовувати Microsoft.VisualBasic.DateAndTime.DateDiff у своєму коді

7
Чому ти вважаєш, що це не гарна ідея? Інтуїтивно, я б здогадався, що бібліотека - це лише інша. NET-бібліотека. Зауважте, я тут граю захисника диявола, я б також неохоче робив це, оскільки це просто "почувається неправильно" (вид обману), але мені цікаво, чи є якась переконлива технічна причина не робити цього.
Адам Ральф

3
@AdamRalph: Зовсім ніяких причин не робити цього. Ці бібліотеки реалізовані в 100% керованому коді, так що все так само, як і все інше. Єдина можлива відмінність полягає в тому, що Microsoft.VisualBasic.dllмодуль повинен бути завантажений, але час, необхідний для цього, незначний. Немає підстав обманювати себе з ретельно перевірених та корисних функцій лише тому, що ви вирішили написати свою програму на C #. (Це стосується і таких речей My.Application.SplashScreen.)
Коді Грей

3
Ви б передумали, якби знали, що це написано на C #? Це було. За тією ж логікою використання System.Data та PresentationFramework також обманює, значні частини цього тексту написані на C ++ / CLI.
Ганс Пасант

3
@AdamRalph: Будь-які конкретні приклади того "дивного багажу", який весни на розум? Або ви це говорите суто гіпотетично? І так, це може зіпсуватись з розумом деяких ваших приятелів C #, які написали епічну кількість коду, щоб зробити щось, що ви можете зробити в одному рядку з правильним usingтвердженням, але я сумніваюся, що це завдасть серйозної шкоди.
Коді Грей

1
@Cody Grey: погоджено, приклад є тривіальним, як ви ілюструєте. Це додатковий код "шум", який вводиться за допомогою виклику такого незвичайного (від C # POV) методу, якого я б хотів уникати. У добре організованій команді подібні речі все-таки підлягають перегляду коду, і їх можна легко уникнути. BTW - я не намагаюся атакувати VB6 / VB.NET. Я описав такі методи як "дивні" лише тому, що у .NET POV немає причин для DateAndTime.Year()існування, враховуючи, що DateTimeмає Yearвластивість. Він існує лише для того, щоб VB.NET виглядав як VB6. Як колишній програміст VB6, я можу оцінити це ;-)
Адам Ральф

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
Уявіть startі endоднакові. Тоді ви отримуєте результат 1. Як це правильно? Чому ви додаєте 1 до результату? Хто голосує за цю відповідь: - /?
пав

Для ідентичних дат він буде давати вихід як 1. В основному він буде рахувати всі місяці, включаючи початок і кінець місяців.
Chirag

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 Fiddle


4

Ви можете використовувати клас DateDiff бібліотеки часових періодів для .NET :

// ----------------------------------------------------------------------
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 і застосувати абсолютно таку ж логіку, що і вище, і AddYears замість AddMonths в циклі while.


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

Ось просте рішення, яке працює принаймні для мене. Це, мабуть, не найшвидше, тому що він використовує класну функцію AddMonth DateTime у циклі:

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
Дейв Кузен

1

Ви можете мати функцію щось подібне.

Наприклад, з 2012/12/27 по 2012/12/29 стає 3 дні. Так само з 2012/12/15 по 2013/01/15 стає 2 місяці, тому що до 2013/01/14 це 1 місяць. з 15-го починається 2-й місяць.

Ви можете видалити "=" у другій, якщо умова, якщо ви не хочете включати обидва дні у розрахунок. тобто з 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 лише для року, місяця, дня. Ви також можете завантажити бібліотеку DateDiff в C #.

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

Це у відповідь на відповідь Кірка Волла. У мене недостатньо балів репутації, щоб відповісти на коментар ...

Мені сподобалося рішення Кірка і збирався безсоромно зірвати його і використовувати його у своєму коді, але коли я переглянув його, зрозумів, що це занадто складно. Непотрібне перемикання та циклічність, і публічний конструктор, який безглуздо використовувати.

Ось мій перепис:

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

У моєму випадку потрібно обчислити повний місяць від дати початку до дня до цього дня в наступному місяці або від початку до кінця місяця.


Наприклад: з 1.01.2018 по 31.01.2018 - це повний місяць.
Ex2: з 01.01.2018 по 2.04.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

Чому б просто не зробити startDate.AddMonths (monthCount) .ToShortDateString ()? Це не відповідає на початкове запитання, яке було задано все одно!
TabbyCool

о, вибачте @TabbyCool, цей код добре працює в моїй програмі! Правило програмістів говорить: спочатку код працює, а потім оптимізація! tanx для коментаря ур :)
reza akhlaghi

1

Моє розуміння загальної різниці місяців між двома датами є складовою та дробовою частиною (дата має значення).

Невід'ємною частиною є різниця у місячних місяцях.

Дробова частина для мене - це різниця% дня (до повних днів місяця) між початковим та кінцевим місяцями.

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

На це не так багато чітких відповідей, оскільки ти завжди припускаєш речі.

Це рішення обчислює між двома датами місяцями між припущенням, що ви хочете зберегти день місяця для порівняння (мається на увазі, що день місяця враховується в обчисленні)

Наприклад, якщо у вас дата 30 січня 2012 року, 29 лютого 2012 року не буде місяця, а 01 березня 2013 року.

Це було випробувано досить ретельно, ймовірно, це очистить пізніше, коли ми використовуємо його, але тут:

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
Бассі

1

Ця проста статична функція обчислює частку місяців між двома датами, наприклад

  • 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

Вміти підрахувати різницю між двома датами в місяцях - це цілком логічна річ, і це потрібно в багатьох бізнес-додатках. Кілька кодерів, котрі надали коментарі, такі як - яка різниця в місяцях між "1 травня 2010 року" та "16 червня 2010 р., Яка різниця в місяцях між 31 грудня 2010 року та 1 січня 2011 року?" - не зрозуміли. основи ділових додатків.

Ось відповідь на вищезазначені 2 коментарі - Кількість місяців між 1 травня 2010 та 16 червня 2010 року становить 1 місяць, кількість місяців між 31 грудня 2010 року та 1 січня 2011 року становить 0. Це було б дуже нерозумно обчислювати їх як 1,5 місяці та 1 секунду, як запропонували вище кодери.

Люди, які працювали над кредитною карткою, обробкою іпотеки, обробкою податками, обробкою орендної плати, щомісячними розрахунками відсотків та великою кількістю інших бізнес-рішень, погодились би.

Проблема полягає в тому, що така функція не включена в C # або VB.NET з цього питання. Datediff враховує лише роки чи місячні компоненти, тому насправді марно.

Ось кілька прикладів реального життя, де потрібно і правильно можна обчислити місяці:

Ви жили в короткостроковій оренді від 18-ти до 23-го серпня. Скільки місяців ти там пробув? Відповідь проста - 6 місяців

У вас є банківська рахунок, де відсотки нараховуються та виплачуються в кінці кожного місяця. Ви здаєте гроші на 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 Ви припускаєте, що все програмне забезпечення є "бізнес-додатком". Мета коду, про який йде мова, не згадується. Ви також припускаєте, що всі "бізнес-програми" мають однакові правила, що, безумовно, не відповідає дійсності.
Джессі Вебб

0

Розширена структура Kirks з ToString (формат) та тривалість (довгі мс)

 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));
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.