Funzione JavaScript per aggiungere X mesi a una data


209

Sto cercando il modo più semplice e pulito per aggiungere X mesi a una data JavaScript.

Preferirei non gestire il rollover dell'anno o scrivere la mia funzione .

C'è qualcosa che può fare questo?


4
Prova ad aggiungere un metodo all'oggetto prototipo della data, in questo modo --------------- Date.prototype.addMonth = function (n) {return new Date (this.setMonth (this.getMonth ( ) + n)); };
Moises Hidalgo,

1
@ kr37, la risposta di Moises Hidalgo non funzionerà correttamente se il mese target non ha il numero del giorno di oggi. Anche la risposta di bmpasini si occupa di questo
Alexandru Severin il

Le risposte qui non sono molto buone, le risposte migliori sono su Aggiunta di mesi a una data in JavaScript .
RobG

Risposte:


278

La seguente funzione aggiunge mesi a una data in JavaScript ( sorgente ). Tiene conto del roll-over dell'anno e della durata variabile dei mesi:

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 soluzione di cui sopra copre il caso limite di passare da un mese con un numero maggiore di giorni rispetto al mese di destinazione. per esempio.

  • Aggiungi dodici mesi al 29 febbraio 2020 (dovrebbe essere il 28 febbraio 2021)
  • Aggiungi un mese al 31 agosto 2020 (dovrebbe essere il 30 settembre 2020)

Se il giorno del mese cambia durante l'applicazione setMonth, allora sappiamo che abbiamo traboccato nel mese successivo a causa di una differenza nella lunghezza del mese. In questo caso, usiamo setDate(0)per tornare all'ultimo giorno del mese precedente.

Nota: questa versione di questa risposta sostituisce una versione precedente (di seguito) che non gestiva con grazia lunghezze di mese diverse.

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
Attenzione: questo non funziona per casi limite, come l'aggiunta al 31 ° giorno della maggior parte dei mesi. Ad esempio, il 31 ottobre 2011 + 1 mese usando questo metodo è il 01 dic 2011 usando l'oggetto Date standard di Javascript.
tad

6
Buona cattura, un po '. Sono sorpreso di non averlo visto. Nota che T-SQL seleziona DATEADD (mese, 1, "2011-10-31") produce "2011-11-30", il che per me è ragionevole. Ho ottenuto 10 voti positivi con una risposta negativa. Freddo. :-)
Ciad

2
Guarda anche gli anni bisestili - l'aggiunta di 1 anno al 29 febbraio in un anno bisestile ti darà risultati imprevedibili a seconda della lingua che stai utilizzando (come risposta generale). Questo è ciò che ha abbattuto la piattaforma cloud Microsoft Azure per diverse ore nel 2012
Ben Walding

15
C'è una addMonthsfunzione qui che rispetta la fine del mese (ad es. 2012-01-31 + 1 month = 2012-02-29E così via).
RobG

3
basta aggiungere per impostare la prima data di ogni mese: today.setHours (0, 0, 0, 0); today.setDate (1);
Alexey Strakh,

56

Sto usando la libreria moment.js per manipolazioni di data e ora . Codice di esempio da aggiungere un mese:

var startDate = new Date(...);
var endDateMoment = moment(startDate); // moment(...) can also be used to parse dates in string format
endDateMoment.add(1, 'months');

12
Per la tua sanità mentale, usa moment.js.
Gaʀʀʏ

3
Assolutamente d'accordo con @garry: usa moment.js.
Michel,

2
Sono d'accordo che questo è il modo più pulito, ma la domanda era: "C'è qualcosa di costruito in che può fare questo?" Moment aggiunge dozzine di Kb alle dimensioni della pagina
Evgeny,

È stato bello Nessun altro plugin che ho trovato in rete può fare molto meglio di così.
Eraniichan,

26

Questa funzione gestisce i casi limite ed è veloce:

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
}

test:

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

Aggiornamento per date non UTC: (di 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
}

test:

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

L'unica risposta ragionevole qui. Ma bisogna fare attenzione alle date non UTC. Potrebbe portare a risultati inaspettati (ad es. 31 gennaio + 1 mese = 1 marzo per UTC + 1 fuso orario mezzanotte)
Antony Hatchkins,

1
Un altro problema è che entrambi modifica la data originale e restituisce il valore modificato. Forse ha senso aggiungerlo al prototipo della data o utilizzare una variabile locale per la data modificata nella funzione?
Antony Hatchkins,

Grazie Antonio, una variabile locale ha un senso.
aMarCruz,

1
Suggerirei di rimuovere la riga "data di ritorno" o di aggiungere una var locale.
Antony Hatchkins,

Aggiornato utilizzando variabili locali e test separati.
aMarCruz,

12

Considerando che nessuna di queste risposte rappresenterà l'anno corrente in cui il mese cambia, puoi trovare quello che ho fatto di seguito che dovrebbe gestirlo:

Il metodo:

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

Questa risposta non tiene conto dell'aggiunta di mesi in assenza di una data corrispondente nel mese risultante (ad es. 31 gennaio + 1 mese => 2 o 3 marzo). Sembra anche che ci sia un problema con l'aggiunta di più di 12 mesi a una data: non c'è. Non è necessario aggiungere, diciamo, 13 mesi per aggiungere 1 anno e 1 mese. Aggiungi solo 13 mesi.
RobG

9

Tratto dalle risposte di @bmpsini e @Jazaret , ma senza estendere i prototipi: usare funzioni semplici ( Perché l'estensione degli oggetti nativi è una cattiva pratica? ):

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

Usalo:

var nextMonth = addMonths(new Date(), 1);

4

Dalle risposte sopra, l'unico che gestisce i casi limite (la libreria bmpasini di datejs) ha 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)

ok ma:

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

peggio :

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"

Ciò è dovuto al fatto che l'ora non è stata impostata, ripristinando quindi le 00:00:00, che possono quindi presentare anomalie al giorno precedente a causa del fuso orario o delle modifiche che fanno risparmiare tempo o altro ...

Ecco la mia soluzione proposta, che non presenta questo problema ed è anche, a mio avviso, più elegante in quanto non si basa su valori codificati.

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

Unità testata con successo 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')
);

3

Come la maggior parte delle risposte ha evidenziato, potremmo usare il metodo setMonth () insieme a getMonth () per aggiungere un numero specifico di mesi a una determinata data.

Esempio: (come menzionato da @ChadD nella sua risposta.)

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

Ma dovremmo usare attentamente questa soluzione poiché avremo problemi con i casi limite.

Per gestire i casi limite, è utile la risposta fornita nel seguente link.

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


3

Soluzione semplice: 2678400000è di 31 giorni in millisecondi

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

Aggiornare:

Usa questi dati per costruire la nostra funzione:

  • 2678400000 - 31 giorni
  • 2592000000 - 30 giorni
  • 2505600000 - 29 giorni
  • 2419200000 - 28 giorni

8
Alcuni mesi non hanno 31 giorni
Alexandru Severin,

Accetto, ma puoi facilmente regolare o scrivere la funzione
dr.dimitru

2
Immagino che ciò che la gente sta cercando sia proprio quella funzione che tiene conto automagicamente di giorni e mesi.
Alexander Bird,

@AlexanderBird quindi utilizza uno strumento antiproiettile creato da qualcun altro, come moment.js
dr.dimitru,

2
Questa risposta non è adatta all'ora legale, dove non tutti i giorni durano 24 ore (o 8,64e7 millisecondi).
RobG

2
d = new Date();

alert(d.getMonth()+1);

I mesi hanno un indice basato su 0, dovrebbe avvisare (4) che è 5 (maggio);


Questo metodo potrebbe restituire un valore <0 e maggiore di 12
Patrick

2

Solo per aggiungere la risposta accettata e i commenti.

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
Quando imposti la Data (31) su "1 febbraio" ottieni "3 Mar". È quello che vuoi davvero?
Antony Hatchkins,

2

Ho scritto questa soluzione alternativa che funziona bene per me. È utile quando si desidera calcolare la fine di un contratto. Ad esempio, inizio = 2016-01-15, mesi = 6, fine = 2016-7-14 (ovvero ultimo giorno - 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>

Esempi:

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

Di seguito è riportato un esempio di come calcolare una data futura in base all'immissione della data (membershipssignup_date) + mesi aggiunti (membershipsmonths) tramite i campi del modulo.

Il campo membershipsmonths ha un valore predefinito di 0

Trigger link (può essere un evento onchange associato al campo del termine dell'iscrizione):

<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

Come dimostrato da molte delle risposte complicate e brutte presentate, Date e Times possono essere un incubo per i programmatori che usano qualsiasi lingua. Il mio approccio è convertire le date e i valori 'delta t' in Epoch Time (in ms), eseguire qualsiasi aritmetica, quindi riconvertire in "tempo umano".

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

Questo è stato scritto per un caso d'uso leggermente diverso, ma dovresti essere in grado di adattarlo facilmente alle attività correlate.

EDIT: fonte completa qui !


3
Risposta inutile, perché non gestisce mesi con diversi numeri di giorni, che è la domanda.
Benubird,

1
@Benubird - da quando lo hai chiesto in modo educato, ho caricato la fonte completa. Link in questo post.
Dan Ahlquist,

Anche questo non è adatto all'ora legale, dove non tutti i giorni durano 24 ore (o 8,64e7 millisecondi).
RobG

No, non lo fa. L'ora legale è un problema di localizzazione.
Dan Ahlquist,

0

Tutto ciò sembra troppo complicato e immagino che entri in un dibattito su cosa significhi esattamente aggiungere "un mese". Significa 30 giorni? Significa dal 1 ° al 1 °? Dall'ultimo giorno all'ultimo giorno?

Se quest'ultimo, l'aggiunta di un mese al 27 febbraio ti porta al 27 marzo, ma l'aggiunta di un mese al 28 febbraio ti porta al 31 marzo (tranne negli anni bisestili, dove ti porta al 28 marzo). Quindi sottraendo un mese dal 30 marzo si ottiene ... 27 febbraio? Chissà...

Per coloro che cercano una soluzione semplice, basta aggiungere millisecondi ed essere fatto.

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

o

Date.prototype.addDays = function(days) {
  this = new Date(this.getTime() + (days * 86400000));
};

Sento che è chiaro che "l'aggiunta di X mesi" significa che il mese aumenta di X. È vero che la tua risposta ha meno passaggi algoritmici da codificare, ma sfortunatamente sento che l'accuratezza supera la semplicità. Quando le persone cercano una risposta, vorranno che il "31 luglio 2016" diventi il ​​"31 agosto 2016". E questo non lo farebbe. Inoltre, se vuoi davvero la semplicità a costo dell'accuratezza, perché non attenersi d.setMonth(d.getMonth()+1)? Quindi questa non è nemmeno l'idea più semplice.
Alexander Bird,

Anche questo non è adatto all'ora legale, dove non tutti i giorni durano 24 ore (o 8,64e7 millisecondi).
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
Lo trovo molto verwirrend quando la maggior parte del codice è in inglese e le variabili in Deutsch. ;)
Bernhard Hofmann,

-1

A volte utile creare data da un operatore come nei parametri BIRT

Sono tornato indietro di 1 mese con:

new Date(new Date().setMonth(new Date().getMonth()-1));   

-3
var a=new Date();
a.setDate(a.getDate()+5);

Come sopra indicato, è possibile aggiungere un mese alla Datefunzione.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.