grady
grady

Reputation: 12735

DateTime.AddMonths adding only month not days

Let's say I have 28th of February 2010 and add one month to this date using AddMonths(1). The resulting date is March 28th, but not 31st of March, which I want.
Is there a way to tweak that a bit so this works without adding custom code?

Edit: I don't need the last day of a month, actually I need to add one month, but when its the last day of a month, I need to find the last day of the next month.

Upvotes: 35

Views: 113004

Answers (18)

rashleighp
rashleighp

Reputation: 1226

How about like this? It solves the Jan 30th problem that would occur with the current best answer.

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

Upvotes: 4

Rafael Leonardi
Rafael Leonardi

Reputation: 62

If you´re trying to get always the same day in the next month, wouldn't it be simplier to use:

DateTime nextMonth = new DateTime(thisMonth.Year, thisMonth.Month + 1, thisMonth.Day);

Upvotes: 0

Marc Gravell
Marc Gravell

Reputation: 1062510

If you mean that the resultant date should be the same distance from the end of the month, then you're into custom code - something like (not fully tested, especially re 28/30/31 months):

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

}

Upvotes: 7

sospeter mule
sospeter mule

Reputation: 21

You can also try this

int numberofmonths = Convert.ToInt32(textBox2.Text); //number of target months as integer
dateTimePicker1.Text = DateTime.Parse(strDate).Date.AddMonths(numberofmonths).ToString("d"); //new date displayed

Upvotes: 0

lazer
lazer

Reputation: 51

Below mentioned extension method will yield the last day of month in case if the given date is last day of the month otherwise as it's in datetime API.

public static DateTime AddMonthsE(this DateTime value,int numberOfMonths)
{           
    bool isEndDate = DateTime.DaysInMonth(value.Year, value.Month) == value.Day;
    if(isEndDate)
    {
        var newDateTime = value.AddMonths(numberOfMonths);
        return new DateTime(newDateTime.Year, newDateTime.Month, DateTime.DaysInMonth(newDateTime.Year, newDateTime.Month));
    }
    return value.AddMonths(numberOfMonths);
}

Input 2/28/2010 12:00:00 AM Output plus 1 months 3/31/2010 12:00:00 AM

Upvotes: 0

M.Buschmann
M.Buschmann

Reputation: 263

Gives the last day in the next month in one line:

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}

(The operations are: get first day of current month (= 1.Feb 10), Add 2 Months (= 1. Apr 10), Subtract by 1 ms (= 31. Mar 10), Optional cutoff Time

Upvotes: 0

RichM
RichM

Reputation: 3

This will add numMonths to someDate and, if someDate is end-of-month, return value will be end-of-month, otherwise it just does AddMonths(numMonths)

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

Upvotes: 0

niklr
niklr

Reputation: 1701

What rashleighp suggested is almost correct but does not work for e.g. adding 1 month to 2016-02-29 (result should be 2016-03-31) or 2017-02-28 (result should be 2017-03-31)

This modified version should work including all special cases.

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

All NUnit tests below passed:

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

Upvotes: 2

Eng.Mohamed Bassuny
Eng.Mohamed Bassuny

Reputation: 21

you can try this

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

Upvotes: 0

Tomas Kubes
Tomas Kubes

Reputation: 25098

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

And generalized for multiple months:

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

The code is tested against February issues, leap year and New Year transition. All test passed.

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

Upvotes: 14

JCCyC
JCCyC

Reputation: 16612

What about this? It can add as many months as you like as an extension method -- i.e. dateDue.AddSmarthMonths(6); -- and considers any day of January after 28 "the last day of the month".

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

Upvotes: 0

Christ A
Christ A

Reputation: 519

This code will add the number of months and will jump to the last day of the target month if the current day is the last day of the current month. Note that there is fundamentally no solution to the problem of January 30 without doing fully custom dates:

If the date is the only state, then no matter how you handle the one month jump ahead from January 30, you have to chose if you interpret the result as the last day of February or simply the 28th of the current month. You can't have both since the date is your only state. There is no 'flag' which tells you that this very 28th of February was originally the single to last day in January.

Effectively this means that Jan30.AddMonthsCustom(1).AddMonthsCustom(1) != Jan30.AddMonthsCustom(2) and that eventually any 30th, 29th and 28th date ends up on the last day of the month if you keep propagating.

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

If I am however wrong, please let me know. I really would like to have this solved.

Upvotes: 2

Amsakanna
Amsakanna

Reputation: 12934

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

Upvotes: 0

Philippe Leybaert
Philippe Leybaert

Reputation: 171734

I don't know what you want to achieve, but you could add one day, add a month and subtract one day.

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

EDIT:

As one of the commenters points out, this sometimes gives the wrong result. After reading your updated question, I think the easiest way of calculating the date you want is:

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

This extension method returns next month's date. When the current date is the last day of the month, it will return the last day of next month.

Upvotes: 57

LukeH
LukeH

Reputation: 269278

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

Upvotes: 0

grady
grady

Reputation: 12735

I solved it now, by checking if it is the last day of a month using GetLastDayInCurrentMonth. If thats the case, I use

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

If its not the last day, I just use AddMonths(1)

Thanks guys!

Upvotes: 0

Sam
Sam

Reputation: 563

Try overloading the day property and setting it to 32. Whgen this is done in JavaScript, I believe it defaults back to the last day of whatever month you're in.

Upvotes: -1

David
David

Reputation: 16120

No - it doesn't take that into account. It's custom code all the way!

Will your code only be interested in the last day of months, or do you want code to add a month to any date, but take into account when the date supplied is the last day of the month?

Upvotes: 0

Related Questions