Aller au contenu

%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.