🚀The world's best VBA AI has evolved. ExcelMaster is now an autonomous Agent.Read more →
Back to Blog

VBA DateAdd & DateDiff dans Excel — calculer sur les dates sans le bug de la longueur des mois

|

VBA DateAdd & DateDiff dans Excel — calculer sur les dates sans le bug de la longueur des mois

TL;DR — Une date est un Double, donc myDate + 1 c'est demain et myDate - 7 c'est la semaine dernière — l'arithmétique simple est correcte pour les jours. Elle s'effondre pour les mois et les années, car un mois n'a pas 30 jours et une année n'a pas 365 jours. DateAdd("m", 1, d) connaît le calendrier ; DateDiff("d", a, b) compte les jours entiers ; et DateDiff("yyyy", a, b) compte les frontières d'année franchies, pas le temps écoulé — voilà pourquoi du 31 déc. au 1er janv. il y a « 1 an ».

Sub DateMathDemo()
    Debug.Print #2026-01-31# + 1                       ' 2026-02-01  - jours: le + simple marche
    Debug.Print DateAdd("m", 1, #2026-01-31#)          ' 2026-02-28  - conscient du mois, plafonne
    Debug.Print DateDiff("d", #2026-01-01#, #2026-03-01#) ' 59       - jours entiers ecoules
    Debug.Print DateSerial(2026, 3, 0)                 ' 2026-02-28  - dernier jour de fevrier
End Sub

Le calcul sur les dates en VBA repose sur un seul fait : une date est un Double qui compte les jours. Cela rend le calcul en jours trivial — + 1, - 7, Date + 30 marchent tous, parce qu'un jour vaut exactement 1. Les ennuis commencent quand vous supposez que la même astuce s'étend aux mois et aux années, qui n'ont pas de longueur fixe. DateAdd, DateDiff et DateSerial existent précisément pour faire la partie calendaire que le simple + et le simple - ne savent pas faire.

Ce que vous allez apprendre

  • Le modèle mental — le simple +/- est correct pour les jours, faux pour les mois et les années
  • DateAdd(interval, number, date) et les codes d'intervalle qui mordent ("m" vs "n", "y" vs "yyyy")
  • Pourquoi DateAdd("m", 1, 31-janv.) renvoie le 28 février — une intelligence de fin de mois que + ne donne pas
  • DateDiff compte les frontières franchies, pas la durée écoulée — le « 1 an » à partir d'un seul jour
  • DateSerial construit une date à partir de ses éléments sans ambiguïté régionale, et l'idiome du dernier jour du mois
  • Quand utiliser l'arithmétique simple et quand une fonction est obligatoire

Le modèle mental : le calcul simple ne vaut que pour les jours

Parce qu'une date est un Double de jours, le calcul en jours ne réclame aucune fonction :

tomorrow = Date + 1
lastWeek = Date - 7
Debug.Print #2026-02-28# + 1        ' 2026-03-01 - VBA passe au mois suivant pour vous

C'est authentiquement correct — VBA gère les fins de mois et les années bissextiles quand vous ajoutez des jours entiers, car cela revient à ajouter au numéro de série. L'erreur, c'est de croire qu'un mois ou une année est un nombre de jours :

nextMonth = Date + 30      ' FAUX - 30 jours n'est pas "un mois"
nextYear  = Date + 365     ' FAUX - rate le jour bissextile, derive chaque annee

+ 30 retombe sur des jours différents selon le mois ; + 365 se décale d'un jour sur tout intervalle qui inclut le 29 février. Dès que votre unité est le mois ou l'année, l'arithmétique simple est un bug et il vous faut DateAdd.

DateAdd et les codes d'intervalle qui mordent

DateAdd(interval, number, date) ajoute number unités d'interval à date. L'interval est un code chaîne, et deux collisions causent l'essentiel de la confusion :

Code Unité Attention
"yyyy" Année "y" n'est pas l'année — c'est le jour de l'année
"q" Trimestre
"m" Mois "m" c'est le mois ; la minute, c'est "n"
"ww" Semaine
"d" Jour "y" et "w" se comportent aussi comme des jours
"h" Heure
"n" Minute pas "m"
"s" Seconde
DateAdd("m", 15, startTime)   ' ajoute 15 MOIS - sans doute pas ce que vous vouliez
DateAdd("n", 15, startTime)   ' ajoute 15 MINUTES - le "n" que vous vouliez vraiment

L'échange "m" / "n" est le bug vedette : vous tendez la main vers "m" en pensant « minute » et vous avez discrètement ajouté quinze mois à un horodatage. Dites les codes à voix haute une fois — m c'est le mois, n c'est la minute, yyyy c'est l'année — et toute la famille cesse d'être piégeuse. number peut être négatif pour soustraire, et le résultat est toujours une date correcte.

Pourquoi DateAdd renvoie le 28 février quand vous ajoutez un mois au 31 janvier

Voici le gain que l'arithmétique simple ne pourra jamais vous offrir. Ajoutez un mois au 31 janvier : il n'existe pas de « 31 février » où atterrir, alors DateAdd ramène au dernier jour valide du mois cible :

DateAdd("m", 1, #2026-01-31#)   ' 2026-02-28  (2028 donnerait 2028-02-29)
DateAdd("m", 1, #2026-03-31#)   ' 2026-04-30  (avril a 30 jours)
DateAdd("yyyy", 1, #2028-02-29#) ' 2029-02-28 (pas de jour bissextile l'an prochain)

DateAdd sait combien de jours compte chaque mois et ajuste les années bissextiles automatiquement — exactement le savoir que + 30 jette à la poubelle. Si vous calculez des dates d'échéance, des renouvellements ou un « même jour le mois prochain », ce plafonnement est le comportement voulu et la raison de ne jamais bricoler le calcul des mois à la main.

DateDiff compte les frontières franchies, pas le temps écoulé

DateDiff(interval, date1, date2) ressemble à « combien de temps entre deux dates », et pour "d" (jours) c'en est. Pour "m", "yyyy", "q" et "ww", il signifie quelque chose de plus subtil et surprenant : combien de frontières d'intervalle séparent les deux dates, pas le temps réellement écoulé.

DateDiff("yyyy", #2025-12-31#, #2026-01-01#)  ' 1  - une frontiere de nouvel an, mais UN jour d'ecart
DateDiff("m",    #2026-01-31#, #2026-02-01#)  ' 1  - une frontiere de mois, un jour d'ecart
DateDiff("d",    #2026-01-31#, #2026-02-01#)  ' 1  - un vrai jour (celui-ci est litteral)

Donc DateDiff("yyyy", …) est un décompte de changements d'année civile, ce qui n'est pas l'âge de quelqu'un ni un nombre d'années écoulées. Pour obtenir un vrai décompte écoulé, vous calculez la différence de frontières puis vous ajustez — pour l'âge, retranchez 1 si l'anniversaire n'a pas encore eu lieu cette année :

age = DateDiff("yyyy", dob, Date)
If DateSerial(Year(Date), Month(dob), Day(dob)) > Date Then age = age - 1

DateDiff renvoie aussi un nombre négatif quand date1 est postérieure à date2, ce qui est un moyen pratique de tester l'ordre. La règle : servez-vous librement de "d" pour les jours entiers ; traitez "m"/"yyyy"/"q" comme des décomptes de frontières et ajustez avant d'appeler le résultat une durée.

DateSerial construit une date à partir de ses éléments, sans ambiguïté régionale

Quand vous avez l'année, le mois et le jour sous forme de nombres séparés, ne les collez pas dans une chaîne en espérant que CDate les lise dans le bon ordre — cela dépend des paramètres régionaux de la machine et intervertit silencieusement le jour et le mois d'un pays à l'autre. DateSerial(year, month, day) construit la date directement, sans texte ni ambiguïté :

d = DateSerial(2026, 2, 1)     ' toujours le 1er fevrier 2026, sur toute machine

Mieux encore, DateSerial normalise les débordements : le mois 13 bascule dans l'année suivante, le jour 0 est le dernier jour du mois précédent. Cela donne les deux idiomes les plus utiles du code de dates :

lastDayOfMonth = DateSerial(y, m + 1, 0)   ' jour 0 = dernier jour du mois m
firstOfNextQ   = DateSerial(y, m + 3, 1)   ' trois mois plus tard, jour 1

DateSerial(y, m + 1, 0) est le « dernier jour de ce mois » canonique — pas de table des longueurs de mois, pas de cas particulier d'année bissextile. Associez-le à Year, Month et Day du guide d'extraction des parties et vous pouvez construire n'importe quelle date par rapport à une autre.

Le verdict honnête : les jours à la main, tout le reste par fonction

La ligne de partage est nette, et rester du bon côté élimine presque tous les bugs de calcul de dates :

  • Jours → arithmétique simpledate + n, date - n. Correct et rapide ; VBA fait rouler les mois et les années bissextiles pour vous.
  • Mois / années → DateAdd → jamais + 30 ni + 365. DateAdd("m", …) ramène aux vraies longueurs de mois ; souvenez-vous que "m" c'est le mois et "n" la minute.
  • Écarts → DateDiff, mais lisez-le comme des frontières"d" compte des jours littéraux ; "m"/"yyyy" comptent des franchissements de frontières, alors ajustez avant d'en faire une durée.
  • Construire à partir des éléments → DateSerial → aucune ambiguïté régionale, et DateSerial(y, m + 1, 0) est l'idiome du dernier jour du mois.

La phrase à garder : l'opérateur + ne connaît pas le calendrier, et DateDiff ne mesure pas le temps. Utilisez chacun pour la seule chose sur laquelle il a raison.

Comment ExcelMaster aide

Les bugs de calcul de dates qui coûtent cher sont les plausibles : une date de renouvellement calculée en + 30 qui dérive selon la longueur des mois, un « âge » tiré de DateDiff("yyyy", …) faux d'un an autour des anniversaires, une date d'échéance construite depuis une chaîne qui intervertit jour et mois selon les paramètres régionaux d'un collègue. Chacun a l'air juste dans un test rapide et échoue sur les cas limites.

ExcelMaster écrit la version correcte du point de vue du calendrier. Décrivez le calcul — « premier jour ouvré du mois prochain », « facture échue dans 45 jours », « nombre de mois entre deux dates » — et il choisit l'arithmétique simple pour les jours, DateAdd avec le bon code d'intervalle pour les mois et les années, et DateSerial pour construire à partir des éléments sans ambiguïté régionale, en convertissant les décomptes de frontières de DateDiff en vraies durées. Vous décrivez la date qu'il vous faut ; il écrit le calcul qui survit aux fins de mois et aux années bissextiles.

Questions fréquentes

Comment ajouter des jours à une date en VBA ?

Utilisez simplement l'arithmétique : une date est un nombre de jours, donc newDate = myDate + 7 ajoute une semaine et myDate - 1 c'est hier. VBA fait rouler les fins de mois et les années bissextiles pour vous quand vous ajoutez des jours entiers. Ne recourez à DateAdd que lorsque l'unité est un mois ou une année, car celles-ci n'ont pas de nombre de jours fixe.

Quelle est la différence entre DateAdd et le simple + en VBA ?

Le simple + ajoute des jours et est correct pour le calcul en jours. DateAdd(interval, number, date) comprend le calendrier, donc DateAdd("m", 1, …) ajoute un vrai mois — le 31 janv. devient 28 févr., pas un 31 impossible — et DateAdd("yyyy", 1, …) gère les années bissextiles. Utilisez + pour les jours ; utilisez DateAdd pour les mois et les années, là où + 30 ou + 365 dériverait.

Pourquoi DateDiff donne-t-il le mauvais nombre d'années en VBA ?

Parce que DateDiff("yyyy", …) compte les frontières d'année franchies, pas les années écoulées. Du 31 décembre au 1er janvier, il renvoie 1, alors qu'un seul jour a passé. Pour un âge ou de vraies années écoulées, calculez DateDiff("yyyy", dob, Date) et retranchez 1 si l'anniversaire de cette année n'a pas encore eu lieu. Utilisez "d" quand vous voulez un décompte littéral de jours.

Que signifient les codes d'intervalle de DateAdd ?

Ce sont des codes chaîne pour l'unité : "yyyy" année, "q" trimestre, "m" mois, "ww" semaine, "d" jour, "h" heure, "n" minute, "s" seconde. Les deux pièges sont que la minute c'est "n", pas "m" (qui est le mois) et que "y" veut dire jour de l'année, pas année (l'année, c'est "yyyy"). Les mêmes codes fonctionnent dans DateDiff et DatePart.

Comment obtenir le dernier jour du mois en VBA ?

Utilisez DateSerial avec le jour 0 du mois suivant : DateSerial(Year(d), Month(d) + 1, 0). Le jour 0 est défini comme le dernier jour du mois précédent, donc cela renvoie le dernier jour du mois de d sans table des longueurs de mois ni cas particulier d'année bissextile. DateSerial construit aussi des dates à partir de leurs éléments sans l'ambiguïté régionale jour/mois de l'analyse d'une chaîne.

Testé dans

Testé dans : Excel 365 (Windows 11), VBA 7.1 — vérifié le 26/08/2026.

Guides connexes : VBA Now, Date & Time · VBA Weekday & DatePart · VBA CStr, CDate & Val · VBA Format · Boucle For VBA