Функция JavaScript для добавления X месяцев к дате


209

Я ищу самый простой и чистый способ добавить X месяцев к дате JavaScript.

Я бы предпочел не справляться с переносом года или писать свою собственную функцию .

Есть ли что-то встроенное, что может сделать это?


4
Попробуйте добавить метод в объект-прототип даты, например, --------------- Date.prototype.addMonth = function (n) {вернуть новую дату (this.setMonth (this.getMonth ( ) + п)); };
— Мойзес Идальго

1
@ kr37, ответ Моисеса Идальго не будет работать правильно, если в целевом месяце нет номера сегодняшнего дня. Ответ bmpasini также справляется с этим
— Александр Северин

Ответы здесь не очень хорошие, лучшие ответы - в Добавление месяцев к дате в JavaScript .
— RobG

Ответы:


278

Следующая функция добавляет месяцы к дате в JavaScript ( источник ). Он учитывает продления года и различную продолжительность месяца:

function addMonths(date, months) {
    var d = date.getDate();
    date.setMonth(date.getMonth() + +months);
    if (date.getDate() != d) {
      date.setDate(0);
    }
    return date;
}

// Add 12 months to 29 Feb 2016 -> 28 Feb 2017
console.log(addMonths(new Date(2016,1,29),12).toString());

// Subtract 1 month from 1 Jan 2017 -> 1 Dec 2016
console.log(addMonths(new Date(2017,0,1),-1).toString());

// Subtract 2 months from 31 Jan 2017 -> 30 Nov 2016
console.log(addMonths(new Date(2017,0,31),-2).toString());

// Add 2 months to 31 Dec 2016 -> 28 Feb 2017
console.log(addMonths(new Date(2016,11,31),2).toString());

Вышеупомянутое решение охватывает крайний случай перехода от месяца с большим числом дней, чем месяц назначения. например.

  • Добавьте двенадцать месяцев к 29 февраля 2020 года (должно быть 28 февраля 2021 года)
  • Добавьте один месяц к 31 августа 2020 года (должно быть 30 сентября 2020 года)

Если при подаче заявки меняется день месяца setMonth, мы знаем, что мы переполнились в следующий месяц из-за разницы в продолжительности месяца. В этом случае мы используем, setDate(0)чтобы вернуться к последнему дню предыдущего месяца.

Примечание: эта версия этого ответа заменяет более раннюю версию (ниже), которая не корректно обрабатывает разные месяцы.

var x = 12; //or whatever offset
var CurrentDate = new Date();
console.log("Current date:", CurrentDate);
CurrentDate.setMonth(CurrentDate.getMonth() + x);
console.log("Date after " + x + " months:", CurrentDate);

130
Осторожно - это не работает для крайних случаев, таких как добавление к 31-му дню большинства месяцев. Например, 31 октября 2011 г. + 1 месяц использования этого метода - 01 декабря 2011 г. с использованием стандартного объекта Date в Javascript.
— тад

6
Хороший улов, малыш. Я удивлен, что я этого не видел. Обратите внимание, что T-SQL select DATEADD (month, 1, '2011-10-31') дает '2011-11-30', что для меня разумно. Я получил 10 голосов с плохим ответом. Прохладно. :-)
— Чад

2
Также следите за високосными годами - добавление 1 года к 29 февраля в високосный год даст вам непредсказуемые результаты в зависимости от того, какой язык вы используете (в качестве общего ответа). Это то, что в 2012 году остановило облачную платформу Microsoft Azure на несколько часов
— Бен Уолдинг,

15
Существует addMonthsфункция здесь , что уважает конец месяца (например , 2012-01-31 + 1 month = 2012-02-29и так далее).
— RobG

3
просто добавьте, чтобы установить 1-ую дату каждого месяца: today.setHours (0, 0, 0, 0); today.setDate (1);
— Алексей Страх

56

Я использую библиотеку moment.js для манипуляций с датой и временем . Пример кода для добавления один месяц:

var startDate = new Date(...);
var endDateMoment = moment(startDate); // moment(...) can also be used to parse dates in string format
endDateMoment.add(1, 'months');

12
Для вашего же здравого смысла, используйте moment.js.
— Gaʀʀʏ

3
Абсолютно согласен с @garry: используйте moment.js.
— Мишель

2
Я согласен, что это самый чистый способ, но вопрос был в том, "Есть ли что-то встроенное, что может сделать это?" Момент добавляет десятки
— Евгений

Это было хорошо Никаких других плагинов, которые я нашел в сети, не может быть намного лучше, чем этот.
— Eraniichan

26

Эта функция обрабатывает крайние случаи и работает быстро:

function addMonthsUTC (date, count) {
  if (date && count) {
    var m, d = (date = new Date(+date)).getUTCDate()

    date.setUTCMonth(date.getUTCMonth() + count, 1)
    m = date.getUTCMonth()
    date.setUTCDate(d)
    if (date.getUTCMonth() !== m) date.setUTCDate(0)
  }
  return date
}

тест:

> d = new Date('2016-01-31T00:00:00Z');
Sat Jan 30 2016 18:00:00 GMT-0600 (CST)
> d = addMonthsUTC(d, 1);
Sun Feb 28 2016 18:00:00 GMT-0600 (CST)
> d = addMonthsUTC(d, 1);
Mon Mar 28 2016 18:00:00 GMT-0600 (CST)
> d.toISOString()
"2016-03-29T00:00:00.000Z"

Обновление для дат не в формате UTC: (А. Хаткинс)

function addMonths (date, count) {
  if (date && count) {
    var m, d = (date = new Date(+date)).getDate()

    date.setMonth(date.getMonth() + count, 1)
    m = date.getMonth()
    date.setDate(d)
    if (date.getMonth() !== m) date.setDate(0)
  }
  return date
}

тест:

> d = new Date(2016,0,31);
Sun Jan 31 2016 00:00:00 GMT-0600 (CST)
> d = addMonths(d, 1);
Mon Feb 29 2016 00:00:00 GMT-0600 (CST)
> d = addMonths(d, 1);
Tue Mar 29 2016 00:00:00 GMT-0600 (CST)
> d.toISOString()
"2016-03-29T06:00:00.000Z"

Единственный разумный ответ здесь. Но необходимо соблюдать осторожность в отношении дат, не связанных с UTC. Это может привести к неожиданным результатам (например, 31 января + 1 месяц = ​​1 марта для UTC + 1 полночь часового пояса)
— Энтони Хэтчкинс

1
Другая проблема заключается в том, что он изменяет исходную дату и возвращает измененное значение. Может быть, имеет смысл добавить его в прототип даты или использовать локальную переменную для измененной даты в функции?
— Энтони Хэтчкинс

Спасибо Антоний, локальная переменная имеет смысл.
— aMarCruz

1
Я бы предложил либо удалить строку «дата возврата», либо добавить локальную переменную.
— Энтони Хэтчкинс

Обновлен с использованием локальной переменной и отдельных тестов.
— aMarCruz

12

Учитывая, что ни один из этих ответов не будет учитывать текущий год, когда меняется месяц, вы можете найти ответ, который я сделал ниже:

Метод:

Date.prototype.addMonths = function (m) {
    var d = new Date(this);
    var years = Math.floor(m / 12);
    var months = m - (years * 12);
    if (years) d.setFullYear(d.getFullYear() + years);
    if (months) d.setMonth(d.getMonth() + months);
    return d;
}

Использование:

return new Date().addMonths(2);

Этот ответ не учитывает добавление месяцев, в которых нет соответствующей даты в получающемся месяце (например, 31 января + 1 месяц => 2 или 3 марта). Также кажется, что есть проблема с добавлением более 12 месяцев к дате: нет. При добавлении, скажем, 13 месяцев, нет необходимости добавлять 1 год и 1 месяц. Просто добавьте 13 месяцев.
— RobG

9

Взяты из ответов @bmpsini и @Jazaret , но не расширяют прототипы: используют простые функции ( почему расширение нативных объектов - плохая практика? ):

function isLeapYear(year) { 
    return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0)); 
}

function getDaysInMonth(year, month) {
    return [31, (isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month];
}

function addMonths(date, value) {
    var d = new Date(date),
        n = date.getDate();
    d.setDate(1);
    d.setMonth(d.getMonth() + value);
    d.setDate(Math.min(n, getDaysInMonth(d.getFullYear(), d.getMonth())));
    return d;
}

Используй это:

var nextMonth = addMonths(new Date(), 1);

4

Из приведенных выше ответов единственная проблема, которая обрабатывает крайние случаи (bmpasini из библиотеки datejs), имеет проблему:

var date = new Date("03/31/2015");
var newDate = date.addMonths(1);
console.log(newDate);
// VM223:4 Thu Apr 30 2015 00:00:00 GMT+0200 (CEST)

Да, но:

newDate.toISOString()
//"2015-04-29T22:00:00.000Z"

хуже :

var date = new Date("01/01/2015");
var newDate = date.addMonths(3);
console.log(newDate);
//VM208:4 Wed Apr 01 2015 00:00:00 GMT+0200 (CEST)
newDate.toISOString()
//"2015-03-31T22:00:00.000Z"

Это происходит из-за того, что время не установлено, и, таким образом, происходит возврат к 00:00:00, который затем может сбиться с предыдущим днем ​​из-за изменения часового пояса или экономии времени или чего-то еще ...

Вот мое предлагаемое решение, в котором нет такой проблемы, и, я думаю, оно более элегантно, поскольку оно не опирается на жестко закодированные значения.

/**
* @param isoDate {string} in ISO 8601 format e.g. 2015-12-31
* @param numberMonths {number} e.g. 1, 2, 3...
* @returns {string} in ISO 8601 format e.g. 2015-12-31
*/
function addMonths (isoDate, numberMonths) {
    var dateObject = new Date(isoDate),
        day = dateObject.getDate(); // returns day of the month number

    // avoid date calculation errors
    dateObject.setHours(20);

    // add months and set date to last day of the correct month
    dateObject.setMonth(dateObject.getMonth() + numberMonths + 1, 0);

    // set day number to min of either the original one or last day of month
    dateObject.setDate(Math.min(day, dateObject.getDate()));

    return dateObject.toISOString().split('T')[0];
};

Модуль успешно протестирован с:

function assertEqual(a,b) {
    return a === b;
}
console.log(
    assertEqual(addMonths('2015-01-01', 1), '2015-02-01'),
    assertEqual(addMonths('2015-01-01', 2), '2015-03-01'),
    assertEqual(addMonths('2015-01-01', 3), '2015-04-01'),
    assertEqual(addMonths('2015-01-01', 4), '2015-05-01'),
    assertEqual(addMonths('2015-01-15', 1), '2015-02-15'),
    assertEqual(addMonths('2015-01-31', 1), '2015-02-28'),
    assertEqual(addMonths('2016-01-31', 1), '2016-02-29'),
    assertEqual(addMonths('2015-01-01', 11), '2015-12-01'),
    assertEqual(addMonths('2015-01-01', 12), '2016-01-01'),
    assertEqual(addMonths('2015-01-01', 24), '2017-01-01'),
    assertEqual(addMonths('2015-02-28', 12), '2016-02-28'),
    assertEqual(addMonths('2015-03-01', 12), '2016-03-01'),
    assertEqual(addMonths('2016-02-29', 12), '2017-02-28')
);

3

Как было отмечено в большинстве ответов, мы можем использовать метод setMonth () вместе с методом getMonth (), чтобы добавить определенное количество месяцев к определенной дате.

Пример: (как упомянуто @ChadD в своем ответе.)

var x = 12; //or whatever offset 
var CurrentDate = new Date();
CurrentDate.setMonth(CurrentDate.getMonth() + x);

Но мы должны осторожно использовать это решение, так как у нас возникнут проблемы с крайними случаями.

Для обработки крайних случаев полезен ответ, который приведен в следующей ссылке.

https://stackoverflow.com/a/13633692/3668866


3

Простое решение: 267840000031 день в миллисекундах

var oneMonthFromNow = new Date((+new Date) + 2678400000);

Обновить:

Используйте эти данные для создания нашей собственной функции:

  • 2678400000 - 31 день
  • 2592000000 - 30 дней
  • 2505600000 - 29 дней
  • 2419200000 - 28 дней

8
У некоторых месяцев нет 31 дня
— Александру Северин

Согласитесь, но вы можете легко настроить или написать функцию
— dr.dimitru

2
Я предполагаю, что люди ищут именно ту функцию, которая автоматически учитывает дни в месяцах.
— Александр Берд

@AlexanderBird, затем используйте пуленепробиваемый инструмент, созданный кем-то другим, например moment.js
— dr.dimitru

2
Этот ответ не учитывает переход на летнее время, когда не все дни имеют продолжительность 24 часа (или 8,64e7 миллисекунд).
— RobG

2
d = new Date();

alert(d.getMonth()+1);

Месяцы имеют индекс, основанный на 0, он должен предупреждать (4), который равен 5 (май);


Этот метод может вернуть значение <0 и больше, чем 12
— Патрик

2

Просто чтобы добавить к принятому ответу и комментариям.

var x = 12; //or whatever offset
var CurrentDate = new Date();

//For the very rare cases like the end of a month
//eg. May 30th - 3 months will give you March instead of February
var date = CurrentDate.getDate();
CurrentDate.setDate(1);
CurrentDate.setMonth(CurrentDate.getMonth()+X);
CurrentDate.setDate(date);

2
Когда вы устанавливаете дату (31) на «1 февраля», вы получаете «3 марта». Это то, что вы действительно хотите?
— Энтони Хэтчкинс

2

Я написал это альтернативное решение, которое прекрасно работает для меня. Это полезно, когда вы хотите рассчитать окончание контракта. Например, начало = 2016-01-15, месяцы = 6, конец = 2016-7-14 (т. Е. Последний день - 1):

<script>
function daysInMonth(year, month)
{
    return new Date(year, month + 1, 0).getDate();
}

function addMonths(date, months)
{
    var target_month = date.getMonth() + months;
    var year = date.getFullYear() + parseInt(target_month / 12);
    var month = target_month % 12;
    var day = date.getDate();
    var last_day = daysInMonth(year, month);
    if (day > last_day)
    {
        day = last_day;
    }
    var new_date = new Date(year, month, day);
    return new_date;
}

var endDate = addMonths(startDate, months);
</script>

Примеры:

addMonths(new Date("2016-01-01"), 1); // 2016-01-31
addMonths(new Date("2016-01-01"), 2); // 2016-02-29 (2016 is a leap year)
addMonths(new Date("2016-01-01"), 13); // 2017-01-31
addMonths(new Date("2016-01-01"), 14); // 2017-02-28

0

Ниже приведен пример расчета будущей даты на основе ввода даты (membersssignup_date) + добавленных месяцев (membershipsmonths) через поля формы.

Поле membershipsmonths имеет значение по умолчанию 0

Триггерная ссылка (может быть событием onchange, прикрепленным к полю членства):

<a href="#" onclick="calculateMshipExp()"; return false;">Calculate Expiry Date</a>

function calculateMshipExp() {

var calcval = null;

var start_date = document.getElementById("membershipssignup_date").value;
var term = document.getElementById("membershipsmonths").value;  // Is text value

var set_start = start_date.split('/');  

var day = set_start[0];  
var month = (set_start[1] - 1);  // January is 0 so August (8th month) is 7
var year = set_start[2];
var datetime = new Date(year, month, day);
var newmonth = (month + parseInt(term));  // Must convert term to integer
var newdate = datetime.setMonth(newmonth);

newdate = new Date(newdate);
//alert(newdate);

day = newdate.getDate();
month = newdate.getMonth() + 1;
year = newdate.getFullYear();

// This is British date format. See below for US.
calcval = (((day <= 9) ? "0" + day : day) + "/" + ((month <= 9) ? "0" + month : month) + "/" + year);

// mm/dd/yyyy
calcval = (((month <= 9) ? "0" + month : month) + "/" + ((day <= 9) ? "0" + day : day) + "/" + year);

// Displays the new date in a <span id="memexp">[Date]</span> // Note: Must contain a value to replace eg. [Date]
document.getElementById("memexp").firstChild.data = calcval;

// Stores the new date in a <input type="hidden" id="membershipsexpiry_date" value="" name="membershipsexpiry_date"> for submission to database table
document.getElementById("membershipsexpiry_date").value = calcval;
}

0

Как показывают многие из сложных, безобразных ответов, Dates and Times могут стать кошмаром для программистов, использующих любой язык. Мой подход заключается в том, чтобы преобразовать даты и значения 'delta t' в Epoch Time (в мс), выполнить любую арифметику, а затем преобразовать обратно в «человеческое время».

// Given a number of days, return a Date object
//   that many days in the future. 
function getFutureDate( days ) {

    // Convert 'days' to milliseconds
    var millies = 1000 * 60 * 60 * 24 * days;

    // Get the current date/time
    var todaysDate = new Date();

    // Get 'todaysDate' as Epoch Time, then add 'days' number of mSecs to it
    var futureMillies = todaysDate.getTime() + millies;

    // Use the Epoch time of the targeted future date to create
    //   a new Date object, and then return it.
    return new Date( futureMillies );
}

// Use case: get a Date that's 60 days from now.
var twoMonthsOut = getFutureDate( 60 );

Это было написано для немного другого варианта использования, но вы должны быть в состоянии легко адаптировать его для связанных задач.

РЕДАКТИРОВАТЬ: Полный источник здесь !


3
Бесполезный ответ, потому что он не обрабатывает месяцы с разным количеством дней, вот в чем вопрос.
— Benubird

1
@Benubird - так как вы так вежливо спросили, я загрузил полный исходный код. Ссылка в этом посте.
— Дэн Алквист

Это также не учитывает переход на летнее время, когда не все дни имеют продолжительность 24 часа (или 8,64e7 миллисекунд).
— RobG

Нет, это не так. ТЛЧ является проблемой локализации.
— Дэн Алквист

0

Все это кажется слишком сложным, и я предполагаю, что он вступает в дискуссию о том, что именно означает «месяц». Это значит 30 дней? Это означает с 1-го по 1-е? С последнего дня до последнего дня?

Если последнее, то если добавить месяц к 27 февраля, вы получите 27 марта, а если добавить месяц к 28 февраля, то получите 31 марта (кроме високосных лет, когда 28 марта). Затем вычитая месяц с 30 марта, вы получаете ... 27 февраля? Кто знает...

Для тех, кто ищет простое решение, просто добавьте миллисекунды и все готово.

function getDatePlusDays(dt, days) {
  return new Date(dt.getTime() + (days * 86400000));
}

или

Date.prototype.addDays = function(days) {
  this = new Date(this.getTime() + (days * 86400000));
};

Мне кажется, что ясно, что «добавление X месяцев» означает, что месяц увеличивается на X. Это правда, что ваш ответ имеет меньше алгоритмических шагов для кодирования, но, к сожалению, я чувствую, что точность превосходит простоту. Когда люди ищут ответ, они хотят, чтобы «31 июля 2016 года» стало «31 августа 2016 года». И это не сделало бы это. Кроме того, если вы действительно хотите простоты за счет точности, почему бы не придерживаться d.setMonth(d.getMonth()+1)? Так что это даже не самая простая идея.
— Александр Берд

Это также не учитывает переход на летнее время, когда не все дни имеют продолжительность 24 часа (или 8,64e7 миллисекунд).
— RobG

-1
addDateMonate : function( pDatum, pAnzahlMonate )
{
    if ( pDatum === undefined )
    {
        return undefined;
    }

    if ( pAnzahlMonate === undefined )
    {
        return pDatum;
    }

    var vv = new Date();

    var jahr = pDatum.getFullYear();
    var monat = pDatum.getMonth() + 1;
    var tag = pDatum.getDate();

    var add_monate_total = Math.abs( Number( pAnzahlMonate ) );

    var add_jahre = Number( Math.floor( add_monate_total / 12.0 ) );
    var add_monate_rest = Number( add_monate_total - ( add_jahre * 12.0 ) );

    if ( Number( pAnzahlMonate ) > 0 )
    {
        jahr += add_jahre;
        monat += add_monate_rest;

        if ( monat > 12 )
        {
            jahr += 1;
            monat -= 12;
        }
    }
    else if ( Number( pAnzahlMonate ) < 0 )
    {
        jahr -= add_jahre;
        monat -= add_monate_rest;

        if ( monat <= 0 )
        {
            jahr = jahr - 1;
            monat = 12 + monat;
        }
    }

    if ( ( Number( monat ) === 2 ) && ( Number( tag ) === 29 ) )
    {
        if ( ( ( Number( jahr ) % 400 ) === 0 ) || ( ( Number( jahr ) % 100 ) > 0 ) && ( ( Number( jahr ) % 4 ) === 0 ) )
        {
            tag = 29;
        }
        else
        {
            tag = 28;
        }
    }

    return new Date( jahr, monat - 1, tag );
}


testAddMonate : function( pDatum , pAnzahlMonate )
{
    var datum_js = fkDatum.getDateAusTTMMJJJJ( pDatum );
    var ergebnis = fkDatum.addDateMonate( datum_js, pAnzahlMonate );

    app.log( "addDateMonate( \"" + pDatum + "\", " + pAnzahlMonate + " ) = \"" + fkDatum.getStringAusDate( ergebnis ) + "\"" );
},


test1 : function()
{
    app.testAddMonate( "15.06.2010",    10 );
    app.testAddMonate( "15.06.2010",   -10 );
    app.testAddMonate( "15.06.2010",    37 );
    app.testAddMonate( "15.06.2010",   -37 );
    app.testAddMonate( "15.06.2010",  1234 );
    app.testAddMonate( "15.06.2010", -1234 );
    app.testAddMonate( "15.06.2010",  5620 );
    app.testAddMonate( "15.06.2010", -5120 );

}

1
Я нахожу это очень странным, когда большая часть кода написана на английском языке, а переменные - на немецком. ;)
— Бернхард Хофманн

-1

Иногда полезно создать дату одним оператором, как в параметрах BIRT

Я сделал 1 месяц назад с:

new Date(new Date().setMonth(new Date().getMonth()-1));   

-3
var a=new Date();
a.setDate(a.getDate()+5);

Как указано выше, вы можете добавить месяц, чтобы Dateфункционировать.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.