DateTime.AddMonths добавляет только месяц не в дни
Скажем, у меня 28 февраля 2010 года и добавьте один месяц к этой дате, используя AddMonths(1)
...
итоговая дата 28 марта, но не 31 марта, которую я хочу.
Есть ли способ немного изменить это, чтобы это работало без добавления пользовательского кода?
Изменить: мне не нужен последний день месяца, на самом деле мне нужно добавить один месяц, но когда он последний день месяца, мне нужно найти последний день следующего месяца.
Ответы
Ответ 1
Я не знаю, чего вы хотите достичь, но вы можете добавить один день, добавить месяц и вычесть один день.
DateTime nextMonth = date.AddDays(1).AddMonths(1).AddDays(-1);
ИЗМЕНИТЬ
Как отмечает один из комментаторов, это иногда дает неправильный результат. После прочтения обновленного вопроса, я думаю, что самый простой способ подсчета нужной даты:
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);
}
Этот метод расширения возвращает дату следующего месяца. Когда текущая дата будет последним днем месяца, она вернет последний день следующего месяца.
Ответ 2
Если вы имеете в виду, что итоговая дата должна быть на одном и том же расстоянии от конца месяца, тогда вы попадаете в пользовательский код - что-то вроде (не полностью протестировано, особенно re 28/30/31 месяц):
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;
}
}
Ответ 3
Как насчет этого? Он решает проблему 30 января, которая возникла бы с лучшим ответом.
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
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));
}
}
И обобщается в течение нескольких месяцев:
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);
}
}
Код проверяется на февральские проблемы, високосный год и переход на Новый год. Все испытания прошли.
![введите описание изображения здесь]()
[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));
}
}
Ответ 5
Этот код добавит количество месяцев и перейдет к последнему дню целевого месяца, если текущий день - последний день текущего месяца. Обратите внимание, что с проблемой 30 января не существует принципиально нет решения, не выполняя полностью пользовательские даты:
Если дата является единственным состоянием, то независимо от того, как вы справляетесь с переходом на один месяц вперед с 30 января, вы должны выбрать, будете ли вы интерпретировать результат как последний день февраля или просто 28-го числа текущего месяца. У вас не может быть обоих, поскольку дата - это ваше единственное состояние. Нет "флага", в котором говорится, что именно 28 февраля изначально был синглом до последнего дня в январе.
Эффективно это означает, что Jan30.AddMonthsCustom(1).AddMonthsCustom(1)!= Jan30.AddMonthsCustom(2) и что в конце концов любой 30-й, 29-й и 28-й даты заканчивается в последний день месяца, если вы продолжаете распространять.
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);
}
Если я ошибаюсь, сообщите мне. Я действительно хотел бы, чтобы это было решено.
Ответ 6
Что предлагает rashleighp, почти правильно, но не работает, например. добавление 1 месяца к 2016-02-29 (результат должен быть 2016-03-31) или 2017-02-28 (результат должен быть 2017-03-31)
Эта модифицированная версия должна работать, включая все особые случаи.
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);
}
Все перечисленные ниже тесты NUnit:
[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);
}
Ответ 7
Нет - это не учитывается. Это настраиваемый код полностью!
Будет ли ваш код интересоваться только в последний день месяца, или вы хотите, чтобы код добавлял месяц к какой-либо дате, но учитывайте, когда указанная дата является последним днем месяца?
Ответ 8
Я решил это сейчас, проверив, является ли это последним днем месяца, используя GetLastDayInCurrentMonth. Если это так, я использую
DateTime nextMonth = date.AddDays(1).AddMonths(1).AddDays(-1);
Если это не последний день, я просто использую AddMonths (1)
Спасибо, ребята!
Ответ 9
if(yourDate.Day == DaysInMonth(yourDate.Year,yourDate.Month)) //check for last day
yourDate.AddDays(DateTime.DaysInMonth(yourDate.Year,(yourDate.Month+1)%12));
Ответ 10
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);
}
}
Ответ 11
Как насчет этого? Он может добавить столько месяцев, сколько вам нравится в качестве метода расширения, т.е. dateDue.AddSmarthMonths(6);
- и рассматривает любой день января после 28 дней последнего дня месяца.
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);
}
Ответ 12
вы можете попробовать это
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);
}
}
Ответ 13
Это добавит numMonths к someDate и, если someDate будет в конце месяца, возвращаемое значение будет в конце месяца, в противном случае он просто добавляет 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);
}
}
Ответ 14
Дает последний день следующего месяца в одной строке:
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}
(Операции: получить первый день текущего месяца (= 1 февраля 10), добавить 2 месяца (= 1 апреля 10), вычесть на 1 мс (= 31. 10 марта), необязательное время отключения
Ответ 15
Приведенный ниже метод расширения даст последний день месяца в том случае, если указанная дата является последним днем месяца, в противном случае, как в API datetime.
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);
}
Ввод 28.02.2010, 12:00:00
Выходные данные плюс 1 месяц 31.03.2010, 12:00:00
Ответ 16
Попробуйте перегрузить свойство day и установите его на 32. Whagen это делается в JavaScript, я считаю, что он по умолчанию возвращается к последнему дню любого месяца, в котором вы находитесь.