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


209

Я шукаю найпростіший, найпростіший спосіб додати X місяців до дати JavaScript.

Я б краще не впорався з перекочуванням року або мусив писати власну функцію .

Чи є щось вбудоване, що може це зробити?


4
Спробуйте додати метод до об’єкта-прототипу дати, наприклад --------------- Date.prototype.addMonth = function (n) {return new Date (this.setMonth (this.getMonth ( ) + п)); };
Moises Hidalgo

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 року, використовуючи стандартний об’єкт дати Javascript.
тад

6
Гарний улов, тад. Я здивований, що я цього не бачив. Зауважте, що TATE SQL select DATEADD (місяць, 1, '2011-10-31') дає "2011-11-30", що для мене є розумним. Я отримав 10 голосів із поганою відповіддю. Класно. :-)
Чад

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

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

3
просто додайте до встановлення 1-ї дати кожного місяця: today.setHours (0, 0, 0, 0); сьогодні.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
Інша проблема полягає в тому, що вони обидві змінюють початкову дату і повертають змінене значення. Може бути, є сенс додати його до прототипу дати або використовувати локальну змінну для зміненої дати у функції?
Antony Hatchkins

Спасибі Антоні, місцева змінна має сенс.
aMarCruz

1
Я б запропонував або видалити рядок "дата повернення", або додати локальний var.
Антоні Хетчкінс

Оновлено за допомогою локальних змінних та відокремлених тестів.
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's з бібліотеки 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

Просте рішення: 2678400000це 31 день в мілісекундах

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

Нижче наводиться приклад того, як обчислити майбутню дату на основі введення дати (članstvossignup_date) + доданих місяців (членськихмісяців) через поля форми.

Поле membershipsmonths має значення за замовчуванням 0

Тригерне ​​посилання (може бути подією, що змінюється, додається до поля терміну членства):

<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

Як показано в багатьох представлених складних, потворних відповідях, "Дати і тайми" можуть бути кошмаром для програмістів, які використовують будь-яку мову. Мій підхід полягає в перетворенні дат і значень "delta t" в час епохи (в мс), виконання будь-якої арифметики, а потім перетворення в "людський час".

// 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 );

Це було написано для дещо іншого випадку використання, але ви повинні мати можливість легко адаптувати його до відповідних завдань.

EDIT: Повне джерело тут !


3
Марна відповідь, оскільки вона не обробляє місяці з різною кількістю днів, в чому полягає питання.
Benubird

1
@Benubird - оскільки ви так ввічливо запитали, я завантажив повне джерело. Посилання в цій публікації.
Dan Ahlquist

Це також не забезпечує економію літнього часу, коли не всі дні тривають 24 години (або 8,64e7 мілісекунд).
RobG

Ні, це не так. DST - проблема локалізації.
Dan Ahlquist

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
Я вважаю, що це дуже verwirrend, коли більша частина коду є англійською мовою та змінними в Deutsch. ;)
Бернхард Хофманн

-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.