%MONTHS
Durée en mois
Représente un nombre de mois, à ajouter à une date ou à un horodatage, ou à en retrancher.
Syntaxe
date + %MONTHS(nombre)
date - %MONTHS(nombre)
nombre- Le nombre de mois : une constante, une zone numérique ou une expression. Un nombre négatif fait reculer.
- Résultat
- Une durée, à combiner avec une date ou un horodatage par
+ou-. Le jour du mois est conservé, sauf s'il n'existe pas dans le mois d'arrivée : on prend alors le dernier jour de ce mois.
À quoi ça sert
Un contrat de 18 mois, un abonnement trimestriel, un préavis de 3 mois : ajouter 30 jours ne convient pas, puisque les mois n'ont pas la même longueur. %MONTHS garde le même quantième d'un mois à l'autre.
Exemple 1 : Contrat et préavis
**free
// %MONTHS : ajouter ou retirer des mois (echeance de contrat, abonnement...).
ctl-opt dftactgrp(*no) actgrp(*new);
dcl-s signature date inz(d'2026-09-15');
dcl-s fin date;
dcl-s duree int(10) inz(18);
dcl-s stamp timestamp inz(z'2026-09-15-09.30.00.000000');
// Contrat de 18 mois.
fin = signature + %months(duree);
snd-msg 'Fin du contrat : [' + %char(fin : *eur) + ']';
// Renouvellement annonce 3 mois avant la fin.
snd-msg 'Preavis : [' + %char(fin - %months(3) : *eur) + ']';
// Un horodatage garde son heure.
snd-msg 'Horodatage + 1 mois : [' + %char(stamp + %months(1)) + ']';
// Mois et jours se combinent, de gauche a droite.
snd-msg '+ 1 mois + 10 j : [' + %char(signature + %months(1) + %days(10) : *eur) + ']';
*inlr = *on;
Résultat réel, compilé et exécuté sur IBM i 7.5 :
Fin du contrat : [15.03.2028]
Preavis : [15.12.2027]
Horodatage + 1 mois : [2026-10-15-09.30.00.000000]
+ 1 mois + 10 j : [25.10.2026]
Le 15 septembre 2026 plus 18 mois donne le 15 mars 2028. On retire 3 mois pour la date de préavis. Sur un horodatage, l'heure est inchangée. Mois et jours s'enchaînent dans une même expression : un mois puis dix jours.
Exemple 2 : Fin de mois
**free
// %MONTHS : le 31 janvier + 1 mois donne le 28 fevrier, et l'erreur se propage.
ctl-opt dftactgrp(*no) actgrp(*new);
dcl-s depart date inz(d'2026-01-31');
dcl-s suivant date;
dcl-s i int(10);
snd-msg '31/01/2026 + 1 mois : [' + %char(depart + %months(1) : *eur) + ']';
snd-msg '31/01/2026 + 2 mois : [' + %char(depart + %months(2) : *eur) + ']';
snd-msg '31/01/2028 + 1 mois : [' + %char(d'2028-01-31' + %months(1) : *eur) + ']';
// Ajouter puis retrancher un mois ne ramene pas a la date de depart.
snd-msg 'Aller-retour : [' + %char(depart + %months(1) - %months(1) : *eur) + ']';
// Echeancier mensuel, methode fausse : on repart de la date precedente.
suivant = depart;
for i = 1 to 4;
suivant = suivant + %months(1);
snd-msg 'En cascade ' + %char(i) + ' : ' + %char(suivant : *eur);
endfor;
// Methode juste : toujours partir de la date d'origine.
for i = 1 to 4;
suivant = depart + %months(i);
snd-msg 'Depuis l''origine ' + %char(i) + ' : ' + %char(suivant : *eur);
endfor;
*inlr = *on;
Résultat réel, compilé et exécuté sur IBM i 7.5 :
31/01/2026 + 1 mois : [28.02.2026]
31/01/2026 + 2 mois : [31.03.2026]
31/01/2028 + 1 mois : [29.02.2028]
Aller-retour : [28.01.2026]
En cascade 1 : 28.02.2026
En cascade 2 : 28.03.2026
En cascade 3 : 28.04.2026
En cascade 4 : 28.05.2026
Depuis l'origine 1 : 28.02.2026
Depuis l'origine 2 : 31.03.2026
Depuis l'origine 3 : 30.04.2026
Depuis l'origine 4 : 31.05.2026
Le 31 janvier plus un mois donne le 28 février (le 29 en 2028), et plus deux mois le 31 mars. Ajouter puis retrancher un mois ramène au 28 janvier. L'échéancier en cascade dérive au 28 pour tous les mois suivants ; en repartant de la date d'origine à chaque fois, on obtient 28 février, 31 mars, 30 avril, 31 mai.
Le piège
Fin de mois : le résultat est ramené au dernier jour du mois d'arrivée, et l'erreur se propage. 31 janvier + 1 mois = 28 février (pas d'erreur, pas de message). Si on construit un échéancier en ajoutant un mois à la date précédente, tout le reste de l'année reste au 28. Il faut toujours écrire origine + %MONTHS(i), avec l'origine fixe.
Et l'opération n'est pas réversible : 31 janvier + %MONTHS(1) - %MONTHS(1) donne le 28 janvier.