Regex date française (jj/mm/aaaa)
La question de référence sur Stack Overflow (267 votes) part d'un problème concret : une regex sans logique d'année bissextile validait « 31/02/4500 » comme une date correcte. La réponse la plus votée (450 votes) corrige ce point précis. Mais en testant réellement deux autres réponses du même fil, on retrouve — dans chacune, indépendamment — une virgule oubliée dans une classe de caractères, qui rouvre une faille du même genre que celle que l'auteur de la réponse la plus votée avait lui-même corrigée dans son historique d'édition. Voici un motif fiable, comparé à ces 3 tentatives réelles.
/^(?:31\/(?:0?[13578]|1[02])\/|(?:29|30)\/(?:0?[13-9]|1[0-2])\/)(?:1[6-9]|[2-9]\d)\d{2}$|^29\/0?2\/(?:(?:1[6-9]|[2-9]\d)(?:0[48]|[2468][048]|[13579][26])|(?:16|[2468][048]|[3579][26])00)$|^(?:0?[1-9]|1\d|2[0-8])\/(?:0?[1-9]|1[0-2])\/(?:1[6-9]|[2-9]\d)\d{2}$/passe 28/28 cas
Comparatif des patterns existants
| Source | pattern | Score sur le corpus | ReDoS |
|---|---|---|---|
| Stack Overflow — tentative de l'auteur de la question, sans logique d'année bissextile (accepte « 31/02/4500 ») | ^(0?[1-9]|[12][0-9]|3[01])[\/\-](0?[1-9]|1[012])[\/\-]\d{4}$ | 20 / 28 | sûr |
| Stack Overflow — extension à 307 votes de la réponse la plus votée, virgule oubliée dans une classe de caractères | ^(?:(?:31(\/|-|\.)(?:0?[13578]|1[02]))\1|(?:(?:29|30)(\/|-|\.)(?:0?[1,3-9]|1[0-2])\2))(?:(?:1[6-9]|[2-9]\d)?\d{2})$|^(?:29(\/|-|\.)0?2\3(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))$|^(?:0?[1-9]|1\d|2[0-8])(\/|-|\.)(?:(?:0?[1-9])|(?:1[0-2]))\4(?:(?:1[6-9]|[2-9]\d)?\d{2})$ | 25 / 28 | sûr |
| Stack Overflow — réponse à 86 votes, ne gère pas correctement les siècles (bug signalé en commentaire, valide seulement 1901-2099), même virgule oubliée | (^(((0[1-9]|1[0-9]|2[0-8])[\/](0[1-9]|1[012]))|((29|30|31)[\/](0[13578]|1[02]))|((29|30)[\/](0[4,6,9]|11)))[\/](19|[2-9][0-9])\d\d$)|(^29[\/]02[\/](19|[2-9][0-9])(00|04|08|12|16|20|24|28|32|36|40|44|48|52|56|60|64|68|72|76|80|84|88|92|96)$) | 24 / 28 | sûr |
Implémentation
const re = new RegExp("^(?:31\\/(?:0?[13578]|1[02])\\/|(?:29|30)\\/(?:0?[13-9]|1[0-2])\\/)(?:1[6-9]|[2-9]\\d)\\d{2}$|^29\\/0?2\\/(?:(?:1[6-9]|[2-9]\\d)(?:0[48]|[2468][048]|[13579][26])|(?:16|[2468][048]|[3579][26])00)$|^(?:0?[1-9]|1\\d|2[0-8])\\/(?:0?[1-9]|1[0-2])\\/(?:1[6-9]|[2-9]\\d)\\d{2}$", "");
re.test(input);Limites
- Ne couvre que la forme numérique jj/mm/aaaa avec « / » comme séparateur : les formes « jj-mm-aaaa », « jj.mm.aaaa » ou en toutes lettres (« 3 septembre 2026 »), également utilisées en France, ne sont pas reconnues par ce motif.
- Accepte un jour ou un mois à un seul chiffre sans zéro de tête (« 1/1/2020 ») aussi bien que la forme complète : pour exiger strictement deux chiffres, il faut retirer les « 0? » optionnels du motif.
- L'année doit compter exactement 4 chiffres et rester dans la plage 1600-9999 : une année à 2 chiffres (« 20 » pour 2020) ou antérieure à 1600 est rejetée, une limite arbitraire héritée des plages de siècle du motif source, sans lien avec une règle du calendrier grégorien lui-même.
- Valide uniquement la FORME d'une date calendaire (nombre de jours par mois + règle bissextile) : ne connaît ni les 10 jours sautés lors de la réforme grégorienne de 1582, ni aucune règle métier (date future interdite, jour férié, etc.).