Función de JavaScript para agregar X meses a una fecha

209

Estoy buscando la forma más fácil y limpia de agregar X meses a una fecha de JavaScript.

Prefiero no manejar la renovación del año o tener que escribir mi propia función .

¿Hay algo incorporado que pueda hacer esto?

Chad
fuente
44
Intente agregar un método al objeto prototipo de la fecha, así --------------- Date.prototype.addMonth = function (n) {return new Date (this.setMonth (this.getMonth ( ) + n)); };
Moises Hidalgo
1
@ kr37, la respuesta de Moisés Hidalgo no funcionará correctamente si el mes objetivo no tiene el número de día de hoy. La respuesta de bmpasini también maneja esto
Alexandru Severin
Las respuestas aquí no son muy buenas, hay mejores respuestas en Agregar meses a una fecha en JavaScript .
RobG

Respuestas:

278

La siguiente función agrega meses a una fecha en JavaScript ( fuente ). Tiene en cuenta las acumulaciones anuales y la duración variable de los 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());

La solución anterior cubre el caso límite de pasar de un mes con un mayor número de días que el mes de destino. p.ej.

  • Agregue doce meses al 29 de febrero de 2020 (debe ser el 28 de febrero de 2021)
  • Agregue un mes al 31 de agosto de 2020 (debe ser el 30 de septiembre de 2020)

Si el día del mes cambia al momento de la solicitud setMonth, sabemos que hemos pasado al mes siguiente debido a una diferencia en la duración del mes. En este caso, usamos setDate(0)para regresar al último día del mes anterior.

Nota: esta versión de esta respuesta reemplaza una versión anterior (a continuación) que no manejó con gracia diferentes duraciones de mes.

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);
Chad
fuente
130
Cuidado: esto no funciona para casos extremos, como agregar al día 31 de la mayoría de los meses. Por ejemplo, el 31 de octubre de 2011 + 1 mes con este método es el 01 de diciembre de 2011 con el objeto Fecha estándar de Javascript.
tad
66
Buena captura, tad. Me sorprende que no haya visto eso. Tenga en cuenta que la selección T-SQL DATEADD (mes, 1, '2011-10-31') produce '2011-11-30', lo que para mí es razonable. Obtuve 10 votos con una mala respuesta. Frio. :-)
Chad
2
También observe los años bisiestos: agregar 1 año al 29 de febrero en un año bisiesto le dará resultados impredecibles dependiendo del idioma que esté usando (como respuesta general). Esto es lo que derribó la plataforma en la nube de Microsoft Azure durante varias horas en 2012
Ben Walding
15
Hay una addMonthsfunción aquí que respeta al final del mes (por ejemplo, 2012-01-31 + 1 month = 2012-02-29y así sucesivamente).
RobG
3
simplemente agregue para establecer la primera fecha de cada mes: today.setHours (0, 0, 0, 0); today.setDate (1);
Alexey Strakh
56

Estoy usando la biblioteca moment.js para manipulaciones de fecha y hora . Código de muestra para agregar un mes:

var startDate = new Date(...);
var endDateMoment = moment(startDate); // moment(...) can also be used to parse dates in string format
endDateMoment.add(1, 'months');
anre
fuente
12
Por su propia cordura, use moment.js.
Gaʀʀʏ
3
Absolutamente de acuerdo con @garry: use moment.js.
Michel
2
Estoy de acuerdo en que esta es la forma más limpia, pero la pregunta era "¿Hay algo integrado que pueda hacer esto?" Momento agrega docenas de Kb al tamaño de página
Evgeny
Esto fue bueno. No he encontrado otros complementos en la red que puedan hacer mucho mejor que esto.
Eraniichan
26

Esta función maneja casos extremos y es 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
}

prueba:

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

Actualización para fechas no 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
}

prueba:

> 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
fuente
La única respuesta razonable aquí. Pero se debe tener cuidado con las fechas no UTC. Podría traer resultados inesperados (por ejemplo, 31 de enero + 1 mes = 1 de marzo para UTC + 1 zona horaria medianoche)
Antony Hatchkins
1
Otro problema es que modifica la fecha original y devuelve el valor modificado. ¿Quizás tenga sentido agregarlo al prototipo de fecha o usar una variable local para la fecha cambiada en la función?
Antony Hatchkins
Gracias Antony, una variable local tiene sentido.
aMarCruz
1
Sugeriría eliminar la línea de "fecha de retorno" o agregar una var local.
Antony Hatchkins
Actualizado utilizando variables locales y pruebas separadas.
aMarCruz
12

Teniendo en cuenta que ninguna de estas respuestas tendrá en cuenta el año actual cuando cambia el mes, puede encontrar una que hice a continuación que debería manejarlo:

El 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);
Dominante
fuente
Esta respuesta no tiene en cuenta la adición de meses si no hay una fecha correspondiente en el mes resultante (por ejemplo, 31 de enero + 1 mes => 2 o 3 de marzo). También parece pensar que hay un problema al agregar más de 12 meses a una Fecha: no existe. No es necesario agregar, por ejemplo, 13 meses, agregar 1 año y 1 mes. Solo agrega 13 meses.
RobG
9

Tomado de las respuestas de @bmpsini y @Jazaret , pero sin extender los prototipos: usando funciones simples ( ¿Por qué extender objetos nativos es una mala práctica? ):

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

Úselo:

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

De las respuestas anteriores, el único que maneja los casos extremos (bmpasini de la biblioteca datejs) tiene un 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)

bien pero:

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

peor :

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"

Esto se debe a que no se configuró la hora, volviendo así a 00:00:00, que luego puede fallar al día anterior debido a cambios de zona horaria o de ahorro de tiempo o lo que sea ...

Aquí está mi solución propuesta, que no tiene ese problema, y ​​también es, creo, más elegante ya que no se basa en 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];
};

Unidad probada con éxito con:

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')
);
Pensamiento
fuente
3

Como la mayoría de las respuestas resaltaron, podríamos usar el método setMonth () junto con el método getMonth () para agregar un número específico de meses a una fecha determinada.

Ejemplo: (como lo mencionó @ChadD en su respuesta).

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

Pero debemos usar esta solución con cuidado, ya que tendremos problemas con los casos límite.

Para manejar casos extremos, la respuesta que se proporciona en el siguiente enlace es útil.

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

Sampath Dilhan
fuente
3

Solución simple: 2678400000es 31 días en milisegundos

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

Actualizar:

Use estos datos para construir nuestra propia función:

  • 2678400000 - 31 días
  • 2592000000 - 30 dias
  • 2505600000 - 29 días
  • 2419200000 - 28 días
dr.dimitru
fuente
8
Algunos meses no tienen 31 días
Alexandru Severin
De acuerdo, pero puede ajustar fácilmente o escribir la función
dr.dimitru
2
Supongo que lo que la gente está buscando es esa función que tiene en cuenta automáticamente días en meses.
Alexander Bird
@AlexanderBird luego usa una herramienta a prueba de balas creada por otra persona, como moment.js
dr.dimitru
2
Esta respuesta no se adapta al horario de verano, donde no todos los días duran 24 horas (o 8,64e7 milisegundos).
RobG
2
d = new Date();

alert(d.getMonth()+1);

Los meses tienen un índice basado en 0, debe alertar (4) que es 5 (mayo);

Ben
fuente
Este método podría devolver un valor <0 y mayor que 12
Patrick
2

Solo para agregar a la respuesta aceptada y los comentarios.

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
fuente
2
Cuando establece la fecha (31) en "1 feb", obtiene "3 mar". ¿Es lo que realmente quieres?
Antony Hatchkins
2

Escribí esta solución alternativa que me funciona bien. Es útil cuando desea calcular el final de un contrato. Por ejemplo, inicio = 2016-01-15, meses = 6, final = 2016-7-14 (es decir, último día - 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>

Ejemplos:

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
fuente
0

El siguiente es un ejemplo de cómo calcular una fecha futura con base en la entrada de fecha (membresía_registro_fecha) + meses agregados (membresía meses) a través de campos de formulario.

El campo membershipsmonths tiene un valor predeterminado de 0

Enlace de activación (puede ser un evento de cambio adjunto al campo de término de membresía):

<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
fuente
0

Como lo demuestran muchas de las respuestas complicadas y feas presentadas, Dates and Times puede ser una pesadilla para los programadores que usan cualquier lenguaje. Mi enfoque es convertir las fechas y los valores 'delta t' en Epoch Time (en ms), realizar cualquier aritmética y luego volver a convertirla en "tiempo humano".

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

Esto fue escrito para un caso de uso ligeramente diferente, pero debería poder adaptarlo fácilmente para tareas relacionadas.

EDITAR: fuente completa aquí !

Dan Ahlquist
fuente
3
Respuesta inútil, porque no maneja meses con diferentes números de días, que es la pregunta.
Benubird
1
@Benubird: como lo preguntaste tan cortésmente, he subido la fuente completa. Enlace en esta publicación.
Dan Ahlquist
Esto tampoco admite el horario de verano, ya que no todos los días duran 24 horas (o 8.64e7 milisegundos).
RobG
No lo hace El horario de verano es una preocupación de localización.
Dan Ahlquist
0

Todo esto parece demasiado complicado y supongo que entra en un debate sobre qué significa exactamente agregar "un mes". ¿Significa 30 días? ¿Significa del 1 al 1? ¿Desde el último día hasta el último día?

Si es esto último, agregar un mes al 27 de febrero lo lleva al 27 de marzo, pero agregar un mes al 28 de febrero lo lleva al 31 de marzo (excepto en los años bisiestos, donde lo lleva al 28 de marzo). Luego, restando un mes del 30 de marzo te da ... 27 de febrero? Quién sabe...

Para aquellos que buscan una solución simple, simplemente agregue milisegundos y listo.

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

o

Date.prototype.addDays = function(days) {
  this = new Date(this.getTime() + (days * 86400000));
};
jdforsythe
fuente
Siento que está claro que "agregar X meses" significa que el mes aumenta en X. Es cierto que su respuesta tiene menos pasos algorítmicos para codificar, pero desafortunadamente siento que la precisión supera la simplicidad. Cuando las personas buscan una respuesta, quieren que '31 de julio de 2016' se convierta en 'Augest 31 2016'. Y esto no haría eso. Además, si realmente quieres simplicidad a costa de la precisión, ¿por qué no te quedas d.setMonth(d.getMonth()+1)? Así que esta ni siquiera es la idea más simple.
Alexander Bird
Esto tampoco admite el horario de verano, donde no todos los días duran 24 horas (o 8.64e7 milisegundos).
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
fuente
1
Me parece muy verwirrend cuando la mayor parte del código está en inglés y las variables en alemán. ;)
Bernhard Hofmann
-1

A veces es útil crear una fecha por un operador como en los parámetros BIRT

Hice 1 mes de regreso con:

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

Como se indicó anteriormente, puede agregar un mes a la Datefunción.

Venkatesh
fuente