Reputation: 12735
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
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
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
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
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
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
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
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
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
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
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.
[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
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
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
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
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
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
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
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
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