Функция 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.