web-dev-qa-db-fra.com

DateTime.AddMonths ajoutant seulement mois et non jours

Disons que j'ai le 28 février 2010 et que j'ajoute un mois à cette date avec AddMonths(1)...
la date résultante est 28 mars, mais pas 31 mars, ce que je veux.
Y a-t-il un moyen de modifier un peu cela pour que cela fonctionne sans ajouter de code personnalisé?

Edit: Je n’ai pas besoin du dernier jour du mois, c’est un besoin d’ajouter un mois, mais lorsque c’est le dernier jour du mois, je dois trouver le dernier jour du mois suivant.

18
grady

Je ne sais pas ce que vous voulez réaliser, mais vous pouvez ajouter un jour, ajouter un mois et soustraire un jour.

DateTime nextMonth = date.AddDays(1).AddMonths(1).AddDays(-1);

MODIFIER:

Comme l’a souligné l’un des commentateurs, cela donne parfois un résultat erroné. Après avoir lu votre question mise à jour, je pense que le moyen le plus simple de calculer la date que vous voulez est:

public static DateTime NextMonth(this DateTime date)
{
   if (date.Day != DateTime.DaysInMonth(date.Year, date.Month))
      return date.AddMonths(1);
   else 
      return date.AddDays(1).AddMonths(1).AddDays(-1);
}

Cette méthode d'extension renvoie la date du mois prochain. Lorsque la date du jour est le dernier jour du mois, le dernier jour du mois suivant est renvoyé.

35
Philippe Leybaert

Si vous voulez dire que la date résultante doit être à la même distance du fin du mois, alors vous utilisez du code personnalisé - quelque chose du genre (non entièrement testé, en particulier les 28/30/31 mois):

class Program
{
    static void Main()
    {
        var when = DateTime.Today;
        DateTime fromEndOfNextMonth = when.AddMonthsRelativeToEndOfMonth(1);
    }

}
public static class DateTimeExtensions
{
    public static DateTime AddMonthsRelativeToEndOfMonth(
               this DateTime when, int months)
    {
        if (months == 0) return when;
        DateTime startOfNextMonth = when;
        int month = when.Month;
        while (startOfNextMonth.Month == month)
        {
            startOfNextMonth = startOfNextMonth.AddDays(1);
        }
        TimeSpan delta = startOfNextMonth - when;
        return startOfNextMonth.AddMonths(1) - delta;
    }

}
7
Marc Gravell

Que diriez-vous comme ça? Il résout le problème du 30 janvier qui se produirait avec la meilleure réponse actuelle.

        public static DateTime AddJustMonths(this DateTime @this, int months)
        {
            var firstDayOfTargetMonth = new DateTime(@this.Year, @this.Month, 1).AddMonths(months);
            var lastDayofTargetMonth = DateTime.DaysInMonth(firstDayOfTargetMonth.Year, firstDayOfTargetMonth.Month);

            var targetDay = @this.Day > lastDayofTargetMonth ? lastDayofTargetMonth : @this.Day;

            return new DateTime(firstDayOfTargetMonth.Year, firstDayOfTargetMonth.Month, targetDay);
        }
4
rashleighp
public static DateTime NextMonth(DateTime date)
{
    DateTime nextMonth = date.AddMonths(1);

    if (date.Day != DateTime.DaysInMonth(date.Year, date.Month)) //is last day in month
    {
        //any other day then last day
        return nextMonth;
    }
    else
    {
       //last day in the month will produce the last day in the next month
       return date.AddDays(DateTime.DaysInMonth(nextMonth.Year, nextMonth.Month));
    }
}

Et généralisé pour plusieurs mois:

public static DateTime AddMonthToEndOfMonth(DateTime date, int numberOfMonths)
{
    DateTime nextMonth = date.AddMonths(numberOfMonths);

    if (date.Day != DateTime.DaysInMonth(date.Year, date.Month)) //is last day in month
    {
        //any other day then last day
        return nextMonth;
    }
    else
    {
        //if date was end of month, add remaining days
        int addDays = DateTime.DaysInMonth(nextMonth.Year, nextMonth.Month) - nextMonth.Day;
        return nextMonth.AddDays(addDays);
    }
}

Le code est testé par rapport aux éditions de février, aux années bissextiles et aux transitions du Nouvel An. Tous les tests ont réussi.

 enter image description here

[TestMethod]
public void AddMonthTest_January()
{
    for (int i = 1; i <= 28; i++)
    {
        Assert.AreEqual(new DateTime(2015, 2, i), NextMonth(new DateTime(2015, 1, i)));
    }
    Assert.AreEqual(new DateTime(2015, 2, 28), NextMonth(new DateTime(2015, 1, 29)));
    Assert.AreEqual(new DateTime(2015, 2, 28), NextMonth(new DateTime(2015, 1, 30)));
    Assert.AreEqual(new DateTime(2015, 2, 28), NextMonth(new DateTime(2015, 1, 31)));
}

[TestMethod]
public void AddMonthTest_February()
{
    Assert.AreEqual(new DateTime(2015, 3, 31), NextMonth(new DateTime(2015, 2, 28)));

    for (int i = 1; i <= 27; i++)
    {
        Assert.AreEqual(new DateTime(2015, 3, i), NextMonth(new DateTime(2015, 2, i)));
    }            
}

[TestMethod]
public void AddMonthTest_March()
{
    Assert.AreEqual(new DateTime(2015, 4, 30), NextMonth(new DateTime(2015, 3, 31)));

    for (int i = 1; i <= 30; i++)
    {
        Assert.AreEqual(new DateTime(2015, 4, i), NextMonth(new DateTime(2015, 3, i)));
    }
}

[TestMethod]
public void AddMonthTest_December()
{            
    for (int i = 1; i <= 31; i++)
    {
        Assert.AreEqual(new DateTime(2016, 1, i), NextMonth(new DateTime(2015, 12, i)));
    }
}

[TestMethod]
public void AddMonthTest_January_LeapYear()
{
    for (int i = 1; i <= 29; i++)
    {
        Assert.AreEqual(new DateTime(2016, 2, i), NextMonth(new DateTime(2016, 1, i)));
    }            
    Assert.AreEqual(new DateTime(2016, 2, 29), NextMonth(new DateTime(2016, 1, 30)));
    Assert.AreEqual(new DateTime(2016, 2, 29), NextMonth(new DateTime(2016, 1, 31)));
}

[TestMethod]
public void AddMonthTest_February_LeapYear()
{
    Assert.AreEqual(new DateTime(2016, 3, 31), NextMonth(new DateTime(2016, 2, 29)));

    for (int i = 1; i <= 28; i++)
    {
        Assert.AreEqual(new DateTime(2016, 3, i), NextMonth(new DateTime(2016, 2, i)));
    }
}

[TestMethod]
public void AddHalfYearTest_January_LeapYear()
{        
    for (int i = 1; i <= 31; i++)
    {
        Assert.AreEqual(new DateTime(2016, 7, i), new DateTime(2016, 1, i).AddMonthToEndOfMonth(6));
    }
}

[TestMethod]
public void AddHalfYearTest_February_LeapYear()
{
    Assert.AreEqual(new DateTime(2016, 8, 31), new DateTime(2016, 2, 29).AddMonthToEndOfMonth(6));

    for (int i = 1; i <= 28; i++)
    {
        Assert.AreEqual(new DateTime(2016, 8, i), new DateTime(2016, 2, i).AddMonthToEndOfMonth(6));
    }
}

[TestMethod]
public void AddHalfYearTest_December()
{
    Assert.AreEqual(new DateTime(2016, 6, 30), new DateTime(2015, 12, 31).AddMonthToEndOfMonth(6));
    for (int i = 1; i <= 30; i++)
    {
        Assert.AreEqual(new DateTime(2016, 6, i), new DateTime(2015, 12, i).AddMonthToEndOfMonth(6));
    }
}
2
Tomas Kubes

Ce code ajoutera le nombre de mois et passera au dernier jour du mois cible si le jour actuel est le dernier jour du mois en cours. Notez qu'il n'y a fondamentalement pas de solution au problème du 30 janvier sans faire des dates entièrement personnalisées:

Si la date est le seul état, quelle que soit la façon dont vous gérez le mois en avance à partir du 30 janvier, vous devez choisir si vous interprétez le résultat comme le dernier jour de février ou simplement le 28 de la mois en cours. Vous ne pouvez pas avoir les deux car la date est votre seul état. Il n'y a pas de 'drapeau' qui vous dit que ce même 28 février était à l'origine le single jusqu'au dernier jour de janvier.

Cela signifie effectivement que Jan30.AddMonthsCustom (1) .AddMonthsCustom (1)! = Jan30.AddMonthsCustom (2) et que les 30, 29 et 28 dates finissent par se terminer le dernier jour du mois si vous continuez à propager.

public static DateTime AddMonthsCustom(this DateTime date, int months)
{

    // Check if we are done quickly.
    if(months == 0)
        return;

    // Lookup the target month and its last day.
    var targetMonth = new DateTime(date.Year, date.Month, 1).AddMonths(months);
    var lastDay = DateTime.DaysInMonth(targetMonth.Year, targetMonth.Month);

    // If we are starting out on the last day of the current month, then jump
    // to the last day of the target month.
    if (date.Day == DateTime.DaysInMonth(date.Year, date.Month))
        return new DateTime(targetMonth.Year, targetMonth.Month, lastDay);

    // If the target month cannot accomodate the current day, jump to the 
    // last day of the target month.
    if (date.Day > lastDay)
        return new DateTime(targetMonth.Year, targetMonth.Month, lastDay);

    // Simply jump to the current day in the target month.
    return new DateTime(targetMonth.Year, targetMonth.Month, date.Day);
}

Si toutefois je me trompe, faites le moi savoir. J'aimerais vraiment que cela soit résolu.

2
Christ A

Ce que rashleighp a suggéré est presque correct mais ne fonctionne pas, par exemple. en ajoutant 1 mois au 2016-02-29 (le résultat devrait être 2016-03-31) ou le 2017-02-28 (le résultat devrait être 2017-03-31)

Cette version modifiée devrait fonctionner avec tous les cas particuliers.

public static DateTime AddMonthsCustom(this DateTime source, int months)
{
    var firstDayOfTargetMonth = new DateTime(source.Year, source.Month, 1).AddMonths(months);
    var lastDayofSourceMonth = DateTime.DaysInMonth(source.Year, source.Month);
    var lastDayofTargetMonth = DateTime.DaysInMonth(firstDayOfTargetMonth.Year, firstDayOfTargetMonth.Month);

    var targetDay = source.Day > lastDayofTargetMonth ? lastDayofTargetMonth : source.Day;
    if (source.Day == lastDayofSourceMonth)
        targetDay = lastDayofTargetMonth;

    return new DateTime(
        firstDayOfTargetMonth.Year, 
        firstDayOfTargetMonth.Month, 
        targetDay, 
        source.Hour, 
        source.Minute, 
        source.Second, 
        source.Millisecond, 
        source.Kind);
}

Tous les tests NUnit ci-dessous ont réussi:

[TestCase("2017-01-01T01:01:01.0010000Z", "2016-12-01T01:01:01.0010000Z", 1)]
[TestCase("2017-02-01T01:01:01.0010000Z", "2016-12-01T01:01:01.0010000Z", 2)]
[TestCase("2017-03-31T01:01:01.0010000Z", "2016-12-31T01:01:01.0010000Z", 3)]
[TestCase("2016-03-28T01:01:01.0010000Z", "2016-02-28T01:01:01.0010000Z", 1)]
[TestCase("2016-03-31T01:01:01.0010000Z", "2016-02-29T01:01:01.0010000Z", 1)]
[TestCase("2017-03-31T01:01:01.0010000Z", "2017-02-28T01:01:01.0010000Z", 1)]
[TestCase("2016-02-29T01:01:01.0010000Z", "2016-01-31T01:01:01.0010000Z", 1)]
[TestCase("2017-02-28T01:01:01.0010000Z", "2017-01-31T01:01:01.0010000Z", 1)]
[TestCase("2016-12-01T01:01:01.0010000Z", "2017-01-01T01:01:01.0010000Z", -1)]
[TestCase("2016-12-01T01:01:01.0010000Z", "2017-02-01T01:01:01.0010000Z", -2)]
[TestCase("2016-12-31T01:01:01.0010000Z", "2017-03-31T01:01:01.0010000Z", -3)]
[TestCase("2016-02-28T01:01:01.0010000Z", "2016-03-28T01:01:01.0010000Z", -1)]
public void DateTimeExtensions_AddMonthsCustom(DateTime expected, DateTime dateTime, int months)
{
    // Arrange
    expected = expected.ToUniversalTime();
    dateTime = dateTime.ToUniversalTime();

    // Act
    DateTime result = dateTime.AddMonthsCustom(months);

    // Assert
    Assert.AreEqual(expected.Kind, result.Kind);
    Assert.AreEqual(expected, result);
}
1
niklr
public static class DateTimeExtensions
{
    public static DateTime AddMonthsCustom(this DateTime source, int months)
    {
        DateTime result = source.AddMonths(months);
        if (source.Day != DateTime.DaysInMonth(source.Year, source.Month))
            return result;

        return new DateTime(result.Year, result.Month,
                            DateTime.DaysInMonth(result.Year, result.Month),
                            result.Hour, result.Minute, result.Second,
                            result.Millisecond, result.Kind);
    }
}
0
LukeH

Je l'ai résolu maintenant, en vérifiant si c'est le dernier jour du mois en utilisant GetLastDayInCurrentMonth . Si c'est le cas, j'utilise 

DateTime nextMonth = date.AddDays(1).AddMonths(1).AddDays(-1);

Si ce n'est pas le dernier jour, je viens d'utiliser AddMonths (1)

Merci les gars!

0
grady
if(yourDate.Day == DaysInMonth(yourDate.Year,yourDate.Month)) //check for last day
    yourDate.AddDays(DateTime.DaysInMonth(yourDate.Year,(yourDate.Month+1)%12));
0
Amsakanna

Et ça? Il peut ajouter autant de mois que vous le souhaitez en tant que méthode d'extension - c'est-à-dire dateDue.AddSmarthMonths(6); - et considère tout jour du mois de janvier après le 28 "le dernier jour du mois".

    public static DateTime AddSmartMonths(this DateTime d, int nMonths)
    {
        int year = d.Year;
        int month = d.Month;
        int day = d.Day;

        if ((day == 30) && (day < DateTime.DaysInMonth(year, month)))
            d = d.AddDays(1);
        else if ((month == 1) && (day > 28))
            d = new DateTime(year, month, 31);

        return d.AddMonths(nMonths);
    }
0
JCCyC

tu peux essayer ça 

private void datTimPkerFrom_ValueChanged(object sender, EventArgs e)
{
    int DaysInMonth = DateTime.DaysInMonth(datTimPkerFrom.Value.Year, datTimPkerFrom.Value.Month);

    if (DaysInMonth == 31)
    {
        datTimPkerTo.Value = datTimPkerFrom.Value.AddDays(30);
    }
    else if (DaysInMonth == 30)
    {
        datTimPkerTo.Value = datTimPkerFrom.Value.AddDays(29);
    }
    else if (DaysInMonth == 29)
    {
        datTimPkerTo.Value = datTimPkerFrom.Value.AddDays(28);
    }
    else
    {
        datTimPkerTo.Value = datTimPkerFrom.Value.AddDays(27);
    }
}
0

Non, cela ne tient pas compte de cela. C'est du code personnalisé jusqu'au bout!

Votre code ne sera-t-il intéressé que par le dernier jour des mois ou voulez-vous que le code ajoute un mois à une date quelconque, mais tenez compte du fait que la date fournie est le dernier jour du mois?

0
David

Donne le dernier jour du mois suivant sur une ligne: 

var t1 = new DateTime(2010,2,28); 
var t2 = t1.AddDays((t1.Day * -1) + 1).AddMonths(2).AddMilliseconds(-1).Date;
// t2: {31.03.2010 00:00:00}

(Les opérations sont les suivantes: obtenir le premier jour du mois en cours (= le 1 er février), ajouter 2 mois (= le 1 er avril), soustraire de 1 ms (= le 31 mars), heure de fin facultative

0
M.Buschmann

Cela ajoutera numMonths à someDate et, si someDate est en fin de mois, la valeur renvoyée sera en fin de mois, sinon AddMonths (numMonths) sera renvoyé.

private DateTime AddMonthsRetainingEOM(DateTime someDate, int numMonths)
    {
        if (someDate.AddDays(1).Day == 1)
        {
            // someDate is EOM
            someDate = someDate.AddMonths(numMonths);
            // keep adding days if new someDate is not EOM
            while (someDate.AddDays(1).Day != 1)
            {
                someDate = someDate.AddDays(1);
            }
            return someDate;
        }
        else
        {
            // not EOM - Just add months
            return someDate.AddMonths(numMonths);
        }
    }
0
RichM