Różnica w miesiącach między dwiema datami

334

Jak obliczyć różnicę w miesiącach między dwiema datami w C #?

Czy istnieje odpowiednik metody VB DateDiff()w C #. Muszę znaleźć różnicę w miesiącach między dwiema datami, które są latami. Dokumentacja mówi, że mogę używać TimeSpantakich jak:

TimeSpan ts = date1 - date2;

ale to daje mi dane w dniach. Nie chcę dzielić tej liczby przez 30 ponieważ nie co miesiąc to 30 dni, a ponieważ dwie wartości operandów są zupełnie od siebie różne, obawiam się, że podzielenie przez 30 może dać mi niewłaściwą wartość.

Jakieś sugestie?

Rauf
źródło
27
Zdefiniuj „różnicę w miesiącach”, jaka jest różnica w miesiącach między „1 maja 2010 r.” A „16 czerwca 2010 r.”? 1,5, 1 czy coś jeszcze?
Cheng Chen,
7
Lub, aby jeszcze bardziej podkreślić ten punkt, jaka jest różnica w miesiącach między 31 grudnia 2010 r. A 1 stycznia 2011 r.? W zależności od dnia może to być różnica tylko 1 sekundy; czy policzyłbyś to jako różnicę jednego miesiąca?
stakx - już nie wnosi
Oto prosty i krótki kod na wypadek, gdybyś nadal nie mógł uzyskać odpowiedzi, zobacz ten POST stackoverflow.com/questions/8820603/...
wirol
11
Danny: 1 miesiąc i 15 dni. stakx: 0 miesięcy i 1 dzień. Chodzi o to, aby uzyskać składnik miesiąca . Wydaje mi się to dość oczywiste i jest to dobre pytanie.
Kirk Woll,

Odpowiedzi:

462

Zakładając, że dzień miesiąca nie ma znaczenia (tzn. Różnica między 2011.1.1 a 2010.12.31 wynosi 1), przy czym data1> data2 daje wartość dodatnią, a data2> data1 wartość ujemna

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

Lub, zakładając, że potrzebujesz przybliżonej liczby „średnich miesięcy” między dwiema datami, poniższe powinny działać dla wszystkich oprócz bardzo dużych różnic dat.

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

Uwaga: jeśli chcesz użyć tego drugiego rozwiązania, testy jednostkowe powinny określać najszerszy zakres dat, z którym aplikacja ma współpracować, i odpowiednio sprawdzać wyniki obliczeń.


Aktualizacja (dzięki dzięki Gary )

W przypadku zastosowania metody „średnich miesięcy” nieco bardziej dokładna liczba dla „średniej liczby dni w roku” wynosi 365.2425 .

Adam Ralph
źródło
3
@ Kurru - 365/12 jest jedynie przybliżoną miarą średniej długości miesiąca w dniach. Jest to niedokładna miara. W przypadku małych zakresów dat ta niedokładność może być tolerowana, ale w przypadku bardzo dużych zakresów dat ta niedokładność może stać się znacząca.
Adam Ralph
21
Myślę, że należy wziąć pod uwagę składnik Dnia. Coś w tym stylu (date1.Year - date2.Year) * 12 + date1.Month - date2.Month + (date1.Day >= date2.Day ? 0 : -1)
DrunkCoder
2
@DrunkCoder to zależy od wymagań danego systemu. W niektórych przypadkach Twoje rozwiązanie może być rzeczywiście najlepszym wyborem. Na przykład ważne jest, aby rozważyć, co się stanie, gdy dwie daty obejmują 31-dniowy miesiąc, 30-dniowy miesiąc, 28-dniowy luty lub 29-dniowy luty. Jeśli wyniki formuły spełniają wymagania systemu, jest to oczywiście właściwy wybór. Jeśli nie, to wymagane jest coś jeszcze.
Adam Ralph,
6
Po drugie, co powiedział Adam, spędziłem lata pisząc kod dla Acturaries. Niektóre obliczenia zostały podzielone przez liczbę dni, zaokrąglone w górę o 30, aby uzyskać liczbę miesięczną . Czasami liczenie miesięcy, zakładając, że każda data zaczyna się pierwszego dnia miesiąca, licz odpowiednio całe miesiące . Nie ma najlepszej metody obliczania dat. Jeśli nie jesteś klientem, dla którego piszesz kod, popchnij to z powrotem do łańcucha i uzyskaj wyjaśnienie, być może przez księgowego klienta.
Binary Worrier
1
365.2425 to nieco dokładniejsza liczba dni w kalendarzu gregoriańskim, jeśli tego właśnie używasz. Jednak według DateTime.MaxValue (1 stycznia 10000) różnica wynosi tylko około 59 dni. Ponadto definicja roku może się znacznie różnić w zależności od twojej perspektywy en.wikipedia.org/wiki/Rok .
Gary
207

Oto kompleksowe rozwiązanie do zwrotu DateTimeSpan, podobne doTimeSpan , z tym wyjątkiem, że obejmuje wszystkie składniki daty oprócz składników czasu.

Stosowanie:

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

Wyjścia:

Lata: 1
Miesiące: 5
dni: 27
godzin: 1
minut: 36
sekund: 50
milisekund: 0

Dla wygody skupiłem logikę na DateTimeSpanstrukturze, ale możesz przenieść metodę CompareDatestam, gdzie uznasz to za stosowne. Pamiętaj też, że nie ma znaczenia, która data nastąpi wcześniej.

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;
    }
}
Kirk Woll
źródło
2
@KirkWoll dzięki. Ale dlaczego DateTimeSpan zwraca 34dni dla tej różnicy czasu i daty w rzeczywistości jest to 35 timeanddate.com/date/…
Deeptechtons
@Deeptechtons, nice catch. Zwróciłem uwagę na kilka problemów, z których oba dotyczą daty początkowej, 31a data „mija” miesiące z mniejszą liczbą dni. Odwróciłem logikę (tak, aby przechodziła od wczesnego do późniejszego niż na odwrót) i teraz gromadzi miesiące bez zmiany bieżącej daty (i tym samym przechodząc między miesiącami z mniejszą liczbą dni) Wciąż nie do końca pewny, jaki jest idealny wynik powinno być w porównaniu 10/31/2012do 11/30/2012. W tej chwili wynikiem jest 1miesiąc.
Kirk Woll
@KirkWoll dzięki za aktualizację, może dostałem jeszcze kilka gotchas, pozwól mi potwierdzić po kilku testach Dobra robota :)
Deeptechtons
1
Napisałem odpowiedź stackoverflow.com/a/17537472/1737957 na podobne pytanie, które testowało proponowane odpowiedzi (i stwierdziłem, że większość z nich nie działa). Ta odpowiedź jest jedną z niewielu, która działa (zgodnie z moim zestawem testów). Link do github na mojej odpowiedzi.
jwg
@KirkWoll - Ta odpowiedź nie działa w przypadku przypadków skrajnych, w których data od ma wartość dnia wyższą niż miesiąc do daty lub gdy data źródłowa jest dniem przestępnym. Starają 2020-02-29się 2021-06-29- zwraca „1r 4m 1d”, ale ta wartość powinna być „1r 4m 0d”, prawda?
Enigmativity,
37

Mógłbyś

if ( date1.AddMonths(x) > date2 )
Mongus Pong
źródło
To takie proste i działa idealnie dla mnie. Byłem mile zaskoczony, widząc, że działa zgodnie z przeznaczeniem, obliczając datę od końca 1 miesiąca do daty pod koniec następnego miesiąca, która ma mniej dni. Na przykład .. 1-31-2018 + 1 miesiąc = 28 218
lucky.expert
To jedno z lepszych rozwiązań.
barnacle.m
Naprawdę proste i wydajne rozwiązanie! Najlepsza zaproponowana odpowiedź.
Cedric Arnould
2
Co jeśli data 1 = 28.10.2018 i data2 = 21.12.2018? Odpowiedź będzie 2., a prawidłowa odpowiedź będzie 3. Z powodu zakresu dat jest to 3 miesiące. jeśli liczymy tylko miesiące ignorując dni. Więc ta odpowiedź NIE jest poprawna.
Tommix
Bardziej logiczne byłoby: if ( date1.AddMonths(x).Month == date2.Month )wystarczy użyć x + 1 jako liczby miesięcy
Tommix
34

Jeśli chcesz dokładną liczbę pełnych miesięcy, zawsze dodatnią (2000-01-15, 2000-02-14 zwraca 0), biorąc pod uwagę pełny miesiąc, kiedy osiągniesz ten sam dzień w następnym miesiącu (coś w rodzaju obliczenia wieku)

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

Edytuj powód: stary kod był nieprawidłowy w niektórych przypadkach, takich jak:

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 },
};
Guillaume86
źródło
Aby uniknąć nieporozumień dla innych ludzi, uważam, że to rozwiązanie jest nieprawidłowe. Wykorzystanie przypadku testowego: new { From = new DateTime(2015, 12, 31), To = new DateTime(2015, 6, 30), Result = 6 } test zakończy się niepowodzeniem, ponieważ wynik to 5.
Cristian Badila
Dodano szybką treść z poprawką, którą proponuję tutaj
Cristian Badila
Nie jestem pewien, czy to rozumiem, moja funkcja zwraca 6 tak, jak powinna: dotnetfiddle.net/MRZNnC
Guillaume86,
Skopiowałem tutaj przypadek testowy i ma błąd. Zepsuty Spec powinno być: new { From = new DateTime(2015, 12, 31), To = new DateTime(2016, 06, 30), Result = 6 }. „Błąd” polega na to.Day < from.Daykodzie, który nie bierze pod uwagę, że miesiące mogą zakończyć się innym „dniem miesiąca”. W takim przypadku od 31 grudnia 2015 r. Do 30 czerwca 2016 r. Minie 6 pełnych miesięcy (ponieważ czerwiec ma 30 dni), ale kod powróci 5.
Cristian Badila
3
Moim zdaniem jest to oczekiwane zachowanie, a przynajmniej takie, którego się spodziewam. Oczekiwałem, że pełny miesiąc przypada na ten sam dzień (lub następny miesiąc, jak w tym przypadku).
Guillaume86
22

Sprawdziłem użycie tej metody w VB.NET przez MSDN i wydaje się, że ma ona wiele zastosowań. Nie ma takiej wbudowanej metody w języku C #. (Nawet nie jest to dobry pomysł) możesz wywoływać VB w C #.

  1. Dodaj Microsoft.VisualBasic.dlldo projektu jako odniesienie
  2. użyj Microsoft.VisualBasic.DateAndTime.DateDiff w swoim kodzie
Cheng Chen
źródło
7
Jak myślisz, dlaczego to nie jest dobry pomysł? Intuicyjnie zgaduję, że biblioteka jest „tylko kolejną biblioteką .NET” dla środowiska wykonawczego. Zauważ, że gram tutaj jako adwokat diabła, byłbym też chętny, aby to zrobić, ponieważ po prostu „czuję się źle” (rodzaj oszustwa), ale zastanawiam się, czy istnieje jakiś przekonujący techniczny powód, aby tego nie robić.
Adam Ralph
3
@AdamRalph: W ogóle nie ma powodu, aby tego nie robić. Te biblioteki są zaimplementowane w 100% zarządzanym kodzie, więc wszystko jest takie samo jak wszystko inne. Jedyną możliwą różnicą jest to, że Microsoft.VisualBasic.dllmoduł musi zostać załadowany, ale czas potrzebny na wykonanie tego jest znikomy. Nie ma powodu, aby oszukiwać się dzięki dokładnie przetestowanym i przydatnym funkcjom tylko dlatego, że zdecydowałeś się napisać swój program w języku C #. (Dotyczy to również takich rzeczy My.Application.SplashScreen.)
Cody Gray
3
Czy zmieniłbyś zdanie, gdybyś wiedział, że został napisany w C #? To było. Zgodnie z tą samą logiką, używanie System.Data i PresentationFramework również oszukuje, znaczna jego część jest napisana w C ++ / CLI.
Hans Passant
3
@AdamRalph: Są jakieś szczególne przykłady tego „dziwnego bagażu”, który przychodzi ci na myśl? A może mówisz to czysto hipotetycznie? I tak, może bałaganić umysły niektórych twoich kumpli z C #, którzy piszą imponującą ilość kodu, aby zrobić coś, co możesz zrobić w jednym wierszu z prawidłowym usingstwierdzeniem, ale wątpię, czy to spowoduje poważne szkody.
Cody Gray
1
@Cody Gray: zgadzam się, przykład jest trywialny, jak ilustrujesz. Jest to dodatkowy „szum” kodu wprowadzony przez wywołanie tak niezwykłej (z C # POV) metody, której chętnie bym unikał. W dobrze zorganizowanym zespole takie rzeczy byłyby i tak wychwycone podczas przeglądu kodu i można ich łatwo uniknąć. BTW - Nie próbuję atakować VB6 / VB.NET. Opisałem takie metody jako „dziwne” tylko dlatego, że z POV platformy .NET nie ma powodu, DateAndTime.Year()aby istnieć, biorąc pod uwagę, że DateTimema Yearwłaściwość. Istnieje tylko po to, aby VB.NET wyglądał bardziej jak VB6. Jako były programista VB6 mogę to docenić ;-)
Adam Ralph
10

Aby uzyskać różnicę w miesiącach (włącznie z początkiem i końcem), niezależnie od dat:

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);
Chirag
źródło
5
Wyobraź sobie starti endsą identyczne. Otrzymujesz wynik 1. Jak to prawda? Dlaczego dodajesz 1 do wyniku? Kto głosuje za odpowiedzią: - /?
Paweł
Dla identycznych dat da wynik jako 1. Zasadniczo będzie liczyć wszystkie miesiące włącznie z początkowymi i końcowymi miesiącami.
Chirag,
3
nie brzmi dla mnie jak różnica między dwoma przedmiotami. Jaka jest różnica między 2 a 2? Czy to naprawdę 1? Sugerowałbym, że różnica wynosi 0
Paweł
8

Użyj czasu Noda :

LocalDate start = new LocalDate(2013, 1, 5);
LocalDate end = new LocalDate(2014, 6, 1);
Period period = Period.Between(start, end, PeriodUnits.Months);
Console.WriteLine(period.Months); // 16

(przykładowe źródło)

Edward Brey
źródło
7

Potrzebowałem tylko czegoś prostego do spełnienia, np. Dat zatrudnienia, w których wpisany jest tylko miesiąc / rok, więc chciałem, aby przepracowano odrębne lata i miesiące. To jest to, czego używam, tutaj tylko dla użyteczności

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

jenson-button-event
źródło
4

Możesz użyć klasy DateDiff biblioteki okresów dla platformy .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

źródło
2

Oto mój wkład w różnicę w miesiącach, które uważam za dokładne:

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

Stosowanie:

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

Możesz utworzyć inną metodę o nazwie DiffYears i zastosować dokładnie taką samą logikę jak powyżej oraz AddYears zamiast AddMonths w pętli while.

Morgs
źródło
2

To działało dla tego, czego potrzebowałem. Dzień miesiąca nie miał w moim przypadku znaczenia, ponieważ zawsze zdarza się, że jest to ostatni dzień miesiąca.

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;
}
Elmer
źródło
2

Najdokładniejszym sposobem jest to, aby zwracać różnicę w ułamku ułamka:

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;
}
Saeed Mahmoudi
źródło
2

Oto proste rozwiązanie, które działa przynajmniej dla mnie. Prawdopodobnie nie jest to najszybszy, ponieważ korzysta z fajnej funkcji AddMonth DateTime w pętli:

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);
}
Simon Mourier
źródło
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
Mohammad Ali
źródło
1

To z mojej własnej biblioteki, zwróci różnicę miesięcy między dwiema datami.

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;
}
Wayne
źródło
1
czy to działa? Dostaję 11 na papierze Jan-31-2014iDec-31-2013
Dave Cousineau
1

Możesz mieć taką funkcję.

Na przykład od 2012/12/27 do 2012/12/29 staje się 3 dni. Podobnie od 2012/12/15 do 2013/01/15 staje się 2 miesiące, ponieważ do 2013/01/14 to 1 miesiąc. od 15. rozpoczął się drugi miesiąc.

Możesz usunąć „=” w drugim warunku, jeśli nie chcesz uwzględniać obu dni w obliczeniach. tj. od 2012/12/15 do 2013/01/15 to 1 miesiąc.

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;
}
Firnas
źródło
1

możesz użyć następującego rozszerzenia: Kod

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
}

Realizacja !

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); 
Waleed AK
źródło
1

Oto o wiele bardziej zwięzłe rozwiązanie wykorzystujące VB.Net DateDiff tylko dla roku, miesiąca i dnia. Możesz także załadować bibliotekę DateDiff w języku C #.

data1 musi wynosić <= data2

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

DO#

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);
Brent
źródło
1

Jest to odpowiedź na odpowiedź Kirka Wolla. Nie mam jeszcze wystarczającej liczby punktów reputacji, aby odpowiedzieć na komentarz ...

Podobało mi się rozwiązanie Kirka i zamierzam bezwstydnie go oderwać i użyć w moim kodzie, ale kiedy go przejrzałem, zdałem sobie sprawę, że jest to zbyt skomplikowane. Niepotrzebne przełączanie i zapętlanie oraz konstruktor publiczny, który nie ma sensu.

Oto moje przepisanie:

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

Zastosowanie 1, prawie tak samo:

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

Użycie 2, podobne:

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);
}
John A.
źródło
1

W moim przypadku wymagane jest obliczenie pełnego miesiąca od daty początkowej do dnia poprzedzającego ten dzień w następnym miesiącu lub od początku do końca miesiąca.


Np .: od 1/1/2018 do 31/1/2018 to pełny miesiąc
Ex2: od 5/1/2018 do 4/2/2018 to pełny miesiąc

więc w oparciu o to oto moje rozwiązanie:

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

Stosowanie:

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

Uwaga: w moim przypadku konieczne było obliczenie pozostałych dni po pełnych miesiącach, więc jeśli nie jest to twój przypadek, możesz zignorować wynik dni lub nawet zmienić metodę powrotu z krotki na liczbę całkowitą.

Ahmed
źródło
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);
}

To rozwiązanie służy do obliczania kosztów wynajmu / subskrypcji, w których różnica nie oznacza odejmowania, lecz ma obejmować okres w tych dwóch datach.

Sukanta
źródło
1

Istnieją 3 przypadki: ten sam rok, rok poprzedni i inne lata.

Jeśli dzień miesiąca nie ma znaczenia ...

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;
    }
}
Patrice Calvé
źródło
1

Napisałem funkcję, aby to osiągnąć, ponieważ inne sposoby nie działały dla mnie.

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());
}
reza akhlaghi
źródło
Proszę odpowiedzieć w języku angielskim (w porównaniu z dowolnym wymyślonym językiem ...)
kleopatra
Dlaczego nie zrobić po prostu startDate.AddMonths (monthCount) .ToShortDateString ()? To i tak nie odpowiada na pierwotne pytanie!
TabbyCool
o, przepraszam @TabbyCool, ten kod działa dobrze w moim programie! reguła programistów mówi: najpierw działa kod, a potem optymalizacja! tanx za komentarz :)
reza akhlaghi
1

Moje rozumienie całkowitej różnicy miesięcy między 2 datami ma integralną i ułamkową część (data ma znaczenie).

Integralną częścią jest różnica pełnych miesięcy.

Część ułamkowa jest dla mnie różnicą% dnia (do pełnych dni miesiąca) między miesiącem początkowym a końcowym.

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

Dzięki temu rozszerzeniu są to wyniki:

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
George Mavritsakis
źródło
1

Nie ma na to wielu jasnych odpowiedzi, ponieważ zawsze zakładasz różne rzeczy.

To rozwiązanie oblicza między dwiema datami miesiące między założeniem, że chcesz zapisać dzień miesiąca do porównania (co oznacza, że ​​dzień miesiąca jest uwzględniany w obliczeniach)

Przykład: jeśli masz datę 30 stycznia 2012 r., 29 lutego 2012 r. Nie będzie miesiącem, ale 01 marca 2013 r. Będzie.

Został przetestowany dość dokładnie, prawdopodobnie wyczyści go później, gdy go użyjemy, ale tutaj:

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
}
GreatNate
źródło
1

W oparciu o doskonałą pracę DateTimeSpan wykonaną powyżej, trochę znormalizowałem kod; wydaje się, że działa całkiem dobrze:

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;
  }
}
Dan Sutton
źródło
Porównując z tym, CompareDates(x, y)gdzie, x={01/02/2019 00:00:00}a y={01/05/2020 00:00:00}następnie Monthsdaje mi2
Bassie
1

Ta prosta funkcja statyczna oblicza ułamek miesięcy między dwoma czasami danych, np

  • 1.1 do 31.1. = 1,0
  • 1.4 do 15.4. = 0,5
  • 16.4 do 30.4. = 0,5
  • 1.3 do 1.4. = 1 + 1/30

Funkcja zakłada, że ​​pierwsza data jest mniejsza niż druga data. Aby poradzić sobie z ujemnymi przedziałami czasu, można łatwo zmodyfikować funkcję, wprowadzając na początku znak i zmienną zamianę.

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);
 }
Michał
źródło
0

Możliwość obliczenia różnicy między 2 datami w miesiącach jest całkowicie logiczną rzeczą i jest potrzebna w wielu aplikacjach biznesowych. Kilku programistów, którzy dostarczyli komentarze, takie jak - jaka jest różnica w miesiącach między „1 maja 2010 r.” A „16 czerwca 2010 r., Jaka jest różnica w miesiącach od 31 grudnia 2010 r. Do 1 stycznia 2011 r.? podstawy aplikacji biznesowych.

Oto odpowiedź na powyższe 2 komentarze - Liczba miesięcy między 1 maja 2010 r. A 16 czerwca 2010 r. Wynosi 1 miesiąc, liczba miesięcy między 31 grudnia 2010 r. A 1 stycznia 2011 r. Wynosi 0. To głupotą byłoby obliczyć je jako 1,5 miesiąca i 1 sekundę, jak sugerowali powyżsi koderzy.

Ludzie, którzy pracowali nad kartami kredytowymi, przetwarzaniem kredytów hipotecznych, przetwarzaniem podatków, przetwarzaniem czynszu, miesięcznymi obliczeniami odsetek i szeroką gamą innych rozwiązań biznesowych, zgodziliby się.

Problem polega na tym, że taka funkcja nie jest zawarta w C # lub VB.NET. Datediff bierze pod uwagę tylko lata lub składnik miesiąca, więc jest właściwie bezużyteczny.

Oto kilka rzeczywistych przykładów tego, gdzie trzeba i poprawnie można obliczyć miesiące:

Mieszkałeś w krótkoterminowym wynajmie od 18 lutego do 23 sierpnia. Ile miesięcy tam przebywałeś? Odpowiedź jest prosta - 6 miesięcy

Posiadasz rachunek bankowy, w którym naliczane są odsetki na koniec każdego miesiąca. Wpłacasz pieniądze 10 czerwca i wyciągasz je 29 października (tego samego roku). Przez ile miesięcy otrzymujesz odsetki? Bardzo prosta odpowiedź - 4 miesiące (znowu dodatkowe dni nie mają znaczenia)

W aplikacjach biznesowych większość czasu, kiedy trzeba obliczyć miesiące, to dlatego, że trzeba znać „pełne” miesiące na podstawie tego, jak ludzie obliczają czas; nie oparte na abstrakcyjnych / nieistotnych myślach.

Tomek
źródło
5
Jest to jeden z powodów, dla których rachunkowość nie jest matematyką. Wynik księgowy zależy od sposobu jego obliczenia. Znam twoje punkty i znam „wspólny pogląd biznesowy” w tej sprawie, ale to wyjaśnienie jest po prostu błędne. Pomiędzy 2012.11.30 a 2012.12.01 jest 0, 1/30, 1/31 lub 1 lub 2 miesiące, w zależności od tego, o co prosiłeś . Czy daty były wyłączne czy włącznie? Czy pytałeś o liczbę miesięcy przekroczonych, dotkniętych lub minionych? Czy chcesz zaokrąglić w górę, zaokrąglić w dół czy dokładnie?
quetzalcoatl
3
Teraz wyjaśnij to biznesmenowi lub księgowemu, a oni zdziwią się. Zawsze jest to dla nich „tak oczywiste, że oczywiście oznaczały X, Y i Z, jak mogłeś inaczej myśleć?” Teraz zdobądź kilku biznesmenów i postaraj się, aby uzgodnili temat. Księgowi są bardziej skłonni się zgodzić, ponieważ w pewnym momencie użyją matematyki, aby sprawdzić, z opcjami, które mogą przypadkowo zsumować dwukrotnie ten sam okres itp. Nawet twoje przykłady obliczeń są dyskusyjne i zależą od regionu lub po prostu nieważne, jak zakładają dodatkowe reguły biznesowe, takie jak ignorowanie dodatkowych dni.
quetzalcoatl
2
-1 Zakładasz, że całe oprogramowanie jest „aplikacją biznesową”. Cel tego kodu nie jest wymieniony. Zakładasz również, że wszystkie „aplikacje biznesowe” mają takie same reguły, co zdecydowanie nie jest prawdą.
Jesse Webb
0

Rozszerzona struktura Kirks z ToString (format) i Duration (długie ms)

 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;
    }
}
Ivan
źródło
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));
Paweł
źródło