Подтвердить что ты не робот

DateTime.AddMonths добавляет только месяц не в дни

Скажем, у меня 28 февраля 2010 года и добавьте один месяц к этой дате, используя AddMonths(1)...
итоговая дата 28 марта, но не 31 марта, которую я хочу.
Есть ли способ немного изменить это, чтобы это работало без добавления пользовательского кода?

Изменить: мне не нужен последний день месяца, на самом деле мне нужно добавить один месяц, но когда он последний день месяца, мне нужно найти последний день следующего месяца.

4b9b3361

Ответ 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, я считаю, что он по умолчанию возвращается к последнему дню любого месяца, в котором вы находитесь.