Fonction JavaScript pour ajouter X mois à une date

209

Je recherche le moyen le plus simple et le plus propre d'ajouter X mois à une date JavaScript.

Je préfère ne pas gérer le roulement de l'année ou devoir écrire ma propre fonction .

Y a-t-il quelque chose de intégré qui peut faire cela?

Tchad
la source
4
Essayez d'ajouter une méthode à l'objet prototype de la date, comme ceci --------------- Date.prototype.addMonth = function (n) {return new Date (this.setMonth (this.getMonth ( ) + n)); };
Moises Hidalgo
1
@ kr37, la réponse de Moises Hidalgo ne fonctionnera pas correctement si le mois cible n'a pas le numéro du jour d'aujourd'hui. La réponse de bmpasini gère également cela
Alexandru Severin
Les réponses ici ne sont pas très bonnes, de meilleures réponses sont à l' ajout de mois à une date en JavaScript .
RobG

Réponses:

278

La fonction suivante ajoute des mois à une date en JavaScript ( source ). Il prend en compte les reports d'année et les durées de mois variables:

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 solution ci-dessus couvre le cas de bord de passer d'un mois avec un nombre de jours supérieur au mois de destination. par exemple.

  • Ajouter douze mois au 29 février 2020 (devrait être le 28 février 2021)
  • Ajouter un mois au 31 août 2020 (devrait être le 30 septembre 2020)

Si le jour du mois change lors de la demande setMonth, nous savons que nous avons débordé le mois suivant en raison d'une différence de durée. Dans ce cas, nous utilisons setDate(0)pour revenir au dernier jour du mois précédent.

Remarque: cette version de cette réponse remplace une version antérieure (ci-dessous) qui ne gérait pas correctement les différentes durées de mois.

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);
Tchad
la source
130
Attention - cela ne fonctionne pas pour les cas marginaux, comme l'ajout au 31e jour de la plupart des mois. Par exemple, le 31 octobre 2011 + 1 mois en utilisant cette méthode est le 1er décembre 2011 en utilisant l'objet Date standard de Javascript.
TAD
6
Bonne prise, un peu. Je suis surpris de ne pas l'avoir vu. Notez que le T-SQL select DATEADD (month, 1, '2011-10-31') donne '2011-11-30', ce qui pour moi est raisonnable. J'ai obtenu 10 votes avec une mauvaise réponse. Cool. :-)
Chad
2
Surveillez également les années bissextiles - ajouter 1 an au 29 février dans une année bissextile vous donnera des résultats imprévisibles en fonction de la langue que vous utilisez (comme réponse générale). C'est ce qui a fait tomber la plateforme cloud Microsoft Azure pendant plusieurs heures en 2012
Ben Walding
15
Il y a une addMonthsfonction ici qui respecte la fin du mois (par exemple 2012-01-31 + 1 month = 2012-02-29, etc.).
RobG
3
il suffit d'ajouter pour définir la 1ère date de chaque mois: today.setHours (0, 0, 0, 0); today.setDate (1);
Alexey Strakh
56

J'utilise moment.js bibliothèque pour des manipulations de date-heure . Exemple de code à ajouter un mois:

var startDate = new Date(...);
var endDateMoment = moment(startDate); // moment(...) can also be used to parse dates in string format
endDateMoment.add(1, 'months');
anre
la source
12
Pour votre propre raison, utilisez moment.js.
Gaʀʀʏ
3
Tout à fait d'accord avec @garry: utilisez moment.js.
Michel
2
Je suis d'accord pour dire que c'est la manière la plus propre, mais la question était "Y a-t-il quelque chose de construit qui peut faire ça?" Moment ajoute des dizaines de Ko à la taille de la page
Evgeny
C'était bien. Aucun autre plugin que j'ai trouvé sur le net ne peut faire beaucoup mieux que cela.
Eraniichan
26

Cette fonction gère les cas marginaux et est rapide:

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
}

tester:

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

Mise à jour pour les dates non UTC: (par 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
}

tester:

> 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
la source
La seule réponse raisonnable ici. Mais il faut faire attention aux dates non UTC. Cela pourrait apporter des résultats inattendus (par exemple 31 janv. + 1 mois = 1 mars pour UTC + 1 fuseau horaire à minuit)
Antony Hatchkins
1
Un autre problème est qu'il modifie à la fois la date d'origine et renvoie la valeur modifiée. Peut-être qu'il est logique de l'ajouter au prototype de date ou d'utiliser une variable locale pour la date modifiée dans la fonction?
Antony Hatchkins
Merci Antony, une variable locale a du sens.
aMarCruz
1
Je suggérerais de supprimer la ligne "date de retour" ou d'ajouter une variable locale.
Antony Hatchkins
Mis à jour à l'aide de variables locales et de tests séparés.
aMarCruz
12

Étant donné qu'aucune de ces réponses ne tiendra compte de l'année en cours lorsque le mois change, vous pouvez en trouver une que j'ai faite ci-dessous qui devrait la gérer:

La méthode:

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

Usage:

return new Date().addMonths(2);
Control Freak
la source
Cette réponse ne tient pas compte de l'ajout de mois s'il n'y a pas de date correspondante dans le mois résultant (par exemple, 31 janvier + 1 mois => 2 ou 3 mars). Il semble également qu'il y ait un problème avec l'ajout de plus de 12 mois à une date: il n'y en a pas. Il n'est pas nécessaire d'ajouter, disons, 13 mois, 1 an et 1 mois. Ajoutez juste 13 mois.
RobG
9

Tiré des réponses @bmpsini et @Jazaret , mais sans étendre les prototypes: utiliser des fonctions simples ( Pourquoi étendre des objets natifs est-il une mauvaise pratique? ):

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

Utilise le:

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

D'après les réponses ci-dessus, le seul qui gère les cas marginaux (bmpasini de la bibliothèque datejs) a un problème:

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 mais:

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

pire :

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"

Cela est dû au fait que l'heure n'est pas réglée, revenant ainsi à 00:00:00, ce qui peut alors basculer vers la veille en raison de fuseaux horaires ou de changements de temps ou autre ...

Voici ma solution proposée, qui n'a pas ce problème, et est aussi, je pense, plus élégante en ce qu'elle ne repose pas sur des valeurs codées en dur.

/**
* @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é testée avec succès avec:

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
la source
3

Comme la plupart des réponses mis en évidence, nous pourrions utiliser setMonth () méthode avec getMonth () méthode pour ajouter un nombre spécifique de mois à une date donnée.

Exemple: (comme mentionné par @ChadD dans sa réponse.)

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

Mais nous devons utiliser cette solution avec soin car nous aurons des problèmes avec les boîtiers de bord.

Pour gérer les cas marginaux, la réponse donnée dans le lien suivant est utile.

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

Sampath Dilhan
la source
3

Solution simple: 2678400000est de 31 jours en millisecondes

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

Mettre à jour:

Utilisez ces données pour créer notre propre fonction:

  • 2678400000 - 31 jours
  • 2592000000 - 30 jours
  • 2505600000 - 29 jours
  • 2419200000 - 28 jours
dr.dimitru
la source
8
Certains mois n'ont pas 31 jours
Alexandru Severin
D'accord, mais vous pouvez facilement ajuster ou écrire la fonction
dr.dimitru
2
Je suppose que ce que les gens recherchent, c'est cette fonction même qui prend automatiquement en compte les jours et les mois.
Alexander Bird
@AlexanderBird utilise ensuite un outil pare-balles créé par quelqu'un d'autre, comme moment.js
dr.dimitru
2
Cette réponse ne prend pas en charge l'heure d'été, où tous les jours ne durent pas 24 heures (ou 8,64e7 millisecondes).
RobG
2
d = new Date();

alert(d.getMonth()+1);

Les mois ont un index basé sur 0, il devrait alerter (4) qui est 5 (mai);

Ben
la source
Cette méthode pourrait renvoyer une valeur <0 et supérieure à 12
Patrick
2

Juste pour ajouter à la réponse acceptée et aux commentaires.

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
la source
2
Lorsque vous définissez la date (31) sur "1 février", vous obtenez "3 mars". C'est vraiment ce que tu veux?
Antony Hatchkins
2

J'ai écrit cette solution alternative qui fonctionne bien pour moi. Il est utile lorsque vous souhaitez calculer la fin d'un contrat. Par exemple, début = 2016-01-15, mois = 6, fin = 2016-7-14 (c'est-à-dire dernier jour - 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>

Exemples:

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
la source
0

Voici un exemple de calcul d'une date future basée sur la saisie de la date (membershipssignup_date) + mois ajoutés (membershipsmonths) via les champs du formulaire.

Le champ membershipsmonths a une valeur par défaut de 0

Lien de déclenchement (peut être un événement onchange attaché au champ de terme d'adhésion):

<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
la source
0

Comme le démontrent de nombreuses réponses complexes et laides présentées, les dates et les heures peuvent être un cauchemar pour les programmeurs utilisant n'importe quel langage. Mon approche consiste à convertir les dates et les valeurs «delta t» en Epoch Time (en ms), effectuer n'importe quelle arithmétique, puis reconvertir en «temps humain».

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

Cela a été écrit pour un cas d'utilisation légèrement différent, mais vous devriez pouvoir l'adapter facilement aux tâches connexes.

EDIT: Source complète ici !

Dan Ahlquist
la source
3
Réponse inutile, car elle ne gère pas les mois avec des nombres de jours différents, c'est la question.
Benubird
1
@Benubird - puisque vous l'avez demandé si poliment, j'ai téléchargé la source complète. Lien dans ce post.
Dan Ahlquist
Cela ne prend pas non plus en compte l'heure d'été, où tous les jours ne durent pas 24 heures (ou 8,64e7 millisecondes).
RobG
Non, non. DST est un problème de localisation.
Dan Ahlquist
0

Tout cela semble beaucoup trop compliqué et je suppose que cela entre dans un débat sur ce que signifie exactement "un mois". Cela signifie-t-il 30 jours? Cela signifie-t-il du 1er au 1er? Du dernier jour au dernier jour?

Si c'est le cas, l'ajout d'un mois au 27 février vous amène au 27 mars, mais l'ajout d'un mois au 28 février vous amène au 31 mars (sauf les années bissextiles, où cela vous mène au 28 mars). Ensuite, en soustrayant un mois du 30 mars, vous obtenez ... 27 février? Qui sait...

Pour ceux qui recherchent une solution simple, ajoutez simplement des millisecondes et terminez.

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
la source
J'ai l'impression qu'il est clair que "ajouter X mois" signifie que le mois augmente de X. Il est vrai que votre réponse a moins d'étapes algorithmiques à coder, mais malheureusement, je pense que la précision l'emporte sur la simplicité. Lorsque les gens chercheront une réponse, ils voudront que le «31 juillet 2016» devienne le «31 août 2016». Et cela ne ferait pas ça. D'ailleurs, si vous voulez vraiment la simplicité au détriment de la précision, pourquoi ne pas rester avec d.setMonth(d.getMonth()+1)? Ce n'est donc même pas l'idée la plus simple.
Alexander Bird
Cela ne prend pas non plus en compte l'heure d'été, où tous les jours ne durent pas 24 heures (ou 8,64e7 millisecondes).
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
la source
1
Je le trouve très verwirrend lorsque la plupart du code est en anglais et les variables en Deutsch. ;)
Bernhard Hofmann
-1

Parfois utile pour créer une date par un opérateur comme dans les paramètres BIRT

J'ai fait 1 mois de retour avec:

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

Comme indiqué ci-dessus, vous pouvez ajouter un mois pour Datefonctionner.

Venkatesh
la source