%DIFF
Écart entre deux dates ou heures
Calcule la différence entre deux dates, deux heures ou deux horodatages, en nombre entier d'années, de mois, de jours, d'heures, de minutes, de secondes ou de microsecondes.
Syntaxe
%DIFF(valeur1 : valeur2 : unité)
valeur1- Le premier terme de la soustraction : une date, une heure ou un horodatage.
valeur2- Ce qu'on retranche. Même type que
valeur1, sauf qu'on peut mélanger date et horodatage. unité*YEARS(*Y),*MONTHS(*M),*DAYS(*D),*HOURS(*H),*MINUTES(*MN),*SECONDS(*S),*MSECONDS(*MS). Les unités possibles dépendent du type : jours, mois et années pour une date, heures, minutes et secondes pour une heure.- Résultat
- Un entier,
valeur1 - valeur2: positif sivaleur1est la plus récente, négatif sinon. Seules les unités entières comptent : le reste est supprimé, sans arrondi.
À quoi ça sert
Dès qu'on veut un retard de paiement, un âge, une ancienneté ou un délai de traitement, il faut soustraire deux dates. %DIFF est la fonction prévue pour cela : elle soustrait deux valeurs et rend l'écart dans l'unité choisie.
Exemple 1 : Jours, mois, années
**free
// %DIFF : ecart entre deux dates, en jours, en mois et en annees.
ctl-opt dftactgrp(*no) actgrp(*new);
dcl-s facture date inz(d'2026-09-30');
dcl-s echeance date inz(d'2026-12-25');
dcl-s naissance date inz(d'2000-05-15');
dcl-s aujourd date inz(d'2026-09-30');
dcl-s ecart int(10);
// La date la plus recente en premier : le resultat est positif.
ecart = %diff(echeance : facture : *days);
snd-msg 'Jours avant echeance : [' + %char(ecart) + ']';
// Dans l'autre sens, le resultat est negatif.
ecart = %diff(facture : echeance : *days);
snd-msg 'Ordre inverse : [' + %char(ecart) + ']';
ecart = %diff(echeance : facture : *months);
snd-msg 'Mois entiers : [' + %char(ecart) + ']';
// L'age : nombre d'annees entieres ecoulees.
ecart = %diff(aujourd : naissance : *years);
snd-msg 'Age : [' + %char(ecart) + ']';
// Une condition sur l'ecart : facture en retard de plus de 30 jours ?
if %diff(aujourd : d'2026-08-15' : *days) > 30;
snd-msg 'Facture du 15/08 : relance necessaire';
endif;
*inlr = *on;
Résultat réel, compilé et exécuté sur IBM i 7.5 :
Jours avant echeance : [86]
Ordre inverse : [-86]
Mois entiers : [2]
Age : [26]
Facture du 15/08 : relance necessaire
Du 30 septembre au 25 décembre : 86 jours, ou 2 mois entiers. En inversant les deux dates, le résultat est négatif. L'âge est simplement l'écart en années entre la naissance et le jour du contrôle. Le résultat se teste directement dans un if.
Exemple 2 : Le résultat est tronqué
**free
// %DIFF : le resultat est toujours un nombre entier, la partie fractionnaire est perdue.
ctl-opt dftactgrp(*no) actgrp(*new);
dcl-s ecart int(10);
dcl-s debut timestamp;
dcl-s fin timestamp;
// Fin de mois : du 31 janvier au 28 fevrier, un mois n'est pas ecoule...
ecart = %diff(d'2026-02-28' : d'2026-01-31' : *months);
snd-msg '31/01 -> 28/02 : ' + %char(ecart) + ' mois';
// ... mais il l'est au 1er mars.
ecart = %diff(d'2026-03-01' : d'2026-01-31' : *months);
snd-msg '31/01 -> 01/03 : ' + %char(ecart) + ' mois';
// L'age change le jour de l'anniversaire, pas avant.
ecart = %diff(d'2026-05-14' : d'2000-05-15' : *years);
snd-msg 'Veille anniv. : ' + %char(ecart) + ' ans';
ecart = %diff(d'2026-05-15' : d'2000-05-15' : *years);
snd-msg 'Jour anniv. : ' + %char(ecart) + ' ans';
// 23 h 59 min 59 s d'ecart : zero jour.
debut = z'2026-09-30-08.00.00.000000';
fin = z'2026-10-01-07.59.59.000000';
ecart = %diff(fin : debut : *days);
snd-msg 'Presque 1 jour : ' + %char(ecart) + ' jour';
// Vers le passe, la troncature va aussi vers zero (pas vers -infini).
fin = z'2026-09-29-08.00.01.000000';
ecart = %diff(fin : debut : *days);
snd-msg 'Passe, jours : ' + %char(ecart);
ecart = %diff(fin : debut : *hours);
snd-msg 'Passe, heures : ' + %char(ecart);
*inlr = *on;
Résultat réel, compilé et exécuté sur IBM i 7.5 :
31/01 -> 28/02 : 0 mois
31/01 -> 01/03 : 1 mois
Veille anniv. : 25 ans
Jour anniv. : 26 ans
Presque 1 jour : 0 jour
Passe, jours : 0
Passe, heures : -23
Du 31 janvier au 28 février, il y a 0 mois ; jusqu'au 1er mars, 1 mois. L'âge passe de 25 à 26 ans le jour de l'anniversaire, pas avant. 23 h 59 min 59 s font 0 jour. Vers le passé, on tronque aussi vers zéro : 23 h 59 min 59 s en arrière donnent -23 heures et 0 jour.
Exemple 3 : Heures, horodatages et microsecondes
**free
// %DIFF sur des heures et des horodatages : secondes, microsecondes, depassement.
ctl-opt dftactgrp(*no) actgrp(*new);
dcl-s ouverture time inz(t'08.00.00');
dcl-s fermeture time inz(t'17.45.30');
dcl-s debut timestamp inz(z'2026-09-30-08.00.00.000000');
dcl-s fin timestamp inz(z'2026-10-02-08.00.00.500000');
dcl-s petit int(10);
dcl-s grand int(20);
// Sur des heures : heures, minutes et secondes.
snd-msg 'Ouvert (min) : ' + %char(%diff(fermeture : ouverture : *minutes));
snd-msg 'Ouvert (s) : ' + %char(%diff(fermeture : ouverture : *seconds));
snd-msg 'Ouvert (h) : ' + %char(%diff(fermeture : ouverture : *hours));
// Sur des horodatages : *SECONDS ignore la demi-seconde.
snd-msg 'Ecart (s) : ' + %char(%diff(fin : debut : *seconds));
// *MSECONDS compte en MICROSECONDES : 2 jours + 0,5 s = 172 800 500 000.
grand = %diff(fin : debut : *mseconds);
snd-msg 'Ecart (ms) : ' + %char(grand);
// Ce nombre ne tient pas dans un entier de 10 chiffres.
monitor;
petit = %diff(fin : debut : *mseconds);
snd-msg 'Dans int(10) : ' + %char(petit);
on-error;
snd-msg 'Dans int(10) : depassement, statut ' + %char(%status);
endmon;
*inlr = *on;
Résultat réel, compilé et exécuté sur IBM i 7.5 :
Ouvert (min) : 585
Ouvert (s) : 35130
Ouvert (h) : 9
Ecart (s) : 172800
Ecart (ms) : 172800500000
Dans int(10) : depassement, statut 103
Sur des heures, on choisit heures, minutes ou secondes. Avec *MSECONDS, le résultat est en microsecondes : 172 800 500 000 pour deux jours et une demi-seconde. Il dépasse la capacité d'un int(10) : le statut 103 signale le débordement, d'où la zone int(20).
Le piège
Le résultat est un entier tronqué, pas arrondi. Du 31 janvier au 28 février, %DIFF(... : *MONTHS) renvoie 0 alors que 31 janvier + %MONTHS(1) donne justement le 28 février : les deux fonctions ne sont pas exactement inverses en fin de mois. De même, une personne de 25 ans et 364 jours a encore 25 ans.
Autres surprises vérifiées : *MSECONDS compte en microsecondes (et fait déborder un int(10)), et l'ordre des dates change le signe du résultat. Enfin %DIFF(horodatage : date : *HOURS) est refusé à la compilation (RNF0593) : les heures ne s'appliquent pas à une date.