Função JavaScript para adicionar X meses a uma data

209

Estou procurando a maneira mais fácil e limpa de adicionar X meses a uma data do JavaScript.

Prefiro não lidar com a rolagem do ano ou ter que escrever minha própria função .

Existe algo embutido que pode fazer isso?

Chade
fonte
4
Tente adicionar um método ao objeto de protótipo da data, assim: --------------- Date.prototype.addMonth = function (n) {return new Date (this.setMonth (this.getMonth ( ) + n)); };
Moises Hidalgo
1
@ kr37, a resposta de Moises Hidalgo não funcionará corretamente se o mês pretendido não tiver o número do dia de hoje. A resposta de bmpasini lida com isso também
Alexandru Severin
As respostas aqui não são muito boas; há respostas melhores em Adicionando meses a uma data em JavaScript .
RobG 4/03

Respostas:

278

A função a seguir adiciona meses a uma data em JavaScript ( fonte ). Ele leva em consideração as renovações por ano e os vários meses:

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

A solução acima abrange o caso extremo de mudar de um mês com um número maior de dias que o mês de destino. por exemplo.

  • Adicione doze meses a 29 de fevereiro de 2020 (deve ser 28 de fevereiro de 2021)
  • Adicione um mês a 31 de agosto de 2020 (deve ser 30 de setembro de 2020)

Se o dia do mês mudar durante a inscrição setMonth, sabemos que transbordamos para o mês seguinte devido a uma diferença na duração do mês. Nesse caso, setDate(0)costumamos voltar ao último dia do mês anterior.

Nota: esta versão desta resposta substitui uma versão anterior (abaixo) que não lidava normalmente com comprimentos de mês diferentes.

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);
Chade
fonte
130
Cuidado - isso não funciona para casos extremos, como adicionar ao 31º dia da maioria dos meses. Por exemplo, 31 de outubro de 2011 + 1 mês usando esse método é 01 de dezembro de 2011 usando o objeto Date padrão do Javascript.
tad
6
Boa captura, pouco. Estou surpreso por não ter visto isso. Observe que a seleção T-SQL DATEADD (mês, 1, '2011-10-31') gera '2011-11-30', o que para mim é razoável. Eu recebi 10 votos com uma resposta ruim. Legal. :-)
Chad
2
Observe também os anos bissextos - adicionar 1 ano a 29 de fevereiro de um ano bissexto fornecerá resultados imprevisíveis, dependendo do idioma que você estiver usando (como resposta geral). Isto é o que levou para baixo da plataforma Microsoft Azure nuvem por várias horas em 2012
Ben Walding
15
Há uma addMonthsfunção aqui que respeita o final do mês (por exemplo, 2012-01-31 + 1 month = 2012-02-29e assim por diante).
RobG
3
basta adicionar para definir a primeira data de cada mês: today.setHours (0, 0, 0, 0); today.setDate (1);
Alexey Strakh
56

Estou usando a biblioteca moment.js para manipulações de data e hora . Código de exemplo para adicionar um mês:

var startDate = new Date(...);
var endDateMoment = moment(startDate); // moment(...) can also be used to parse dates in string format
endDateMoment.add(1, 'months');
anre
fonte
12
Para sua própria sanidade, use moment.js.
Gaʀʀʏ
3
Concordo absolutamente com @garry: use moment.js.
Michel Michel
2
Concordo que esta é a maneira mais limpa, mas a pergunta era "Existe algo embutido que possa fazer isso?" Momento acrescenta dezenas de Kb para o tamanho da página
Evgeny
Essa foi boa. Nenhum outro plug-in que encontrei na rede pode fazer muito melhor que isso.
Eraniichan
26

Essa função lida com casos extremos e é rápida:

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
}

teste:

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

Atualização para datas não UTC: (por A.Hatchkins)

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
}

teste:

> 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"
aMarCruz
fonte
A única resposta razoável aqui. Mas é preciso ter cuidado com datas não UTC. Pode gerar resultados inesperados (por exemplo, 31 de janeiro + 1 mês = 1 de março para o UTC + 1 fuso horário)
Antony Hatchkins 15/16
1
Outra questão é que ela modifica a data original e retorna o valor modificado. Talvez faça sentido adicioná-lo ao protótipo de data ou usar uma variável local para a data alterada na função?
Antony Hatchkins
Obrigado Antony, uma variável local faz sentido.
AMarCruz
1
Eu sugiro remover a linha "data de retorno" ou adicionar um var local.
Antony Hatchkins
Atualizado usando variáveis ​​locais e testes separados.
aMarCruz
12

Considerando que nenhuma dessas respostas será responsável pelo ano atual em que o mês for alterado, você pode encontrar uma que fiz abaixo e que deve lidar com isso:

O método:

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;
}

Uso:

return new Date().addMonths(2);
Control Freak
fonte
Esta resposta não leva em consideração a adição de meses, se não houver data correspondente no mês resultante (por exemplo, 31 de janeiro + 1 mês => 2 ou 3 de março). Também parece pensar que há um problema ao adicionar mais de 12 meses a uma Data: não há. Não é necessário adicionar, digamos, 13 meses, para adicionar 1 ano e 1 mês. Basta adicionar 13 meses.
RobG 4/03
9

Tomado de @bmpsini e @Jazaret respostas, mas não se estendendo protótipos: o uso de funções simples ( Por que está estendendo objetos nativos uma má prática? ):

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;
}

Use-o:

var nextMonth = addMonths(new Date(), 1);
Miquel
fonte
4

Das respostas acima, o único que lida com casos de borda (biblioteca da bmpasini da datejs) tem um problema:

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)

OK mas:

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

pior :

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"

Isso ocorre porque o horário não está sendo definido, revertendo para 00:00:00, que pode ser alterado para o dia anterior devido a alterações de fuso horário ou economia de tempo ou o que for ...

Aqui está a minha solução proposta, que não tem esse problema e também é, acho, mais elegante, pois não depende de valores codificados.

/**
* @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];
};

Unidade testada com sucesso com:

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')
);
pansay
fonte
3

Como a maioria das respostas destacou, poderíamos usar o método setMonth () junto com o método getMonth () para adicionar um número específico de meses a uma determinada data.

Exemplo: (como mencionado por @ChadD em sua resposta.)

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

Mas devemos usar essa solução com cuidado, pois teremos problemas com casos extremos.

Para lidar com casos extremos, é útil uma resposta dada no link a seguir.

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

Sampath Dilhan
fonte
3

Solução simples: 2678400000é de 31 dias em milissegundos

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

Atualizar:

Use esses dados para criar nossa própria função:

  • 2678400000 - 31 dias
  • 2592000000 - 30 dias
  • 2505600000 - 29 dias
  • 2419200000 - 28 dias
dr.dimitru
fonte
8
Alguns meses não têm 31 dias
Alexandru Severin
Concordo, mas você pode facilmente ajustar, ou função de gravação
dr.dimitru
2
Suponho que o que as pessoas procuram é exatamente essa função que automaticamente leva em consideração dias em meses.
Alexander Bird
@AlexanderBird em seguida, usar uma ferramenta à prova de balas criado por outra pessoa, como moment.js
dr.dimitru
2
Essa resposta não permite o horário de verão, onde nem todos os dias têm 24 horas (ou 8,64e7 milissegundos).
RobG
2
d = new Date();

alert(d.getMonth()+1);

Os meses têm um índice baseado em 0, ele deve alertar (4) que é 5 (maio);

Ben
fonte
Este método pode retornar um valor <0 e maior que 12
Patrick
2

Apenas para adicionar à resposta aceita e aos comentários.

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);
iamjt
fonte
2
Quando você define Data (31) como "1 de fevereiro", você obtém "3 de março". É o que você realmente quer?
Antony Hatchkins 15/02
2

Eu escrevi esta solução alternativa que funciona bem para mim. É útil quando você deseja calcular o final de um contrato. Por exemplo, start = 15-0-2016, meses = 6, end = 14-14-2016 (ou seja, último dia - 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>

Exemplos:

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
David Ragazzi
fonte
0

A seguir, é apresentado um exemplo de como calcular uma data futura com base na entrada de data (MembershipSignup_date) + meses adicionados (MembershipMonths) por meio de campos de formulário.

O campo membershipsmonths possui um valor padrão 0

Link de acionamento (pode ser um evento onchange anexado ao campo do termo de associação):

<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;
}
John G
fonte
0

Como demonstrado por muitas das complicadas e feias respostas apresentadas, Datas e horários podem ser um pesadelo para programadores que usam qualquer idioma. Minha abordagem é converter datas e valores 'delta t' em Epoch Time (em ms), executar qualquer aritmética e depois converter novamente em "human 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 );

Isso foi escrito para um caso de uso um pouco diferente, mas você deve poder adaptá-lo facilmente para tarefas relacionadas.

EDIT: Fonte completa aqui !

Dan Ahlquist
fonte
3
Resposta inútil, porque não lida com meses com diferentes números de dias, eis a questão.
Benubird 13/01
1
@ Benubird - desde que você pediu tão educadamente, eu enviei a fonte completa. Link nesta postagem.
Dan Ahlquist
Isso também não acomoda o horário de verão, onde nem todos os dias têm 24 horas (ou 8,64e7 milissegundos).
precisa saber é
Não, não faz. O horário de verão é uma preocupação de localização.
precisa
0

Tudo isso parece muito complicado e eu acho que entra em debate o que exatamente adicionar "um mês" significa. Isso significa 30 dias? Isso significa do 1º ao 1º? Desde o último dia até o último dia?

Se este último, adicionar um mês a 27 de fevereiro o levará a 27 de março, mas adicionar um mês a 28 de fevereiro o levará a 31 de março (exceto em anos bissextos, onde o levará a 28 de março). Subtrair um mês de 30 de março leva você a ... 27 de fevereiro? Quem sabe...

Para quem procura uma solução simples, basta adicionar milissegundos e pronto.

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

ou

Date.prototype.addDays = function(days) {
  this = new Date(this.getTime() + (days * 86400000));
};
jdforsythe
fonte
Sinto que está claro que "adicionar X meses" significa que o mês aumenta em X. É verdade que sua resposta tem menos etapas algorítmicas para codificar, mas infelizmente sinto que a precisão supera a simplicidade. Quando as pessoas procuram uma resposta, desejam que '31 de julho de 2016' se torne '31 de agosto de 2016'. E isso não faria isso. Além disso, se você realmente deseja simplicidade ao custo da precisão, por que não ficar com ela d.setMonth(d.getMonth()+1)? Portanto, essa nem é a idéia mais simples.
Alexander Bird
Isso também não acomoda o horário de verão, onde nem todos os dias têm 24 horas (ou 8,64e7 milissegundos).
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 );

}
ea234
fonte
1
Acho isso muito comum quando a maior parte do código está em inglês e as variáveis ​​em alemão. ;)
Bernhard Hofmann
-1

Às vezes, criar data útil por um operador, como nos parâmetros BIRT

Eu fiz 1 mês atrás com:

new Date(new Date().setMonth(new Date().getMonth()-1));   
Andrei Koshelap
fonte
-3
var a=new Date();
a.setDate(a.getDate()+5);

Como o método indicado acima, você pode adicionar um mês para Datefuncionar.

Venkatesh
fonte