Berechnung der Differenz in Monaten zwischen zwei Daten


128

In C # /. NET TimeSpanhat TotalDays, TotalMinutesetc., aber ich kann keine Formel für die Gesamtmonatsdifferenz herausfinden. Variable Tage pro Monat und Schaltjahre werfen mich immer wieder ab. Wie kann ich TotalMonths erhalten ?

Bearbeiten Es tut mir leid, dass ich nicht klarer bin: Ich weiß, dass ich das nicht wirklich verstehen kann, TimeSpanaber ich dachte, ich würde es verwenden TotalDaysund TotalMinuteswäre ein gutes Beispiel, um auszudrücken, wonach ich gesucht habe ... außer ich versuche, Gesamtmonate zu erhalten.

Beispiel: 25. Dezember 2009 - 6. Oktober 2009 = 2 Gesamtmonate. Der 6. Oktober bis 5. November entspricht 0 Monaten. Am 6. November, 1 Monat. Am 6. Dezember 2 Monate


2
Was erwarten Sie vom 25. Dezember 2009 bis 6. Oktober 2009?
Jeff Moser

2
Wie definieren Sie TimeSpan in Monaten?
Aliostad

1
@Aliostad - Ohne Datum könnten Sie einen Monat als 30 Tage definieren und ziemlich genau sein.
ChaosPandion

Es wurde aus irgendeinem Grund von einem Mod mit dieser Frage zusammengeführt.
Jamiec

Eigentlich musst du meinen Beitrag hier lesen, der diese Frage beantwortet und eine codierte Lösung bietet: stackoverflow.com/questions/1916358/… ignoriere die Trolle (brianary) und achte auf mein Gespräch über Kommentare mit supercat. Die Monate, die am Anfang und am Ende einer Zeitspanne liegen, die wir "verwaiste Monate" nennen, und die Frage, wie diese verwaisten Monate in Tagen definiert werden können - sobald Sie dies festgestellt haben (und wie Sie es definieren möchten) ), der Rest ist nur Code (der enthalten ist). Mein def. basiert auf dem, was meine Benutzer meiner Meinung nach erwarten werden
Erx_VB.NExT.Coder

Antworten:


222

Sie werden das nicht von a bekommen können TimeSpan, weil ein "Monat" eine variable Maßeinheit ist. Sie müssen es selbst berechnen und herausfinden, wie genau es funktionieren soll.

Sollten Daten beispielsweise einen Unterschied von einem Monat oder null Monaten aufweisen July 5, 2009und August 4, 2009ergeben? Wenn Sie sagen, es sollte einen ergeben, was ist dann mit July 31, 2009und August 1, 2009? Ist das ein Monat? Ist es einfach die Differenz der MonthWerte für die Daten oder hängt es eher mit einer tatsächlichen Zeitspanne zusammen? Die Logik zum Bestimmen all dieser Regeln ist nicht trivial, daher müssen Sie Ihre eigenen bestimmen und den entsprechenden Algorithmus implementieren.

Wenn Sie nur einen Unterschied in den Monaten wünschen - ohne Berücksichtigung der Datumswerte -, können Sie Folgendes verwenden:

public static int MonthDifference(this DateTime lValue, DateTime rValue)
{
    return (lValue.Month - rValue.Month) + 12 * (lValue.Year - rValue.Year);
}

Beachten Sie, dass dies eine relative Differenz zurückgibt. Wenn dies rValuegrößer als ist lValue, ist der Rückgabewert negativ. Wenn Sie einen absoluten Unterschied wollen, können Sie dies verwenden:

public static int MonthDifference(this DateTime lValue, DateTime rValue)
{
    return Math.Abs((lValue.Month - rValue.Month) + 12 * (lValue.Year - rValue.Year));
}

@ Dinah dies ist nur eine Annäherung, wenn Sie die wahre .Monat und .Jahre wissen wollen - Ich habe gerade eine Antwort für das gepostet, was Sie lesen können. Obwohl dies eine gute Annäherung ist (Requisiten an Adam Robinson), sollten Sie bedenken, dass Sie Ihre Benutzer nur unbeabsichtigt anlügen, wenn Sie eine dieser Annäherungen verwenden.
Erx_VB.NExT.Coder

@ Erx_VB.NExT.Coder: Danke für die Requisiten, aber während Ihre Antwort besagt, dass keine der Antworten die Tatsache berücksichtigt, dass ein Monat eine variable Maßeinheit ist, scheinen die meisten dies zu tun; Sie verwenden einfach nicht Ihre spezielle Annäherung. In diesem Fall zeigt der allererste Satz in meiner Antwort an, dass er variabel ist. Jede Antwort, einschließlich Ihrer, ist eine Annäherung , einfach weil es keine genaue Antwort ist. Ihr "2 Monate" -Ergebnis kann für verschiedene Eingaben unterschiedliche Bedeutungen haben, es handelt sich also um eine Annäherung.
Adam Robinson

meins ist jedoch keine Annäherung, wenn heute der 14. März ist, dann werden die zwei durchlässigen Monate basierend auf der Tatsache berechnet, dass Jan 31 Tage darin hatte und Februar 29 Tage darin hatte. Nun, Sie haben insofern Recht, als meine Methode nicht die Definition eines "allgemeinen" Monats ist, und Ihre ist es! Meins gilt jedoch nur, wenn Sie Dinge wie "Dieser Kommentar wurde vor x Monaten und y Tagen veröffentlicht" melden. Der Teil "AGO" macht den Unterschied, da er sich auf die vorherigen x Monate bezieht und diese vorherigen x Monate berechnet werden müssen basierend darauf, wie viele Tage in diesen x Monaten vorhanden waren! Link ....
Erx_VB.NExT.Coder

Ist das sinnvoll? Wenn Sie sich also auf bestimmte, bekannte Monate beziehen, ist meine Methode 100% genau und Sie wären eine Annäherung. Wenn Sie sich jedoch auf einen Monat im Allgemeinen beziehen, wäre Ihre Annäherung eine bessere Idee, und meins wäre einfach eine schlechte Idee (es ist nicht dafür gemacht und es wäre sinnlos, es zu benutzen). Hier ist der Link zu meinem Artikel, der das Problem beschreibt und eine Lösung bietet: stackoverflow.com/questions/1916358/…
Erx_VB.NExT.Coder

2
Dies scheint dieselbe Logik zu sein, die von der SQL Server DateDiff-Funktion (Monat, ...) verwendet wird. Es hat auch den Vorteil, dass es äußerst präzise und leicht zu erklären und zu verstehen ist. Ich würde es wie folgt erklären ... wie viele Seiten im Kalender müssten Sie umblättern, um von einem Datum zum anderen zu gelangen?
JoelFan

51

(Mir ist klar, dass dies eine alte Frage ist, aber ...)

Dies ist in reinem .NET relativ schmerzhaft. Ich würde meine eigene Noda Time- Bibliothek empfehlen , die speziell für solche Dinge entwickelt wurde:

LocalDate start = new LocalDate(2009, 10, 6);
LocalDate end = new LocalDate(2009, 12, 25);
Period period = Period.Between(start, end);
int months = period.Months;

(Es gibt andere Optionen, z. B. wenn Sie auch über Jahre hinweg nur eine Anzahl von Monaten wünschen, die Sie verwenden würden Period period = Period.Between(start, end, PeriodUnits.Months);)


Ich habe Ihre Bibliothek heruntergeladen und den oben geschriebenen Code kopiert, erhalte jedoch einen Fehler bei der Kompilierung. Fehler 1 Operator '-' kann nicht auf Operanden vom Typ 'NodaTime.LocalDate' und 'NodaTime.LocalDate' angewendet werden. Ich kenne diesen Beitrag aus 5 Jahren. Hat sich seit dieser Zeit etwas geändert, wodurch dieser Code nicht funktioniert?
Hakan Fıstık

1
@HakamFostok: Entschuldigung - es wird funktionieren, wenn 2.0 veröffentlicht wird, aber bis dahin müssen Sie verwenden Period.Between. Habe den Code so bearbeitet, dass er mit NodaTime 1.3.1 funktioniert.
Jon Skeet

Vielen Dank, die NodaTime-Bibliothek hat genau das getan, was ich tun möchte. Ich wollte nicht nur die Monate zwischen zwei Daten berechnen, sondern auch die verbleibenden Tage, und genau das hat NodaTime getan, nochmals vielen Dank.
Hakan Fıstık

1
@ JonSkeet Deine Bibliothek von dir ist wirklich schwarze Magie. Termine beißen mich die ganze Zeit. Dieses Code-Snippet hat mir enorm viel Zeit gespart.
onefootswill

28

Vielleicht möchten Sie nichts über Monatsfraktionen wissen. Was ist mit diesem Code?


public static class DateTimeExtensions
{
    public static int TotalMonths(this DateTime start, DateTime end)
    {
        return (start.Year * 12 + start.Month) - (end.Year * 12 + end.Month);
    }
}

//  Console.WriteLine(
//     DateTime.Now.TotalMonths(
//         DateTime.Now.AddMonths(-1))); // prints "1"



1
Ich verstehe die * 100 nicht. Sollte es * 12 sein?
Rüschen

9

Sie müssen zunächst definieren, was Sie unter TotalMonths verstehen.
Eine einfache Definition legt einen Monat auf 30,4 Tage (365,25 / 12).

Darüber hinaus scheint jede Definition einschließlich Brüchen nutzlos zu sein, und der häufigere ganzzahlige Wert (ganze Monate zwischen Daten) hängt auch von nicht standardmäßigen Geschäftsregeln ab.


9

Ich habe eine sehr einfache Erweiterungsmethode für DateTimeund DateTimeOffsetdazu geschrieben. Ich wollte, dass es genau so funktioniert, wie eine TotalMonthsImmobilie TimeSpanfunktionieren würde: dh die Anzahl der vollständigen Monate zwischen zwei Daten zurückgeben, wobei alle Teilmonate ignoriert werden. Weil es darauf basiert DateTime.AddMonths(), respektiert es unterschiedliche Monatslängen und gibt zurück, was ein Mensch als Zeitraum von Monaten verstehen würde.

(Leider können Sie es nicht als Erweiterungsmethode in TimeSpan implementieren, da das Wissen über die tatsächlich verwendeten Daten nicht erhalten bleibt und sie monatelang wichtig sind.)

Der Code und die Tests sind beide auf GitHub verfügbar . Der Code ist sehr einfach:

public static int GetTotalMonthsFrom(this DateTime dt1, DateTime dt2)
{
    DateTime earlyDate = (dt1 > dt2) ? dt2.Date : dt1.Date;
    DateTime lateDate = (dt1 > dt2) ? dt1.Date : dt2.Date;

    // Start with 1 month's difference and keep incrementing
    // until we overshoot the late date
    int monthsDiff = 1;
    while (earlyDate.AddMonths(monthsDiff) <= lateDate)
    {
        monthsDiff++;
    }

    return monthsDiff - 1;
}

Und es besteht alle diese Unit-Testfälle:

// Simple comparison
Assert.AreEqual(1, new DateTime(2014, 1, 1).GetTotalMonthsFrom(new DateTime(2014, 2, 1)));
// Just under 1 month's diff
Assert.AreEqual(0, new DateTime(2014, 1, 1).GetTotalMonthsFrom(new DateTime(2014, 1, 31)));
// Just over 1 month's diff
Assert.AreEqual(1, new DateTime(2014, 1, 1).GetTotalMonthsFrom(new DateTime(2014, 2, 2)));
// 31 Jan to 28 Feb
Assert.AreEqual(1, new DateTime(2014, 1, 31).GetTotalMonthsFrom(new DateTime(2014, 2, 28)));
// Leap year 29 Feb to 29 Mar
Assert.AreEqual(1, new DateTime(2012, 2, 29).GetTotalMonthsFrom(new DateTime(2012, 3, 29)));
// Whole year minus a day
Assert.AreEqual(11, new DateTime(2012, 1, 1).GetTotalMonthsFrom(new DateTime(2012, 12, 31)));
// Whole year
Assert.AreEqual(12, new DateTime(2012, 1, 1).GetTotalMonthsFrom(new DateTime(2013, 1, 1)));
// 29 Feb (leap) to 28 Feb (non-leap)
Assert.AreEqual(12, new DateTime(2012, 2, 29).GetTotalMonthsFrom(new DateTime(2013, 2, 28)));
// 100 years
Assert.AreEqual(1200, new DateTime(2000, 1, 1).GetTotalMonthsFrom(new DateTime(2100, 1, 1)));
// Same date
Assert.AreEqual(0, new DateTime(2014, 8, 5).GetTotalMonthsFrom(new DateTime(2014, 8, 5)));
// Past date
Assert.AreEqual(6, new DateTime(2012, 1, 1).GetTotalMonthsFrom(new DateTime(2011, 6, 10)));

3
Rustikale, aber beste Lösung. Kopiert und eingefügt. Vielen Dank
Daniel Dolz

8

Sie müssen es selbst aus den Datenzeiten herausarbeiten. Wie Sie mit den Stub-Tagen am Ende umgehen, hängt davon ab, wofür Sie sie verwenden möchten.

Eine Methode wäre, den Monat zu zählen und am Ende die Tage zu korrigieren. Etwas wie:

   DateTime start = new DateTime(2003, 12, 25);
   DateTime end = new DateTime(2009, 10, 6);
   int compMonth = (end.Month + end.Year * 12) - (start.Month + start.Year * 12);
   double daysInEndMonth = (end - end.AddMonths(1)).Days;
   double months = compMonth + (start.Day - end.Day) / daysInEndMonth;

Netter Code, obwohl 1 Fehler: stattdessen: (als 28. Februar + 1 Monat == 28. März) :-) // DezimaltageInEndMonth = (Ende - Ende.AddMonths (1)). Tage; Ich schlage vor: decimal daysInEndMonth = DateTime.DaysInMonth (end.Year, end.Month) * -1;
Bezieur

3

Ich würde es so machen:

static int TotelMonthDifference(this DateTime dtThis, DateTime dtOther)
{
    int intReturn = 0;

    dtThis = dtThis.Date.AddDays(-(dtThis.Day-1));
    dtOther = dtOther.Date.AddDays(-(dtOther.Day-1));

    while (dtOther.Date > dtThis.Date)
    {
        intReturn++;     
        dtThis = dtThis.AddMonths(1);
    }

    return intReturn;
}

4
Das ist sicherlich ein Algorithmus, aber es könnte erheblich vereinfacht werdenreturn (dtOther.Month - dtThis.Month) + 12 * (dtOther.Year - dtThis.Year);
Adam Robinson

1
Zwei Probleme: Sie beginnen mit 2 Daten, nicht mit einer Zeitspanne. Zweitens berechnen Sie zwischen dem 1. beider Monate, das ist eine sehr fragwürdige Definition. Obwohl es manchmal richtig sein könnte.
Henk Holterman

@Henk: Ja, das ist natürlich nicht immer richtig, deshalb habe ich gesagt, dass ich es so machen würde, nicht wie es jemand machen sollte. Das OP hat nicht angegeben, wie das Ergebnis berechnet werden soll. @Adam: Wow, ich dachte noch einmal viel zu kompliziert ... das passiert mir allzu oft. Vielen Dank für den Kommentar, Sie haben offensichtlich Recht, Ihre Version ist viel besser. Ich werde dies von nun an verwenden.
Maximilian Mayerl

@Adam: warum gibst du das nicht als tatsächliche Antwort ein?! Dies ist die bisher kompakteste. Sehr glatt.
Dinah

@ Dinah: Ich wollte nicht annehmen, dass du das wirklich wolltest. Wenn dies der Fall ist, habe ich meine vorherige Antwort so bearbeitet, dass sie diesen Ansatz enthält.
Adam Robinson

3

Es gibt nicht viele klare Antworten darauf, weil Sie immer Dinge annehmen.

Diese Lösung berechnet zwischen zwei Daten die Monate zwischen der Annahme, dass Sie den Tag des Monats zum Vergleich speichern möchten (dh, der Tag des Monats wird bei der Berechnung berücksichtigt).

Wenn Sie beispielsweise ein Datum vom 30. Januar 2012 haben, ist der 29. Februar 2012 kein Monat, sondern der 1. März 2013.

Es wurde ziemlich gründlich getestet, wird es wahrscheinlich später bereinigen, wenn wir es verwenden, und nimmt zwei Daten anstelle einer Zeitspanne auf, was wahrscheinlich besser ist. Hoffe das hilft jemand anderem.

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
}

3

Die akzeptierte Antwort funktioniert perfekt, wenn Sie volle Monate möchten.

Ich brauchte Teilmonate. Dies ist die Lösung, die ich für Teilmonate gefunden habe:

    /// <summary>
    /// Calculate the difference in months.
    /// This will round up to count partial months.
    /// </summary>
    /// <param name="lValue"></param>
    /// <param name="rValue"></param>
    /// <returns></returns>
    public static int MonthDifference(DateTime lValue, DateTime rValue)
    {
        var yearDifferenceInMonths = (lValue.Year - rValue.Year) * 12;
        var monthDifference = lValue.Month - rValue.Month;

        return yearDifferenceInMonths + monthDifference + 
            (lValue.Day > rValue.Day
                ? 1 : 0); // If end day is greater than start day, add 1 to round up the partial month
    }

Ich brauchte auch einen Jahresunterschied mit dem gleichen Bedarf für Teiljahre. Hier ist die Lösung, die ich gefunden habe:

    /// <summary>
    /// Calculate the differences in years.
    /// This will round up to catch partial months.
    /// </summary>
    /// <param name="lValue"></param>
    /// <param name="rValue"></param>
    /// <returns></returns>
    public static int YearDifference(DateTime lValue, DateTime rValue)
    {
        return lValue.Year - rValue.Year +
               (lValue.Month > rValue.Month // Partial month, same year
                   ? 1
                   : ((lValue.Month = rValue.Month) 
                     && (lValue.Day > rValue.Day)) // Partial month, same year and month
                   ? 1 : 0);
    }

Sie hatten einen logischen Fehler in Ihrer YearDifferenceFunktion, als lValue.Month < rValue.Month- ich habe das jetzt behoben, möchten Sie vielleicht überprüfen ...
Stobor

2

Alte Frage, die ich kenne, aber vielleicht jemandem helfen könnte. Ich habe die oben von @Adam akzeptierte Antwort verwendet, dann aber überprüft, ob die Differenz 1 oder -1 beträgt, und dann überprüft, ob es sich um eine vollständige Differenz des Kalendermonats handelt. 21/07/55 und 20/08/55 wären also kein voller Monat, sondern 21/07/55 und 21/07/55.

/// <summary>
/// Amended date of birth cannot be greater than or equal to one month either side of original date of birth.
/// </summary>
/// <param name="dateOfBirth">Date of birth user could have amended.</param>
/// <param name="originalDateOfBirth">Original date of birth to compare against.</param>
/// <returns></returns>
public JsonResult ValidateDateOfBirth(string dateOfBirth, string originalDateOfBirth)
{
    DateTime dob, originalDob;
    bool isValid = false;

    if (DateTime.TryParse(dateOfBirth, out dob) && DateTime.TryParse(originalDateOfBirth, out originalDob))
    {
        int diff = ((dob.Month - originalDob.Month) + 12 * (dob.Year - originalDob.Year));

        switch (diff)
        {
            case 0:
                // We're on the same month, so ok.
                isValid = true;
                break;
            case -1:
                // The month is the previous month, so check if the date makes it a calendar month out.
                isValid = (dob.Day > originalDob.Day);
                break;
            case 1:
                // The month is the next month, so check if the date makes it a calendar month out.
                isValid = (dob.Day < originalDob.Day);
                break;
            default:
                // Either zero or greater than 1 month difference, so not ok.
                isValid = false;
                break;
        }
        if (!isValid)
            return Json("Date of Birth cannot be greater than one month either side of the date we hold.", JsonRequestBehavior.AllowGet);
    }
    else
    {
        return Json("Date of Birth is invalid.", JsonRequestBehavior.AllowGet);
    }
    return Json(true, JsonRequestBehavior.AllowGet);
}

2
case IntervalType.Month:
    returnValue = start.AddMonths(-end.Month).Month.ToString();
    break;
case IntervalType.Year:
    returnValue = (start.Year - end.Year).ToString();
    break;

2
Eine Beschreibung zum Code wäre auch für andere Leser von Vorteil.
Boeckm

Ja, bitte fügen Sie einen Kommentar hinzu.
Amar

1

Das Problem mit Monaten ist, dass es nicht wirklich eine einfache Maßnahme ist - sie sind nicht konstant groß. Sie müssten Ihre Regeln für das definieren, was Sie einschließen möchten, und von dort aus arbeiten. Zum Beispiel vom 1. Januar bis 1. Februar - Sie könnten argumentieren, dass dort 2 Monate beteiligt sind, oder Sie könnten sagen, dass dies ein Monat ist. Was ist dann mit "1 Jan 20:00" bis "1 Feb 00:00" - das ist nicht ganz ein ganzer Monat. Ist das 0? 1? Was ist mit umgekehrt (1. Januar 00:00 bis 1. Februar 20:00) ... 1? 2?

Definieren Sie zuerst die Regeln, dann müssen Sie sie selbst codieren, fürchte ich ...


1

Wenn Sie ein Ergebnis 1zwischen 28th Febund haben möchten 1st March:

DateTime date1, date2;
int monthSpan = (date2.Year - date1.Year) * 12 + date2.Month - date1.Month

Dies scheint dieselbe Logik zu sein, die von der SQL Server DateDiff-Funktion (Monat, ...) verwendet wird. Es hat auch den Vorteil, dass es äußerst präzise und leicht zu erklären und zu verstehen ist. Ich würde es wie folgt erklären ... wie viele Seiten im Kalender müssten Sie umblättern, um von einem Datum zum anderen zu gelangen?
JoelFan

1

Diese Bibliothek berechnet die Differenz der Monate unter Berücksichtigung aller Teile von DateTime:

// ----------------------------------------------------------------------
public void DateDiffSample()
{
  DateTime date1 = new DateTime( 2009, 11, 8, 7, 13, 59 );
  Console.WriteLine( "Date1: {0}", date1 );
  // > Date1: 08.11.2009 07:13:59
  DateTime date2 = new DateTime( 2011, 3, 20, 19, 55, 28 );
  Console.WriteLine( "Date2: {0}", date2 );
  // > Date2: 20.03.2011 19:55:28

  DateDiff dateDiff = new DateDiff( date1, date2 );

  // differences
  Console.WriteLine( "DateDiff.Years: {0}", dateDiff.Years );
  // > DateDiff.Years: 1
  Console.WriteLine( "DateDiff.Quarters: {0}", dateDiff.Quarters );
  // > DateDiff.Quarters: 5
  Console.WriteLine( "DateDiff.Months: {0}", dateDiff.Months );
  // > DateDiff.Months: 16
  Console.WriteLine( "DateDiff.Weeks: {0}", dateDiff.Weeks );
  // > DateDiff.Weeks: 70
  Console.WriteLine( "DateDiff.Days: {0}", dateDiff.Days );
  // > DateDiff.Days: 497
  Console.WriteLine( "DateDiff.Weekdays: {0}", dateDiff.Weekdays );
  // > DateDiff.Weekdays: 71
  Console.WriteLine( "DateDiff.Hours: {0}", dateDiff.Hours );
  // > DateDiff.Hours: 11940
  Console.WriteLine( "DateDiff.Minutes: {0}", dateDiff.Minutes );
  // > DateDiff.Minutes: 716441
  Console.WriteLine( "DateDiff.Seconds: {0}", dateDiff.Seconds );
  // > DateDiff.Seconds: 42986489

  // elapsed
  Console.WriteLine( "DateDiff.ElapsedYears: {0}", dateDiff.ElapsedYears );
  // > DateDiff.ElapsedYears: 1
  Console.WriteLine( "DateDiff.ElapsedMonths: {0}", dateDiff.ElapsedMonths );
  // > DateDiff.ElapsedMonths: 4
  Console.WriteLine( "DateDiff.ElapsedDays: {0}", dateDiff.ElapsedDays );
  // > DateDiff.ElapsedDays: 12
  Console.WriteLine( "DateDiff.ElapsedHours: {0}", dateDiff.ElapsedHours );
  // > DateDiff.ElapsedHours: 12
  Console.WriteLine( "DateDiff.ElapsedMinutes: {0}", dateDiff.ElapsedMinutes );
  // > DateDiff.ElapsedMinutes: 41
  Console.WriteLine( "DateDiff.ElapsedSeconds: {0}", dateDiff.ElapsedSeconds );
  // > DateDiff.ElapsedSeconds: 29
} // DateDiffSample

1

Im Folgenden finden Sie die genaueste Methode, da die Definition von "1 Monat" sich je nach Monat ändert und keine der anderen Antworten dies berücksichtigt! Wenn Sie weitere Informationen zu dem Problem wünschen, das nicht in das Framework integriert ist, können Sie diesen Beitrag lesen: Ein reales Zeitspannenobjekt mit .Jahren und .Monaten (das Lesen dieses Beitrags ist jedoch nicht erforderlich, um die folgende Funktion zu verstehen und zu verwenden. Es funktioniert zu 100%, ohne die inhärenten Ungenauigkeiten der Annäherung, die andere gerne verwenden - und Sie können die .ReverseIt-Funktion durch die integrierte .Reverse-Funktion ersetzen, die Sie möglicherweise in Ihrem Framework haben (der Vollständigkeit halber nur hier).

Bitte beachten Sie, dass Sie eine beliebige Anzahl von Datums- / Zeitgenauigkeiten, Sekunden und Minuten oder Sekunden, Minuten und Tage bis zu Jahren (die 6 Teile / Segmente enthalten würden) erhalten können. Wenn Sie die Top 2 angeben und diese älter als ein Jahr sind, wird "vor 1 Jahr und 3 Monaten" zurückgegeben und der Rest wird nicht zurückgegeben, da Sie zwei Segmente angefordert haben. Wenn es nur ein paar Stunden alt ist, wird es nur "vor 2 Stunden und 1 Minute" zurückgegeben. Natürlich gelten dieselben Regeln, wenn Sie 1, 2, 3, 4, 5 oder 6 Segmets angeben (maximal 6, da Sekunden, Minuten, Stunden, Tage, Monate, Jahre nur 6 Typen ergeben). Es werden auch Grammatikprobleme wie "Minuten" und "Minute" korrigiert, je nachdem, ob es 1 Minute oder mehr ist, für alle Typen gleich, und die generierte "Zeichenfolge" ist immer grammatikalisch korrekt.

Hier einige Beispiele zur Verwendung: bAllowSegments gibt an, wie viele Segmente angezeigt werden sollen ... dh: Wenn 3, wäre die Rückgabezeichenfolge (als Beispiel) ... "3 years, 2 months and 13 days"(ohne Stunden, Minuten und Sekunden als Top-3-Zeit Kategorien werden zurückgegeben), wenn das Datum jedoch ein neueres Datum war, z. B. vor einigen Tagen, wird "4 days, 1 hour and 13 minutes ago"stattdessen die Angabe derselben Segmente (3) zurückgegeben , sodass alles berücksichtigt wird!

Wenn bAllowSegments 2 ist, wird es zurückgegeben "3 years and 2 months"und wenn 6 (Maximalwert) zurückgegeben wird. Beachten Sie "3 years, 2 months, 13 days, 13 hours, 29 minutes and 9 seconds"jedoch, dass NEVER RETURNdies in "0 years, 0 months, 0 days, 3 hours, 2 minutes and 13 seconds ago"etwa so ist, da es keine Datumsdaten in den oberen 3 Segmenten enthält und diese ignoriert, selbst wenn Sie 6 Segmente angeben Also mach dir keine Sorgen :). Wenn sich ein Segment mit 0 darin befindet, wird dies natürlich beim Bilden des Strings berücksichtigt und als "3 days and 4 seconds ago"Teil "0 Stunden" angezeigt und ignoriert! Viel Spaß und bitte kommentieren Sie, wenn Sie möchten.

 Public Function RealTimeUntilNow(ByVal dt As DateTime, Optional ByVal bAllowSegments As Byte = 2) As String
  ' bAllowSegments identifies how many segments to show... ie: if 3, then return string would be (as an example)...
  ' "3 years, 2 months and 13 days" the top 3 time categories are returned, if bAllowSegments is 2 it would return
  ' "3 years and 2 months" and if 6 (maximum value) would return "3 years, 2 months, 13 days, 13 hours, 29 minutes and 9 seconds"
  Dim rYears, rMonths, rDays, rHours, rMinutes, rSeconds As Int16
  Dim dtNow = DateTime.Now
  Dim daysInBaseMonth = Date.DaysInMonth(dt.Year, dt.Month)

  rYears = dtNow.Year - dt.Year
  rMonths = dtNow.Month - dt.Month
  If rMonths < 0 Then rMonths += 12 : rYears -= 1 ' add 1 year to months, and remove 1 year from years.
  rDays = dtNow.Day - dt.Day
  If rDays < 0 Then rDays += daysInBaseMonth : rMonths -= 1
  rHours = dtNow.Hour - dt.Hour
  If rHours < 0 Then rHours += 24 : rDays -= 1
  rMinutes = dtNow.Minute - dt.Minute
  If rMinutes < 0 Then rMinutes += 60 : rHours -= 1
  rSeconds = dtNow.Second - dt.Second
  If rSeconds < 0 Then rSeconds += 60 : rMinutes -= 1

  ' this is the display functionality
  Dim sb As StringBuilder = New StringBuilder()
  Dim iSegmentsAdded As Int16 = 0

  If rYears > 0 Then sb.Append(rYears) : sb.Append(" year" & If(rYears <> 1, "s", "") & ", ") : iSegmentsAdded += 1
  If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn

  If rMonths > 0 Then sb.AppendFormat(rMonths) : sb.Append(" month" & If(rMonths <> 1, "s", "") & ", ") : iSegmentsAdded += 1
  If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn

  If rDays > 0 Then sb.Append(rDays) : sb.Append(" day" & If(rDays <> 1, "s", "") & ", ") : iSegmentsAdded += 1
  If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn

  If rHours > 0 Then sb.Append(rHours) : sb.Append(" hour" & If(rHours <> 1, "s", "") & ", ") : iSegmentsAdded += 1
  If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn

  If rMinutes > 0 Then sb.Append(rMinutes) : sb.Append(" minute" & If(rMinutes <> 1, "s", "") & ", ") : iSegmentsAdded += 1
  If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn

  If rSeconds > 0 Then sb.Append(rSeconds) : sb.Append(" second" & If(rSeconds <> 1, "s", "") & "") : iSegmentsAdded += 1

parseAndReturn:

  ' if the string is entirely empty, that means it was just posted so its less than a second ago, and an empty string getting passed will cause an error
  ' so we construct our own meaningful string which will still fit into the "Posted * ago " syntax...

  If sb.ToString = "" Then sb.Append("less than 1 second")

  Return ReplaceLast(sb.ToString.TrimEnd(" ", ",").ToString, ",", " and")

 End Function

Natürlich benötigen Sie eine "ReplaceLast" -Funktion, die eine Quellzeichenfolge und ein Argument verwendet, das angibt, was ersetzt werden muss, und ein weiteres Argument, das angibt, durch was ersetzt werden soll, und das nur das letzte Vorkommen dieser Zeichenfolge ersetzt ... Ich habe meine aufgenommen, wenn Sie keine haben oder sie nicht implementieren möchten. Hier funktioniert sie also "wie sie ist", ohne dass Änderungen erforderlich sind. Ich weiß, dass die Umkehrfunktion nicht mehr benötigt wird (existiert in .net), aber die Funktionen ReplaceLast und ReverseIt wurden aus den Tagen vor.net übernommen. Bitte entschuldigen Sie, wie alt sie aussehen könnten (funktioniert immer noch zu 100%, obwohl sie verwendet wurden) em seit über zehn Jahren, kann garantieren, dass sie fehlerfrei sind) ... :). Prost.

<Extension()> _ 
Public Function ReplaceLast(ByVal sReplacable As String, ByVal sReplaceWhat As String, ByVal sReplaceWith As String) As String 
    ' let empty string arguments run, incase we dont know if we are sending and empty string or not. 
    sReplacable = sReplacable.ReverseIt 
    sReplacable = Replace(sReplacable, sReplaceWhat.ReverseIt, sReplaceWith.ReverseIt, , 1) ' only does first item on reversed version! 
    Return sReplacable.ReverseIt.ToString 
End Function 

<Extension()> _ 
Public Function ReverseIt(ByVal strS As String, Optional ByVal n As Integer = -1) As String 
    Dim strTempX As String = "", intI As Integer 

    If n > strS.Length Or n = -1 Then n = strS.Length 

    For intI = n To 1 Step -1 
        strTempX = strTempX + Mid(strS, intI, 1) 
    Next intI 

    ReverseIt = strTempX + Right(strS, Len(strS) - n) 

End Function 

0

Wenn Sie die genaue Zahl wollen, können Sie nicht nur aus der Zeitspanne, da Sie wissen müssen, welche Monate Sie zu tun haben und ob Sie es mit einem Schaltjahr zu tun haben, wie Sie sagten.

Geben Sie entweder eine ungefähre Zahl an oder spielen Sie mit den ursprünglichen DateTimes



0

In idiomatic-c # gibt es keine eingebaute Möglichkeit, dies genau zu tun. Es gibt einige Problemumgehungen, wie z. B. dieses CodeProject-Beispiel , das von Personen codiert wurde.


0

Wenn Sie mit Monaten und Jahren zu tun haben, brauchen Sie etwas, das weiß, wie viele Tage jeder Monat hat und welche Jahre Schaltjahre sind.

Geben Sie den Gregorianischen Kalender (und andere kulturspezifische Kalenderimplementierungen ) ein.

Der Kalender bietet zwar keine Methoden zur direkten Berechnung der Differenz zwischen zwei Zeitpunkten, verfügt jedoch über Methoden wie

DateTime AddWeeks(DateTime time, int weeks)
DateTime AddMonths(DateTime time, int months)
DateTime AddYears(DateTime time, int years)

0
DateTime start = new DateTime(2003, 12, 25);
DateTime end = new DateTime(2009, 10, 6);
int compMonth = (end.Month + end.Year * 12) - (start.Month + start.Year * 12);
double daysInEndMonth = (end - end.AddMonths(1)).Days;
double months = compMonth + (start.Day - end.Day) / daysInEndMonth;

0

Die Methode gibt eine Liste zurück, die 3 Elemente enthält: Das erste ist das Jahr, das zweite der Monat und das Endelement der Tag:

public static List<int> GetDurationInEnglish(DateTime from, DateTime to)
    {
        try
        {
            if (from > to)
                return null;

            var fY = from.Year;
            var fM = from.Month;
            var fD = DateTime.DaysInMonth(fY, fM);

            var tY = to.Year;
            var tM = to.Month;
            var tD = DateTime.DaysInMonth(tY, tM);

            int dY = 0;
            int dM = 0;
            int dD = 0;

            if (fD > tD)
            {
                tM--;

                if (tM <= 0)
                {
                    tY--;
                    tM = 12;
                    tD += DateTime.DaysInMonth(tY, tM);
                }
                else
                {
                    tD += DateTime.DaysInMonth(tY, tM);
                }
            }
            dD = tD - fD;

            if (fM > tM)
            {
                tY--;

                tM += 12;
            }
            dM = tM - fM;

            dY = tY - fY;

            return new List<int>() { dY, dM, dD };
        }
        catch (Exception exception)
        {
            //todo: log exception with parameters in db

            return null;
        }
    }

0

Hier ist mein Beitrag, um einen Unterschied in Monaten zu erzielen, den ich für richtig befunden habe:

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

Verwendung:

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

Sie können eine andere Methode namens DiffYears erstellen und genau dieselbe Logik wie oben und AddYears anstelle von AddMonths in der while-Schleife anwenden.


0

Viel zu spät zum Spiel, aber ich kann mir vorstellen, dass dies für jemanden hilfreich sein kann. Die Mehrheit der Menschen neigt dazu, Monat für Monat nach Datum zu messen, mit Ausnahme der Tatsache, dass Monate in verschiedenen Variationen vorliegen. Mit diesem Gedankenrahmen habe ich einen Einzeiler erstellt, der die Daten für uns vergleicht. Verwenden Sie den folgenden Prozess.

  1. Jede Anzahl von Jahren über 1 beim Vergleich des Jahres wird mit 12 multipliziert. Es gibt keinen Fall, in dem dies weniger als 1 volles Jahr entsprechen kann.
  2. Wenn das Endjahr größer ist, müssen wir bewerten, ob der aktuelle Tag größer oder gleich dem vorherigen Tag 2A ist. Wenn der Endtag größer oder gleich ist, nehmen wir den aktuellen Monat und addieren dann 12 Monate, subtrahieren den Monat des Startmonats 2B. Wenn der Endtag kleiner als der Starttag ist, führen wir dasselbe wie oben aus, außer dass wir dem Startmonat vor dem Subtrahieren 1 hinzufügen
  3. Wenn das Endjahr nicht größer ist, führen wir die gleiche Leistung wie 2A / 2B durch, jedoch ohne die 12 Monate hinzuzufügen, da wir nicht um das Jahr herum bewerten müssen.

        DateTime date = new DateTime(2003, 11, 25);
        DateTime today = new DateTime(2004, 12, 26);
        var time = (today.Year - date.Year > 1 ? (today.Year - date.Year - 1) * 12 : 0) +  (today.Year > date.Year ? (today.Day >= date.Day ? today.Month + 12 - date.Month : today.Month + 12 - (date.Month + 1)) : (today.Day >= date.Day ? today.Month - date.Month : today.Month - (date.Month + 1)));

Tod durch Ternär?
SpaceBison

0

Meine Einstellung zu dieser Antwort verwendet ebenfalls eine Erweiterungsmethode , die jedoch ein positives oder negatives Ergebnis liefern kann.

public static int MonthsBefore(this DateTime dt1, DateTime dt2)
{
    (DateTime early, DateTime late, bool dt2After) = dt2 > dt1 ? (dt1,dt2,true) : (dt2,dt1,false);
    DateTime tmp; // Save the result so we don't repeat work
    int months = 1;
    while ((tmp = early.AddMonths(1)) <= late)
    {
        early = tmp;
        months++;
    }
    return (months-1)*(dt2After ? 1 : -1);
}

Ein paar Tests:

// Just under 1 month's diff
Assert.AreEqual(0, new DateTime(2014, 1, 1).MonthsBefore(new DateTime(2014, 1, 31)));
// Just over 1 month's diff
Assert.AreEqual(1, new DateTime(2014, 1, 1).MonthsBefore(new DateTime(2014, 2, 2)));    
// Past date returns NEGATIVE
Assert.AreEqual(-6, new DateTime(2012, 1, 1).MonthsBefore(new DateTime(2011, 6, 10)));

0

Wenn Sie zwei der obigen Antworten kombinieren, ist eine andere Erweiterungsmethode:

public static int ElapsedMonths(this DateTime date1, DateTime date2)
{
    DateTime earlierDate = (date1 > date2) ? date2 : date1;
    DateTime laterDate = (date1 > date2) ? date1 : date2;
    var eMonths = (laterDate.Month - earlierDate.Month) + 12 * (laterDate.Year - earlierDate.Year) - 
                                            ((earlierDate.Day > laterDate.Day) ? 1 : 0);
    return eMonths;
}

Vielen Dank an @AdamRobinson und @MarkWhittaker


-1

Berechnen Sie die Anzahl der Monate zwischen 2 Daten:

$date1 = '2017-01-20';
$date2 = '2019-01-20';

$ts1 = strtotime($date1);
$ts2 = strtotime($date2);

$year1 = date('Y', $ts1);
$year2 = date('Y', $ts2);

$month1 = date('m', $ts1);
$month2 = date('m', $ts2);

echo $joining_months = (($year2 - $year1) * 12) + ($month2 - $month1);

1
Dies ist PHP, nicht C #.
Nach dem
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.