Diferença de meses entre duas datas em JavaScript


135

Como calcularia a diferença para dois objetos Date () em JavaScript, enquanto retornava apenas o número de meses na diferença?

Qualquer ajuda seria ótimo :)


Um mês não é uma unidade de medida muito precisa porque a duração do mês muda dependendo do mês em que é. Se um intervalo durar 30 dias entre janeiro e fevereiro, será menos de 1 mês se você pensar em termos de 31 dias, mas mais de 1 mês se considerar os 28 ou 29 dias de fevereiro.
precisa

7
Pergunta não muito bem definida. De 28 de fevereiro, 23:58 a 1 de março, 00:01 é um mês? Ou apenas um dia? Ou apenas três minutos? Ou todos os três?
Thilo

1
@ Thilo Alguém que precise implementar isso provavelmente não terá respostas, pois o gerente não tem ideia do que está perguntando em primeiro lugar. :)
AlbertEngelB

Respostas:


229

A definição de "o número de meses na diferença" está sujeita a muita interpretação. :-)

Você pode obter o ano, mês e dia do mês a partir de um objeto de data JavaScript. Dependendo das informações que você está procurando, você pode usá-las para descobrir quantos meses existem entre dois pontos no tempo.

Por exemplo, fora do punho:

function monthDiff(d1, d2) {
    var months;
    months = (d2.getFullYear() - d1.getFullYear()) * 12;
    months -= d1.getMonth();
    months += d2.getMonth();
    return months <= 0 ? 0 : months;
}

(Observe que os valores do mês no JavaScript começam com 0 = janeiro.)

A inclusão de meses fracionários acima é muito mais complicada, porque três dias em fevereiro típico são uma fração maior desse mês (~ 10,714%) do que três dias em agosto (~ 9.677%) e, é claro, até fevereiro é uma meta em movimento dependendo se é um ano bissexto.

Existem também algumas bibliotecas de data e hora disponíveis para JavaScript que provavelmente facilitam esse tipo de coisa.


Nota : Havia um + 1no acima, aqui:

months = (d2.getFullYear() - d1.getFullYear()) * 12;
months -= d1.getMonth() + 1;
// −−−−−−−−−−−−−−−−−−−−^^^^
months += d2.getMonth();

Isso porque originalmente eu disse:

... descobre quantos meses completos estão entre duas datas, sem contar meses parciais (por exemplo, excluindo o mês em que cada data está).

Eu o removi por dois motivos:

  1. Não contar meses parciais acaba não sendo o que muitas (a maioria?) Das pessoas que estão chegando à resposta querem, então pensei em separá-las.

  2. Nem sempre funcionou mesmo com essa definição. :-D (Desculpe.)


82

Se você não considera o dia do mês, essa é de longe a solução mais simples

function monthDiff(dateFrom, dateTo) {
 return dateTo.getMonth() - dateFrom.getMonth() + 
   (12 * (dateTo.getFullYear() - dateFrom.getFullYear()))
}


//examples
console.log(monthDiff(new Date(2000, 01), new Date(2000, 02))) // 1
console.log(monthDiff(new Date(1999, 02), new Date(2000, 02))) // 12 full year
console.log(monthDiff(new Date(2009, 11), new Date(2010, 0))) // 1

Esteja ciente de que o índice do mês é baseado em 0. Isso significa que January = 0e December = 11.


11
Este é o código mais curto e fácil de entender que eu tinha visto até agora!
Omar

Nada complicado com este, exceto o fato de que todo mês iniciado é contado. 31/03/2011 -> 01/05/2011 será de dois meses e 01/03/2011 -> 31/05/2011, mas deve ser três para ser exato.
Natim 5/09

Se você quer apenas saber o número de meses, este é PERFEITO !!!!! Se você precisar saber com base nos dias do mês, isso não funcionará.
OSDM

1
Comece com o fato de 2018/01 e 2017/01 estarem separados por 12 meses. Trabalhe para trás a partir daí. Esta resposta recebe. ;)
Gerard ONeill

1
Obrigado! Esta função é muito útil para mim #
315 Long

29

Às vezes, você pode querer obter apenas a quantidade de meses entre duas datas, ignorando totalmente a parte do dia. Por exemplo, se você teve duas datas - 2013/06/21 e 2013/10/18 - e se importou apenas com as partes 2013/06 e 2013/10, aqui estão os cenários e as possíveis soluções:

var date1=new Date(2013,5,21);//Remember, months are 0 based in JS
var date2=new Date(2013,9,18);
var year1=date1.getFullYear();
var year2=date2.getFullYear();
var month1=date1.getMonth();
var month2=date2.getMonth();
if(month1===0){ //Have to take into account
  month1++;
  month2++;
}
var numberOfMonths; 

1.Se você deseja apenas o número de meses entre as duas datas, excluindo mês1 e mês2

numberOfMonths = (year2 - year1) * 12 + (month2 - month1) - 1;

2.Se você deseja incluir um dos meses

numberOfMonths = (year2 - year1) * 12 + (month2 - month1);

3.Se você deseja incluir os dois meses

numberOfMonths = (year2 - year1) * 12 + (month2 - month1) + 1;

Agradável! Funcionou perfeito para mim.

3
Para o último, eu recomendo: numberOfMonths=(year2-year1)*12+(month2-month1)+1;o que faz o mesmo, mas é mais semanticamente correto para mim
Natim

Sim, abordagem agradável e mais elegante.
Mikayil Abdullayev

Essa foi a melhor resposta. Obrigado!
Marienke #

Isso é simples e limpo. Obrigado por escrever um ótimo código.
zeros-and-ones

28

Aqui está uma função que fornece com precisão o número de meses entre duas datas.
O comportamento padrão conta apenas meses inteiros, por exemplo, 3 meses e 1 dia resultará em uma diferença de 3 meses. Você pode evitar isso definindo o roundUpFractionalMonthsparâmetro como true, portanto, uma diferença de 3 meses e 1 dia será retornada como 4 meses.

A resposta aceita acima (resposta de TJ Crowder) não é precisa, às vezes retorna valores errados.

Por exemplo, monthDiff(new Date('Jul 01, 2015'), new Date('Aug 05, 2015'))retornos 0que estão obviamente errados. A diferença correta é de 1 mês inteiro ou 2 meses arredondados.

Aqui está a função que escrevi:

function getMonthsBetween(date1,date2,roundUpFractionalMonths)
{
    //Months will be calculated between start and end dates.
    //Make sure start date is less than end date.
    //But remember if the difference should be negative.
    var startDate=date1;
    var endDate=date2;
    var inverse=false;
    if(date1>date2)
    {
        startDate=date2;
        endDate=date1;
        inverse=true;
    }

    //Calculate the differences between the start and end dates
    var yearsDifference=endDate.getFullYear()-startDate.getFullYear();
    var monthsDifference=endDate.getMonth()-startDate.getMonth();
    var daysDifference=endDate.getDate()-startDate.getDate();

    var monthCorrection=0;
    //If roundUpFractionalMonths is true, check if an extra month needs to be added from rounding up.
    //The difference is done by ceiling (round up), e.g. 3 months and 1 day will be 4 months.
    if(roundUpFractionalMonths===true && daysDifference>0)
    {
        monthCorrection=1;
    }
    //If the day difference between the 2 months is negative, the last month is not a whole month.
    else if(roundUpFractionalMonths!==true && daysDifference<0)
    {
        monthCorrection=-1;
    }

    return (inverse?-1:1)*(yearsDifference*12+monthsDifference+monthCorrection);
};

6
Sério, quem votou contra? Ele está certo, a resposta aceita é simplesmente ... errada.
Michael Blackburn

2
Essa é a resposta mais adequada.
Kalyan Chavali

Isso não está certo se, digamos, date1 é 22 de janeiro e date2 é 22 de agosto. Em seguida, retorna 7, que obviamente deve ser 8?
Nicolai Harbo 22/08

faça esta resposta aceita. Este mais preciso do que o aceito. Por exemplo, a resposta aceita não calcula os dias
Munkhdelger Tumenbayar

A resposta aceita de Crowder foi atualizada. Ainda pode haver diferenças importantes entre ela e esta resposta, mas, até o momento em que este artigo foi escrito, as preocupações listadas aqui foram abordadas.
clozach 14/06

23

Se você precisar contar meses inteiros, independentemente do mês ser 28, 29, 30 ou 31 dias. Abaixo deve funcionar.

var months = to.getMonth() - from.getMonth() 
    + (12 * (to.getFullYear() - from.getFullYear()));

if(to.getDate() < from.getDate()){
    months--;
}
return months;

Esta é uma versão estendida da resposta https://stackoverflow.com/a/4312956/1987208, mas corrige o caso em que calcula 1 mês para o caso de 31 de janeiro a 1º de fevereiro (1 dia).

Isso cobrirá o seguinte;

  • De 1º a 31 de janeiro ---> 30 dias ---> resultará em 0 (lógico, pois não é um mês inteiro)
  • De 1 de fevereiro a 1 de março ---> 28 ou 29 dias ---> resultará em 1 (lógico, pois é um mês inteiro)
  • De 15 de fevereiro a 15 de março ---> 28 ou 29 dias ---> resultarão em 1 (lógico desde que o mês se passou)
  • 31 de janeiro a 1º de fevereiro ---> 1 dia ---> resultará em 0 (óbvio, mas a resposta mencionada no post resulta em 1 mês)

2
Tha é o que eu pensei que eu vim aqui para verificação e essa é a única resposta que faz sentido para mim
Andreas

2
Isso não funciona ao comparar dois meses em que o mês é mais curto. Por exemplo, de 31 de março a 30 de abril. Isso é tudo em abril, então a resposta deve ser "1".
Michael Blackburn

7

Diferença de meses entre duas datas no JavaScript:

 start_date = new Date(year, month, day); //Create start date object by passing appropiate argument
 end_date = new Date(new Date(year, month, day)

total de meses entre a data inicial e a data final:

 total_months = (end_date.getFullYear() - start_date.getFullYear())*12 + (end_date.getMonth() - start_date.getMonth())

6

Eu sei que isso é muito tarde, mas postá-lo de qualquer maneira, apenas no caso de ajudar os outros. Aqui está uma função que eu criei que parece fazer um bom trabalho em contar diferenças em meses entre duas datas. É reconhecidamente muito mais atrevido que o de Mr.Crowder, mas fornece resultados mais precisos ao percorrer o objeto de data. Está no AS3, mas você deve ser capaz de abandonar a digitação forte e terá JS. Sinta-se livre para torná-lo melhor procurando alguém lá fora!

    function countMonths ( startDate:Date, endDate:Date ):int
    {
        var stepDate:Date = new Date;
        stepDate.time = startDate.time;
        var monthCount:int;

        while( stepDate.time <= endDate.time ) { 
            stepDate.month += 1;
            monthCount += 1;
        }           

        if ( stepDate != endDate ) { 
            monthCount -= 1;
        }

        return monthCount;
    }

Essa é a abordagem correta mais provável para a maioria das situações. Há tantas exceções e caprichos em nosso calendário, a melhor abordagem é delegar a manipulação deles em uma biblioteca bem testada, como Date (). Para a maioria dos períodos (curtos), isso retornará a mesma resposta que as abordagens calculadas, mas quando você estiver lidando com períodos longos (incluindo anos bissextos, anos bissextos que não são anos bissextos, bissextos) segundos, etc), é mais provável que você encontre uma exceção em que subtrair meses e adicionar anos estará incorreto. Veja minha resposta para exceções quando o cálculo seria melhor.
Michael Blackburn

5

Para expandir a resposta de @ TJ, se você estiver procurando meses simples, em vez de meses completos, basta verificar se a data de d2 é maior ou igual a d1. Ou seja, se d2 estiver no final do mês e d1 no mês, haverá mais 1 mês. Portanto, você deve conseguir fazer isso:

function monthDiff(d1, d2) {
    var months;
    months = (d2.getFullYear() - d1.getFullYear()) * 12;
    months -= d1.getMonth() + 1;
    months += d2.getMonth();
    // edit: increment months if d2 comes later in its month than d1 in its month
    if (d2.getDate() >= d1.getDate())
        months++
    // end edit
    return months <= 0 ? 0 : months;
}

monthDiff(
    new Date(2008, 10, 4), // November 4th, 2008
    new Date(2010, 2, 12)  // March 12th, 2010
);
// Result: 16; 4 Nov – 4 Dec '08, 4 Dec '08 – 4 Dec '09, 4 Dec '09 – 4 March '10

Isso não explica totalmente os problemas de horário (por exemplo, 3 de março às 16:00 e 3 de abril às 15:00), mas é mais preciso e apenas para algumas linhas de código.


5

Considere cada data em termos de meses e subtraia para encontrar a diferença.

var past_date = new Date('11/1/2014');
var current_date = new Date();

var difference = (current_date.getFullYear()*12 + current_date.getMonth()) - (past_date.getFullYear()*12 + past_date.getMonth());

Isso fará a diferença de meses entre as duas datas, ignorando os dias.


4

Existem duas abordagens: matemática e rápida, mas sujeitas a caprichos no calendário, ou iterativas e lentas, mas lida com todas as esquisitices (ou pelo menos os delegados as tratam em uma biblioteca bem testada).

Se você percorrer o calendário, aumentando a data de início em um mês e ver se passamos a data de término. Isso delega o tratamento de anomalias para as classes internas Date (), mas pode ser lento se você estiver fazendo isso por um grande número de datas. A resposta de James adota essa abordagem. Por mais que eu não goste da ideia, acho que essa é a abordagem "mais segura" e, se você estiver fazendo apenas um cálculo, a diferença de desempenho é realmente insignificante. Tendemos a otimizar demais as tarefas que serão executadas apenas uma vez.

Agora, se você estiver calculando essa função em um conjunto de dados, provavelmente não deseja executar essa função em cada linha (ou seja, Deus não permita, várias vezes por registro). Nesse caso, você pode usar quase qualquer uma das outras respostas aqui, exceto a resposta aceita, que está errada (diferença entre new Date()e new Date()é -1)?

Aqui está minha abordagem de uma abordagem matemática e rápida, responsável por diferentes comprimentos de meses e anos bissextos. Você realmente só deve usar uma função como essa se aplicá-la a um conjunto de dados (fazendo esse cálculo repetidamente). Se você precisar fazer isso apenas uma vez, use a abordagem iterativa de James acima, pois você está delegando o tratamento de todas as (muitas) exceções ao objeto Date ().

function diffInMonths(from, to){
    var months = to.getMonth() - from.getMonth() + (12 * (to.getFullYear() - from.getFullYear()));

    if(to.getDate() < from.getDate()){
        var newFrom = new Date(to.getFullYear(),to.getMonth(),from.getDate());
        if (to < newFrom  && to.getMonth() == newFrom.getMonth() && to.getYear() %4 != 0){
            months--;
        }
    }

    return months;
}

3

Aqui você segue outra abordagem com menos loop:

calculateTotalMonthsDifference = function(firstDate, secondDate) {
        var fm = firstDate.getMonth();
        var fy = firstDate.getFullYear();
        var sm = secondDate.getMonth();
        var sy = secondDate.getFullYear();
        var months = Math.abs(((fy - sy) * 12) + fm - sm);
        var firstBefore = firstDate > secondDate;
        firstDate.setFullYear(sy);
        firstDate.setMonth(sm);
        firstBefore ? firstDate < secondDate ? months-- : "" : secondDate < firstDate ? months-- : "";
        return months;
}

1
cuidado com este método: (obviamente, quando você o lê com atenção) altera a primeira data que é passada de volta ao chamador (como as datas são passadas por referência).
Andiih 15/07/16

3

Você também pode considerar esta solução, isso functionretorna a diferença do mês em número inteiro ou número

Passar a data de início como a primeira ou a última paramé tolerante a falhas. Ou seja, a função ainda retornaria o mesmo valor.

const diffInMonths = (end, start) => {
   var timeDiff = Math.abs(end.getTime() - start.getTime());
   return Math.round(timeDiff / (2e3 * 3600 * 365.25));
}

const result = diffInMonths(new Date(2015, 3, 28), new Date(2010, 1, 25));

// shows month difference as integer/number
console.log(result);


Você deve multiplicar por 365,25 levar anos bissextos em conta
darryn.ten

2

Calcular a diferença entre duas datas incluem fração do mês (dias).


var difference = (date2.getDate() - date1.getDate()) / 30 +
    date2.getMonth() - date1.getMonth() +
    (12 * (date2.getFullYear() - date1.getFullYear()));

Por exemplo:
date1 : 24/09/2015 (24 de setembro de 2015)
date2 : 09/11/2015 (9 de novembro de 2015)
a diferença: 2,5 (meses)


2

Isso deve funcionar bem:

function monthDiff(d1, d2) {
    var months;
    months = (d2.getFullYear() - d1.getFullYear()) * 12;
    months += d2.getMonth() - d1.getMonth();
    return months;
}

1
function calcualteMonthYr(){
    var fromDate =new Date($('#txtDurationFrom2').val()); //date picker (text fields)
    var toDate = new Date($('#txtDurationTo2').val());

var months=0;
        months = (toDate.getFullYear() - fromDate.getFullYear()) * 12;
        months -= fromDate.getMonth();
        months += toDate.getMonth();
            if (toDate.getDate() < fromDate.getDate()){
                months--;
            }
    $('#txtTimePeriod2').val(months);
}

1

O código a seguir retorna meses completos entre duas datas, levando em consideração o número de dias dos meses parciais.

var monthDiff = function(d1, d2) {
  if( d2 < d1 ) { 
    var dTmp = d2;
    d2 = d1;
    d1 = dTmp;
  }

  var months = (d2.getFullYear() - d1.getFullYear()) * 12;
  months -= d1.getMonth() + 1;
  months += d2.getMonth();

  if( d1.getDate() <= d2.getDate() ) months += 1;

  return months;
}

monthDiff(new Date(2015, 01, 20), new Date(2015, 02, 20))
> 1

monthDiff(new Date(2015, 01, 20), new Date(2015, 02, 19))
> 0

monthDiff(new Date(2015, 01, 20), new Date(2015, 01, 22))
> 0

1
function monthDiff(d1, d2) {
var months, d1day, d2day, d1new, d2new, diffdate,d2month,d2year,d1maxday,d2maxday;
months = (d2.getFullYear() - d1.getFullYear()) * 12;
months -= d1.getMonth() + 1;
months += d2.getMonth();
months = (months <= 0 ? 0 : months);
d1day = d1.getDate();
d2day = d2.getDate();
if(d1day > d2day)
{
    d2month = d2.getMonth();
    d2year = d2.getFullYear();
    d1new = new Date(d2year, d2month-1, d1day,0,0,0,0);
    var timeDiff = Math.abs(d2.getTime() - d1new.getTime());
          diffdate = Math.abs(Math.ceil(timeDiff / (1000 * 3600 * 24))); 
    d1new = new Date(d2year, d2month, 1,0,0,0,0);
    d1new.setDate(d1new.getDate()-1);
    d1maxday = d1new.getDate();
    months += diffdate / d1maxday;
}
else
{
      if(!(d1.getMonth() == d2.getMonth() && d1.getFullYear() == d2.getFullYear()))
    {
        months += 1;
    }
    diffdate = d2day - d1day + 1;
    d2month = d2.getMonth();
    d2year = d2.getFullYear();
    d2new = new Date(d2year, d2month + 1, 1, 0, 0, 0, 0);
    d2new.setDate(d2new.getDate()-1);
    d2maxday = d2new.getDate();
    months += diffdate / d2maxday;
}

return months;

}


1

abaixo da lógica buscará diferença em meses

(endDate.getFullYear()*12+endDate.getMonth())-(startDate.getFullYear()*12+startDate.getMonth())

1
function monthDiff(date1, date2, countDays) {

  countDays = (typeof countDays !== 'undefined') ?  countDays : false;

  if (!date1 || !date2) {
    return 0;
  }

  let bigDate = date1;
  let smallDate = date2;

  if (date1 < date2) {
    bigDate = date2;
    smallDate = date1;
  }

  let monthsCount = (bigDate.getFullYear() - smallDate.getFullYear()) * 12 + (bigDate.getMonth() - smallDate.getMonth());

  if (countDays && bigDate.getDate() < smallDate.getDate()) {
    --monthsCount;
  }

  return monthsCount;
}

0

Veja o que eu uso:

function monthDiff() {
    var startdate = Date.parseExact($("#startingDate").val(), "dd/MM/yyyy");
    var enddate = Date.parseExact($("#endingDate").val(), "dd/MM/yyyy");
    var months = 0;
    while (startdate < enddate) {
        if (startdate.getMonth() === 1 && startdate.getDate() === 28) {
            months++;
            startdate.addMonths(1);
            startdate.addDays(2);
        } else {
            months++;
            startdate.addMonths(1);
        }
    }
    return months;
}

0

Também conta os dias e os converte em meses.

function monthDiff(d1, d2) {
    var months;
    months = (d2.getFullYear() - d1.getFullYear()) * 12;   //calculates months between two years
    months -= d1.getMonth() + 1; 
    months += d2.getMonth();  //calculates number of complete months between two months
    day1 = 30-d1.getDate();  
    day2 = day1 + d2.getDate();
    months += parseInt(day2/30);  //calculates no of complete months lie between two dates
    return months <= 0 ? 0 : months;
}

monthDiff(
    new Date(2017, 8, 8), // Aug 8th, 2017    (d1)
    new Date(2017, 12, 12)  // Dec 12th, 2017   (d2)
);
//return value will be 4 months 

Você pode adicionar uma explicação mais detalhada de como isso funciona? Isto faria com que a resposta melhor
serge1peshcoff

Existe uma suposição meses têm 30 dias?
Ben Taliadoros

0

Número de meses em que o dia e a hora não importam

Nesse caso, não estou preocupado com meses inteiros, meses parciais, quanto tempo dura um mês etc. Eu só preciso saber o número de meses. Um caso relevante do mundo real seria onde um relatório deve ser entregue todos os meses, e eu preciso saber quantos relatórios devem existir.

Exemplo:

  • Janeiro = 1 mês
  • Janeiro - fevereiro = 2 meses
  • Novembro - janeiro = 3 meses

Este é um exemplo de código elaborado para mostrar para onde os números estão indo.

Vamos usar 2 carimbos de data e hora que devem resultar em 4 meses

  • Data e hora de 13 de novembro de 2019: 1573621200000
  • Registro de data e hora de 20 de fevereiro de 2020: 1582261140000

Pode ser um pouco diferente com o fuso horário / horário estipulado. O dia, minutos e segundos não importam e podem ser incluídos no carimbo de data / hora, mas o desconsideraremos com nosso cálculo real.

Etapa 1: converter o carimbo de data e hora em uma data JavaScript

let dateRangeStartConverted = new Date(1573621200000);
let dateRangeEndConverted = new Date(1582261140000);

Etapa 2: obter valores inteiros para os meses / anos

let startingMonth = dateRangeStartConverted.getMonth();
let startingYear = dateRangeStartConverted.getFullYear();
let endingMonth = dateRangeEndConverted.getMonth();
let endingYear = dateRangeEndConverted.getFullYear();

Isso nos dá

  • Mês inicial: 11
  • Ano de início: 2019
  • Mês final: 2
  • Ano final: 2020

Etapa 3: adicione (12 * (endYear - startYear)) + 1ao mês final.

  • Isso faz com que o nosso mês inicial fique em 11
  • Isso torna o mês final igual a 15 2 + (12 * (2020 - 2019)) + 1 = 15

Etapa 4: subtrair os meses

15 - 11 = 4; nós obtemos nosso resultado de 4 meses.

Exemplo de 29 meses

De novembro de 2019 a março de 2022, são 29 meses. Se você as colocar em uma planilha do Excel, verá 29 linhas.

  • Nosso mês inicial é 11
  • Nosso mês final é 40 3 + (12 * (2022-2019)) + 1

40 - 11 = 29


-2
anyVar = (((DisplayTo.getFullYear() * 12) + DisplayTo.getMonth()) - ((DisplayFrom.getFullYear() * 12) + DisplayFrom.getMonth()));

3
Embora esse snippet de código possa ser a solução, incluir uma explicação realmente ajuda a melhorar a qualidade da sua postagem. Lembre-se de que você está respondendo à pergunta dos leitores no futuro e essas pessoas podem não saber os motivos da sua sugestão de código.
Eugene Podskal 01/08/19

Não adianta postar uma resposta que dependa de funções que não sejam explicadas ou definidas na resposta.
RobG 5/08/19

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.