Différence de mois entre deux dates


334

Comment calculer la différence en mois entre deux dates en C #?

Y a-t-il l'équivalent de la DateDiff()méthode de VB en C #. J'ai besoin de trouver une différence de mois entre deux dates séparées par des années. La documentation dit que je peux utiliser TimeSpancomme:

TimeSpan ts = date1 - date2;

mais cela me donne des données en jours. Je ne veux pas diviser ce nombre par 30 car tous les mois ne sont pas 30 jours et comme les deux valeurs d'opérande sont assez éloignées l'une de l'autre, je crains que la division par 30 ne me donne une mauvaise valeur.

Aucune suggestion?


27
Définissez la "différence en mois", quelle est la différence en mois entre "1er mai 2010" et "16 juin 2010"? 1.5, 1 ou autre chose?
Cheng Chen

7
Ou, pour insister davantage sur ce point, quelle est la différence en mois entre le 31 décembre 2010 et le 1er janvier 2011? Selon la journée, cela pourrait être une différence de seulement 1 seconde; comptez-vous cela comme une différence d'un mois?
stakx - ne contribue plus le

Voici le code simple et court au cas où, vous ne pourriez toujours pas obtenir la réponse, voir ce POST stackoverflow.com/questions/8820603/…
wirol

11
Danny: 1 mois et 15 jours. stakx: 0 mois et 1 jour. Le but est d'obtenir la composante mois . Cela me semble assez évident et c'est une bonne question.
Kirk Woll, le

Réponses:


462

En supposant que le jour du mois n'est pas pertinent (c'est-à-dire que le diff entre 2011.1.1 et 2010.12.31 est 1), avec date1> date2 donnant une valeur positive et date2> date1 une valeur négative

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

Ou, en supposant que vous souhaitiez un nombre approximatif de «mois moyens» entre les deux dates, les éléments suivants devraient fonctionner pour toutes les différences de dates, sauf les énormes.

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

Notez que si vous utilisiez cette dernière solution, vos tests unitaires devraient indiquer la plage de dates la plus large avec laquelle votre application est conçue pour fonctionner et valider les résultats du calcul en conséquence.


Mise à jour (avec merci à Gary )

Si vous utilisez la méthode du «mois moyen», un nombre légèrement plus précis à utiliser pour le «nombre moyen de jours par an» est 365,2425 .


3
@Kurru - 365/12 n'est qu'une mesure approximative de la durée moyenne d'un mois en jours. C'est une mesure inexacte. Pour les petites plages de dates, cette inexactitude peut être tolérée, mais pour les très grandes plages de dates, cette inexactitude peut devenir importante.
Adam Ralph

21
Je pense qu'il est nécessaire de considérer la composante Day. Quelque chose comme ça (date1.Year - date2.Year) * 12 + date1.Month - date2.Month + (date1.Day >= date2.Day ? 0 : -1)
DrunkCoder

2
@DrunkCoder, cela dépend des exigences d'un système donné. Dans certains cas, votre solution peut en effet être le meilleur choix. Par exemple, il est important de considérer ce qui se passe lorsque deux dates s'étendent sur un mois de 31 jours, un mois de 30 jours, un février de 28 jours ou un février de 29 jours. Si les résultats de votre formule fournissent ce dont le système a besoin, c'est clairement le bon choix. Sinon, quelque chose d'autre est requis.
Adam Ralph

6
Pour appuyer ce qu'Adam a dit, j'ai passé des années à écrire du code pour les actuaires. Certains calculs ont été divisés par nombre de jours, arrondis à 30 pour obtenir un chiffre mensuel . Parfois, en comptant les mois, chaque date commence le premier du mois, comptez des mois entiers en conséquence . Il n'y a pas de meilleure méthode pour calculer les dates. À moins que vous ne soyez le client pour lequel vous écrivez du code, repoussez-le dans la chaîne et faites-le clarifier, éventuellement par votre comptable client.
Binary Worrier

1
365.2425 est un nombre de jours légèrement plus précis dans un calendrier grégorien, si c'est ce que vous utilisez. Cependant, selon DateTime.MaxValue (1er janvier 10000), cela ne représente qu'une différence d'environ 59 jours. De plus, la définition d'une année peut être très différente selon votre point de vue en.wikipedia.org/wiki/Year .
Gary

208

Voici une solution complète pour renvoyer un DateTimeSpan, similaire à un TimeSpan, sauf qu'il inclut tous les composants de date en plus des composants d'heure.

Usage:

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

Les sorties:

Années: 1
Mois: 5
Jours: 27
Heures: 1
Minutes: 36
Secondes: 50
Millisecondes: 0

Pour plus de commodité, j'ai regroupé la logique dans la DateTimeSpanstructure, mais vous pouvez déplacer la méthode CompareDatesoù bon vous semble. Notez également, peu importe la date qui précède l'autre.

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 merci. Mais pourquoi est-ce que DateTimeSpan renvoie des 34jours pour cette différence de date et d'heure, c'est en fait 35 timeanddate.com/date/…
Deeptechtons

@Deeptechtons, belle prise. Il y a quelques problèmes que vous avez portés à mon attention, à la fois en ce qui concerne la date de début est 31et la date "passe à travers" des mois avec moins de jours. J'ai inversé la logique (pour qu'elle passe du début à plus tard que l'inverse) et accumule maintenant les mois sans modifier la date actuelle (et donc passer par des mois intermédiaires avec moins de jours) Je ne sais toujours pas exactement quel est le résultat idéal devrait être lors de la comparaison 10/31/2012avec 11/30/2012. En ce moment, le résultat est le 1mois.
Kirk Woll

@KirkWoll merci pour la mise à jour, peut-être que j'ai eu quelques accrochages, permettez-moi de l'affirmer après quelques tests Bon travail :)
Deeptechtons

1
J'ai écrit une réponse stackoverflow.com/a/17537472/1737957 à une question similaire qui a testé les réponses proposées (et constaté que la plupart d'entre elles ne fonctionnent pas). Cette réponse est l'une des rares qui fonctionne (selon ma suite de tests). Lien vers github sur ma réponse.
jwg

@KirkWoll - Cette réponse ne semble pas fonctionner pour les cas limites où la date de début a une valeur de jour supérieure au mois de la date ou où la date source est un jour bissextile. Essayez 2020-02-29de 2021-06-29- il renvoie "1y 4m 1d", mais la valeur doit être "1y 4m 0d", non?
Énigmativité du

37

Vous pourriez faire

if ( date1.AddMonths(x) > date2 )

C'est tellement simple et fonctionne parfaitement pour moi. J'ai été agréablement surpris de voir que cela fonctionne comme prévu lors du calcul d'une date entre la fin d'un mois et une date à la fin du mois suivant qui a moins de jours. Par exemple .. 1-31-2018 + 1 mois = 28 février 218
lucky.expert

C'est l'une des meilleures solutions.
barnacle.m

Solution vraiment simple et efficace! La meilleure réponse proposée.
Cedric Arnould

2
Et si date1 = 2018-10-28 et date2 = 2018-12-21? La réponse sera 2. tandis que la bonne réponse devrait être 3. En raison de la plage de dates est de 3 mois. si nous ne comptons que des mois en ignorant les jours. Cette réponse n'est donc PAS correcte.
Tommix

Plus logique serait: if ( date1.AddMonths(x).Month == date2.Month )alors vous utilisez simplement x + 1 comme nombre de mois
Tommix

34

Si vous voulez le nombre exact de mois complets, toujours positif (2000-01-15, 2000-02-14 renvoie 0), considérant qu'un mois complet est lorsque vous atteignez le même jour le mois suivant (quelque chose comme le calcul de l'âge)

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

Modifier la raison: l'ancien code n'était pas correct dans certains cas comme:

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

Afin d'éviter toute confusion pour les autres, je pense que cette solution n'est pas correcte. Utilisation du cas de test: new { From = new DateTime(2015, 12, 31), To = new DateTime(2015, 6, 30), Result = 6 } le test échouera car le résultat est 5.
Cristian Badila

Ajout d'un aperçu rapide avec le correctif que je propose ici
Cristian Badila

Je ne suis pas sûr de l'avoir, ma fonction renvoie 6 comme il se doit: dotnetfiddle.net/MRZNnC
Guillaume86

J'ai copié le cas de test ici à la main et il y a une erreur. La spécification ne devrait être: new { From = new DateTime(2015, 12, 31), To = new DateTime(2016, 06, 30), Result = 6 }. Le "bug" réside dans le to.Day < from.Daycode qui ne tient pas compte du fait que les mois peuvent se terminer par un "jour du mois" différent. Dans ce cas du 31 décembre 2015 au 30 juin 2016, 6 mois complets se seront écoulés (depuis juin a 30 jours) mais votre code en retournerait 5.
Cristian Badila

3
C'est un comportement attendu à mon avis, eh bien ou c'est le comportement auquel je m'attends au moins. J'ai précisé qu'un mois complet, c'est quand vous atteignez le même jour (ou le mois suivant comme dans ce cas).
Guillaume86

22

J'ai vérifié l'utilisation de cette méthode dans VB.NET via MSDN et il semble qu'elle ait beaucoup d'utilisations. Il n'y a pas une telle méthode intégrée en C #. (Même ce n'est pas une bonne idée), vous pouvez appeler les VB en C #.

  1. Ajoutez Microsoft.VisualBasic.dllà votre projet comme référence
  2. utiliser Microsoft.VisualBasic.DateAndTime.DateDiff dans votre code

7
Pourquoi pensez-vous que ce n'est pas une bonne idée? Intuitivement, je suppose que la bibliothèque est «juste une autre bibliothèque .NET» pour le runtime. Remarque, je joue l'avocat du diable ici, je serais également réticent à le faire car cela `` se sent mal '' (sorte de tricherie) mais je me demande s'il y a une raison technique convaincante de ne pas le faire.
Adam Ralph

3
@AdamRalph: Aucune raison de ne pas le faire. Ces bibliothèques sont implémentées dans du code géré à 100%, c'est donc la même chose que tout le reste. La seule différence concevable est que le Microsoft.VisualBasic.dllmodule doit être chargé, mais le temps qu'il faut pour le faire est négligeable. Il n'y a aucune raison de vous empêcher d'utiliser des fonctionnalités testées et utiles uniquement parce que vous avez choisi d'écrire votre programme en C #. (Cela vaut aussi pour des choses comme ça My.Application.SplashScreen.)
Cody Gray

3
Souhaitez-vous changer d'avis si vous saviez qu'il a été écrit en C #? C'était. Par la même logique, l'utilisation de System.Data et PresentationFramework triche aussi, des parties substantielles de celui-ci écrites en C ++ / CLI.
Hans Passant

3
@AdamRalph: Des exemples particuliers de ces "bagages étranges" qui vous viennent à l'esprit? Ou dites-vous cela purement hypothétique? Et oui, cela pourrait déranger l'esprit de certains de vos amis C # qui ont écrit une quantité épique de code pour faire quelque chose que vous pouvez faire en une seule ligne avec la bonne usingdéclaration, mais je doute qu'il y ait de graves dommages.
Cody Gray

1
@Cody Gray: d'accord, l'exemple est trivial comme vous l'illustrez. C'est le `` bruit '' de code supplémentaire introduit en appelant une méthode si inhabituelle (à partir d'un POV C #) que je voudrais éviter. Dans une équipe bien organisée, de telles choses seraient de toute façon détectées lors de la révision du code et pourraient être facilement évitées. BTW - Je n'essaie pas d'attaquer VB6 / VB.NET. J'ai décrit ces méthodes comme «étranges» uniquement parce que, à partir d'un POV .NET, il n'y a aucune raison DateAndTime.Year()d'exister, étant donné qu'elles DateTimeont une Yearpropriété. Il n'existe que pour que VB.NET ressemble davantage à VB6. En tant qu'ancien programmeur VB6, je peux l'apprécier ;-)
Adam Ralph

10

Pour obtenir la différence en mois (début et fin inclus), indépendamment des dates:

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
Imaginez startet endsont identiques. Ensuite, vous obtenez un résultat de 1. Comment est-ce vrai? Pourquoi ajoutez-vous 1 au résultat? Qui vote pour cette réponse: - /?
paul

Pour des dates identiques, il donnera un résultat égal à 1. Fondamentalement, il comptera tous les mois, y compris les mois de début et de fin.
Chirag

3
ne ressemble pas à la différence entre deux éléments pour moi. Quelle est la difference entre 2 et 2? Est-ce vraiment 1? Je dirais que la différence est de 0.
paul

8

Utilisez Noda Time :

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

(exemple de source)


7

J'avais juste besoin de quelque chose de simple pour répondre, par exemple, à des dates d'emploi où seul le mois / l'année est entré, donc je voulais des années et des mois distincts travaillés.

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

Vous pouvez utiliser la classe DateDiff de la bibliothèque de périodes pour .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

Voici ma contribution pour obtenir la différence en mois que j'ai trouvée exacte:

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

Usage:

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

Vous pouvez créer une autre méthode appelée DiffYears et appliquer exactement la même logique que ci-dessus et AddYears au lieu d'AddMonths dans la boucle while.


2

Cela a fonctionné pour ce dont j'avais besoin. Le jour du mois n'a pas d'importance dans mon cas car il se trouve que c'est toujours le dernier jour du mois.

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

La façon la plus précise est celle qui renvoie la différence en mois par fraction:

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

Voici une solution simple qui fonctionne au moins pour moi. Ce n'est probablement pas le plus rapide car il utilise la fonctionnalité AddMonth de DateTime dans une boucle:

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

Ceci est de ma propre bibliothèque, retournera la différence de mois entre deux dates.

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
Est-ce que ça marche? Je continue d'avoir 11 sur papier pour Jan-31-2014etDec-31-2013
Dave Cousineau

1

Vous pouvez avoir une fonction quelque chose comme ça.

Par exemple, du 2012/12/27 au 2012/12/29 devient 3 jours. De même, du 2012/12/15 au 2013/01/15 devient 2 mois, car jusqu'au 2013/01/14 c'est 1 mois. à partir du 15, c'est le 2e mois qui a commencé.

Vous pouvez supprimer le "=" dans la seconde condition if, si vous ne souhaitez pas inclure les deux jours dans le calcul. soit du 2012/12/15 au 2013/01/15 soit 1 mois.

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

vous pouvez utiliser l'extension suivante: Code

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
}

La mise en oeuvre !

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

Voici une solution beaucoup plus concise utilisant VB.Net DateDiff pour l'année, le mois et le jour uniquement. Vous pouvez également charger la bibliothèque DateDiff en C #.

date1 doit être <= 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

C'est en réponse à la réponse de Kirk Woll. Je n'ai pas encore assez de points de réputation pour répondre à un commentaire ...

J'aimais la solution de Kirk et j'allais l'arracher sans vergogne et l'utiliser dans mon code, mais quand je l'ai regardée, j'ai réalisé que c'était beaucoup trop compliqué. Commutation et boucle inutiles, et un constructeur public inutile à utiliser.

Voici ma réécriture:

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

Usage1, à peu près la même:

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

Usage2, similaire:

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

Dans mon cas, il est nécessaire de calculer le mois complet de la date de début à la veille de ce jour du mois suivant ou du début à la fin du mois.


Ex: du 1/1/2018 au 31/1/2018 est un mois complet
Ex2: du 01/05/2018 au 4/2/2018 est un mois complet

sur cette base, voici ma solution:

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

Usage:

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

Remarque: dans mon cas, il était nécessaire de calculer les jours restants après les mois complets, donc si ce n'est pas votre cas, vous pouvez ignorer le résultat des jours ou même changer la méthode de retour de tuple à entier.


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

Cette solution est pour le calcul de location / abonnement, où la différence ne signifie pas être une soustraction, elle est censée être la durée dans ces deux dates.


1

Il y a 3 cas: la même année, l'année précédente et les autres années.

Si le jour du mois n'a pas d'importance ...

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

J'ai écrit une fonction pour accomplir cela, car les autres méthodes ne fonctionnaient pas pour moi.

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

Veuillez répondre en anglais (vs n'importe quelle langue inventée ...)
kleopatra

Pourquoi ne pas simplement lancer startDate.AddMonths (monthCount) .ToShortDateString ()? Cela ne répond pas de toute façon à la question d'origine!
TabbyCool

oh, désolé @TabbyCool, ce code fonctionne bien dans mon programme! la règle des programmeurs dit: d'abord le code fonctionne, puis l'optimisation! tanx pour votre commentaire :)
reza akhlaghi

1

Ma compréhension de la différence totale des mois entre 2 dates a une partie intégrale et une partie fractionnaire (la date compte).

La partie intégrante est la différence des mois complets.

La partie fractionnaire, pour moi, est la différence du% de la journée (aux jours entiers du mois) entre les mois de début et de fin.

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

Avec cette extension, voici les résultats:

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

Il n'y a pas beaucoup de réponses claires à ce sujet parce que vous supposez toujours des choses.

Cette solution calcule entre deux dates les mois entre l'hypothèse que vous souhaitez enregistrer le jour du mois pour comparaison, (ce qui signifie que le jour du mois est pris en compte dans le calcul)

Exemple, si vous avez une date du 30 janvier 2012, le 29 février 2012 ne sera pas un mois mais le 1er mars 2013 le sera.

Il a été testé assez soigneusement, le nettoiera probablement plus tard au fur et à mesure que nous l'utilisons, mais ici:

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

Sur la base de l'excellent travail DateTimeSpan effectué ci-dessus, j'ai normalisé un peu le code; cela semble assez bien fonctionner:

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

Lorsque l'on compare avec CompareDates(x, y)x={01/02/2019 00:00:00}et y={01/05/2020 00:00:00}puis Monthsme donne2
Bassie

1

Cette fonction statique simple calcule la fraction de mois entre deux Datetimes, par exemple

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

La fonction suppose que la première date est inférieure à la deuxième date. Pour gérer les intervalles de temps négatifs, on peut facilement modifier la fonction en introduisant un signe et un échange de variable au début.

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

Pouvoir calculer la différence entre 2 dates en mois est une chose parfaitement logique à faire et est nécessaire dans de nombreuses applications commerciales. Les nombreux codeurs ici qui ont fourni des commentaires tels que - quelle est la différence de mois entre "1er mai 2010" et "16 juin 2010, quelle est la différence de mois entre le 31 décembre 2010 et le 1er janvier 2011? - n'ont pas compris les bases des applications métier.

Voici la réponse aux 2 commentaires ci-dessus - Le nombre de mois entre le 1er mai 2010 et le 16 juin 2010 est de 1 mois, le nombre de mois entre le 31 décembre 2010 et le 1er janvier 2011 est de 0. Il serait très stupide de les calculer comme 1,5 mois et 1 seconde, comme les codeurs ci-dessus l'ont suggéré.

Les gens qui ont travaillé sur les cartes de crédit, le traitement des hypothèques, le traitement des impôts, le traitement des loyers, le calcul des intérêts mensuels et une grande variété d'autres solutions commerciales seraient d'accord.

Le problème est qu'une telle fonction n'est pas incluse dans C # ou VB.NET d'ailleurs. Datediff ne prend en compte que les années ou la composante mois, donc est en fait inutile.

Voici quelques exemples concrets où vous devez calculer correctement les mois:

Vous avez vécu dans une location à court terme du 18 février au 23 août. Combien de mois y êtes-vous resté? La réponse est simple - 6 mois

Vous avez un compte bancaire où les intérêts sont calculés et payés à la fin de chaque mois. Vous déposez de l'argent le 10 juin et le retirez le 29 octobre (même année). Pour combien de mois recevez-vous de l'intérêt? Réponse très simple - 4 mois (encore une fois, les jours supplémentaires n'ont pas d'importance)

Dans les applications commerciales, la plupart du temps, lorsque vous devez calculer des mois, c'est parce que vous devez connaître des mois «complets» en fonction de la façon dont les humains calculent le temps; ne repose pas sur des pensées abstraites / non pertinentes.


5
C'est l'une des raisons pour lesquelles la comptabilité n'est pas mathématique. En comptabilité, le résultat dépend de la façon dont vous le calculez. Je connais vos points et je connais le "point de vue commercial commun" à ce sujet, mais cette explication est clairement erronée. Entre le 2012.11.30 et le 2012.12.01, il y a soit 0, soit 1/30, soit 1/31, soit 1 ou 2 mois, selon ce que vous avez demandé . Les dates étaient-elles exclusives ou inclusives? Avez-vous demandé le nombre de mois passés, touchés ou passés? Vouliez-vous arrondir, arrondir ou exact?
quetzalcoatl

3
Maintenant, expliquez-le à un homme d'affaires ou à un comptable et ils vous donneront un regard perplexe. C'est toujours "si évident pour eux qu'ils signifient bien sûr X et Y et Z, comment pourriez-vous penser différemment?" Maintenant, réunissez plusieurs hommes d'affaires et essayez de les amener à s'entendre sur le sujet. Les comptables sont plus susceptibles d'être d'accord, car à un moment donné, ils utiliseront les mathématiques pour vérifier avec quelles options ils pourraient accidentellement résumer deux fois la même période, etc. règles commerciales supplémentaires comme ignorer les jours supplémentaires.
quetzalcoatl

2
-1 Vous supposez que tous les logiciels sont une "application métier". Le but du code en question n'est pas mentionné. Vous supposez également que toutes les "applications métier" ont les mêmes règles, ce qui n'est certainement pas vrai.
Jesse Webb

0

Structure Kirks étendue avec ToString (format) et durée (long 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;
    }
}

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));
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.