Diferencia en meses entre dos fechas en JavaScript


135

¿Cómo resolvería la diferencia para dos objetos Date () en JavaScript, mientras solo devuelvo el número de meses en la diferencia?

Cualquier ayuda sería genial :)


Un mes no es una unidad de medida muy precisa porque la duración del mes cambia dependiendo de qué mes es. Si un intervalo dura 30 días entre enero y febrero, es menos de 1 mes si piensa en términos de un mes de 31 días, pero más de 1 mes si considera los 28 o 29 días de febrero.
Mark Byers

77
Pregunta no muy bien definida. ¿Es el 28 de febrero 23:58 al 1 de marzo 00:01 un mes? ¿O solo un día? ¿O solo tres minutos? O los tres?
Thilo

1
@Thilo Alguien que necesite implementar esto probablemente no tendrá respuestas, ya que su gerente no tiene idea de lo que están preguntando en primer lugar. :)
AlbertEngelB

Respuestas:


229

La definición de "el número de meses en la diferencia" está sujeta a mucha interpretación. :-)

Puede obtener el año, mes y día del mes a partir de un objeto de fecha de JavaScript. Dependiendo de la información que esté buscando, puede usarlos para calcular cuántos meses hay entre dos puntos en el tiempo.

Por ejemplo, fuera de lugar:

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

(Tenga en cuenta que los valores de mes en JavaScript comienzan con 0 = enero).

Incluir meses fraccionarios en lo anterior es mucho más complicado, porque tres días en un febrero típico es una fracción mayor de ese mes (~ 10.714%) que tres días en agosto (~ 9.677%), y por supuesto, incluso febrero es un objetivo móvil dependiendo de si es un año bisiesto.

También hay algunas bibliotecas de fecha y hora disponibles para JavaScript que probablemente faciliten este tipo de cosas.


Nota : Solía ​​haber un + 1en lo anterior, aquí:

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

Eso es porque originalmente dije:

... esto descubre cuántos meses completos hay entre dos fechas, sin contar los meses parciales (por ejemplo, excluyendo el mes en que se encuentra cada fecha).

Lo he eliminado por dos razones:

  1. Sin contar los meses parciales resulta que no es lo que quieren (¿la mayoría?) Las personas que llegan a la respuesta, así que pensé que debería separarlos.

  2. No siempre funcionó incluso con esa definición. :-D (lo siento)


82

Si no considera el día del mes, esta es la solución más simple.

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

Tenga en cuenta que el índice mensual está basado en 0. Esto significa que January = 0y December = 11.


11
¡Este es el código más corto y fácil de entender que había visto hasta ahora!
Omar

No hay nada complicado con este, excepto el hecho de que cada mes que comienza se cuenta. 31/03/2011 -> 01/05/2011 serán dos meses, así como 01/03/2011 -> 31/05/2011 pero deben ser tres para ser exactos.
Natim

¡Si solo quieres saber la cantidad de meses, este es PERFECTO! Si necesita saber según los días del mes, esto no funcionará.
OSDM

1
Comience por el hecho de que 2018/01 y 2017/01 están separados por 12 meses. Trabaja al revés desde allí. Esta respuesta lo entiende. ;)
Gerard ONeill

1
¡Gracias! Esta función es muy útil para mí
Trương Long

29

A veces es posible que desee obtener solo la cantidad de meses entre dos fechas ignorando totalmente la parte del día. Entonces, por ejemplo, si tuvo dos fechas: 2013/06/21 y 2013/10 / 18- y solo le importaron las partes 2013/06 y 2013/10, estos son los escenarios y las posibles soluciones:

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.Si desea solo el número de meses entre las dos fechas, excluyendo ambos mes1 y mes2

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

2.Si quieres incluir alguno de los meses

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

3.Si quieres incluir los dos meses

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

¡Buena esa! Funcionó perfecto para mí.

3
Para el último, recomendaré: numberOfMonths=(year2-year1)*12+(month2-month1)+1;que hace lo mismo pero es más semánticamente correcto para mí
Natim

Sí, un enfoque agradable y más elegante.
Mikayil Abdullayev

Esta fue la mejor respuesta. ¡Gracias!
marienke

Esto es simple y limpio. Gracias por escribir un gran código.
ceros y unos

28

Aquí hay una función que proporciona con precisión el número de meses entre 2 fechas.
El comportamiento predeterminado solo cuenta meses completos, por ejemplo, 3 meses y 1 día dará como resultado una diferencia de 3 meses. Puede evitar esto configurando el parámetro roundUpFractionalMonthscomo true, por lo que una diferencia de 3 meses y 1 día se devolverá como 4 meses.

La respuesta aceptada arriba (respuesta de TJ Crowder) no es precisa, a veces devuelve valores incorrectos.

Por ejemplo, monthDiff(new Date('Jul 01, 2015'), new Date('Aug 05, 2015'))devuelve0 que obviamente es incorrecto. La diferencia correcta es 1 mes completo o 2 meses redondeados.

Aquí está la función que escribí:

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

66
En serio, ¿quién votó en contra? Tiene razón, la respuesta aceptada es simplemente ... incorrecta.
Michael Blackburn

2
Esta es la respuesta más adecuada.
Kalyan Chavali

Esto no está bien si digamos que date1 es el 22 de enero y date2 es el 22 de agosto. Luego devuelve 7, que obviamente debería ser 8?
Nicolai Harbo

haz esta respuesta aceptada. Esta más precisa que la aceptada. Por ejemplo, la respuesta aceptada no calcula los días
Munkhdelger Tumenbayar

La respuesta aceptada de Crowder se ha actualizado desde entonces. Todavía puede haber diferencias importantes entre este y esta respuesta, pero, a partir de este escrito, las preocupaciones enumeradas aquí se han abordado.
clozach

23

Si necesita contar meses completos, independientemente de que el mes sea 28, 29, 30 o 31 días. A continuación debería funcionar.

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

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

Esta es una versión extendida de la respuesta https://stackoverflow.com/a/4312956/1987208, pero corrige el caso en el que calcula 1 mes para el caso del 31 de enero al 1 de febrero (1 día).

Esto cubrirá lo siguiente;

  • 1 de enero al 31 de enero ---> 30 días ---> dará como resultado 0 (lógico ya que no es un mes completo)
  • 1 de febrero al 1 de marzo ---> 28 o 29 días ---> dará como resultado 1 (lógico ya que es un mes completo)
  • 15 de febrero al 15 de marzo ---> 28 o 29 días ---> dará como resultado 1 (lógico desde que pasó un mes)
  • 31 de enero al 1 de febrero ---> 1 día ---> dará como resultado 0 (obvio, pero la respuesta mencionada en la publicación da como resultado 1 mes)

2
Eso es lo que pensé que vine aquí para verificación y esa es la única respuesta que tiene sentido para mí
Andreas, el

2
Esto no funciona cuando se comparan dos meses donde el mes es más corto. Por ejemplo, del 31 de marzo al 30 de abril. Eso es todo abril, así que la respuesta debería ser "1".
Michael Blackburn

7

Diferencia en meses entre dos fechas en 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)

meses totales entre fecha_inicio y fecha_finalización:

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

6

Sé que esto es muy tarde, pero publicarlo de todos modos en caso de que ayude a otros. Aquí hay una función que se me ocurrió que parece hacer un buen trabajo al contar las diferencias en meses entre dos fechas. Es cierto que es mucho más atrevido que Mr.Crowder, pero proporciona resultados más precisos al pasar por el objeto de fecha. Está en AS3, pero debería poder soltar la escritura fuerte y tendrá JS. ¡Siéntase libre de hacer que se vea mejor a cualquiera!

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

Este es el enfoque correcto más probable para la mayoría de las situaciones. Hay tantas excepciones y caprichos en nuestro calendario, el mejor enfoque es delegar su manejo a una biblioteca bien probada, como Date (). Para la mayoría de los períodos (cortos), esto devolverá la misma respuesta que los enfoques calculados, pero cuando se trata de períodos de tiempo largos (incluidos los años bisiestos, años bisiestos que no son años bisiestos, bisiestos- segundos, etc.) es más probable que se encuentre con una excepción en la que restar meses y agregar años será incorrecto. Vea mi respuesta para excepciones cuando el cálculo sería mejor.
Michael Blackburn

5

Para ampliar la respuesta de @ TJ, si está buscando meses simples, en lugar de meses calendario completos, puede verificar si la fecha de d2 es mayor o igual que d1. Es decir, si d2 es más tarde en su mes que d1 en su mes, entonces hay 1 mes más. Entonces deberías poder hacer esto:

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

Esto no tiene en cuenta los problemas de tiempo (por ejemplo, el 3 de marzo a las 4:00 p.m. y el 3 de abril a las 3:00 p.m.), pero es más preciso y solo tiene un par de líneas de código.


5

Considere cada fecha en términos de meses, luego reste para encontrar la diferencia.

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

Esto le dará la diferencia de meses entre las dos fechas, ignorando los días.


4

Hay dos enfoques, matemático y rápido, pero sujeto a caprichos en el calendario, o iterativo y lento, pero maneja todas las rarezas (o al menos los delegados las manejan en una biblioteca bien probada).

Si itera por el calendario, incremente la fecha de inicio en un mes y vea si pasamos la fecha de finalización. Esto delega el manejo de anomalías a las clases integradas Date (), pero podría ser lento SI está haciendo esto por un gran número de fechas. La respuesta de James toma este enfoque. Por mucho que no me guste la idea, creo que este es el enfoque "más seguro", y si solo está haciendo un cálculo, la diferencia de rendimiento es realmente insignificante. Tendemos a tratar de optimizar las tareas que solo se realizarán una vez.

Ahora, si está calculando esta función en un conjunto de datos, probablemente no quiera ejecutar esa función en cada fila (o Dios no lo quiera, varias veces por registro). En ese caso, puede usar casi cualquiera de las otras respuestas aquí excepto la respuesta aceptada, que es simplemente incorrecta (la diferencia entre new Date()y new Date()es -1).

Aquí está mi apuesta por un enfoque matemático y rápido, que representa diferentes duraciones de meses y años bisiestos. Realmente solo debería usar una función como esta si la aplicará a un conjunto de datos (haciendo este cálculo una y otra vez). Si solo necesita hacerlo una vez, use el enfoque iterativo de James anterior, ya que está delegando el manejo de todas las (muchas) excepciones al 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

Aquí tienes otro enfoque con menos bucles:

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
tenga cuidado con este método: (obviamente, cuando lo lee con cuidado) muta la primera fecha que se devuelve a la persona que llama (ya que las fechas se pasan por referencia).
Andiih

3

También podría considerar esta solución, esto functiondevuelve la diferencia mensual en número entero o número

Pasar la fecha de inicio como la primera o la última parames tolerante a fallas. Es decir, la función aún devolvería el mismo 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);


Debe multiplicar por 365.25 para tener en cuenta los años bisiestos
darryn.ten

2

Calcular la diferencia entre dos fechas incluye fracción de mes (días).


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

Por ejemplo:
fecha1 : 24/09/2015 (24 de septiembre de 2015)
fecha2 : 11/09/2015 (9 de noviembre de 2015)
la diferencia: 2.5 (meses)


2

Esto debería funcionar bien:

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

El siguiente código devuelve meses completos entre dos fechas teniendo en cuenta también el número de días de meses parciales.

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

debajo de la lógica obtendrá la diferencia en 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

Mira lo que 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

También cuenta los días y los convierte en 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 

¿Puedes agregar una explicación más detallada de cómo funciona esto? Esto mejoraría la respuesta
serge1peshcoff

¿Hay una suposición que los meses tienen 30 días?
Ben Taliadoros

0

Número de meses cuando el día y la hora no importan

En este caso, no me preocupan los meses completos, los meses parciales, la duración de un mes, etc. Solo necesito saber la cantidad de meses. Un caso relevante en el mundo real sería cuando se debe presentar un informe cada mes, y necesito saber cuántos informes debería haber.

Ejemplo:

  • Enero = 1 mes
  • Enero - febrero = 2 meses
  • Noviembre - enero = 3 meses

Este es un ejemplo de código elaborado para mostrar a dónde van los números.

Tomemos 2 marcas de tiempo que deberían resultar en 4 meses

  • Marca de tiempo del 13 de noviembre de 2019: 1573621200000
  • 20 de febrero, fecha y hora de 2020: 1582261140000

Puede ser ligeramente diferente con su zona horaria / hora extraída. El día, los minutos y los segundos no importan y pueden incluirse en la marca de tiempo, pero lo ignoraremos con nuestro cálculo real.

Paso 1: convierta la marca de tiempo a una fecha de JavaScript

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

Paso 2: obtenga valores enteros para los meses / años

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

Esto nos da

  • Mes de inicio: 11
  • Año de inicio: 2019
  • Mes de finalización: 2
  • Año final: 2020

Paso 3: Agregar (12 * (endYear - startYear)) + 1al mes final.

  • Esto hace que nuestro mes de inicio permanezca a las 11
  • Esto hace que nuestro mes final sea igual a 15 2 + (12 * (2020 - 2019)) + 1 = 15

Paso 4: resta los meses

15 - 11 = 4; Obtenemos nuestro resultado de 4 meses.

Ejemplo de ejemplo de 29 meses

Noviembre de 2019 hasta marzo de 2022 es de 29 meses. Si los coloca en una hoja de cálculo de Excel, verá 29 filas.

  • Nuestro mes de inicio es 11
  • Nuestro mes final es 40 3 + (12 * (2022-2019)) + 1

40-11 = 29


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

3
Si bien este fragmento de código puede ser la solución, incluir una explicación realmente ayuda a mejorar la calidad de su publicación. Recuerde que está respondiendo la pregunta para los lectores en el futuro, y que esas personas podrían no conocer los motivos de su sugerencia de código.
Eugene Podskal

No sirve de mucho publicar una respuesta que depende de funciones que no se explican o definen en la respuesta.
RobG

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.