Regex durée ISO 8601 (P1DT12H)
Une durée ISO 8601 s'écrit « P3Y6M4DT12H30M5S » : « P », puis années, mois, jours (Y M D), puis « T » et heures, minutes, secondes (H M S), dans cet ordre, ou « P2W » pour des semaines. Il faut au moins un champ, et « T » doit être suivi d'au moins un champ. Les tentatives relues rendent tous les champs facultatifs : moment.js accepte « P », « PT » et même « PY » (nombre vide), iso8601-duration accepte « P » et « PT », isodate (Python) accepte « PT » ainsi que les semaines mélangées aux jours. Voici un motif construit sur la grammaire XML Schema, avec les semaines seules, comparé à ces tentatives.
/^P(?:(?:[0-9]+Y(?:[0-9]+M)?(?:[0-9]+D)?|[0-9]+M(?:[0-9]+D)?|[0-9]+D)(?:T(?:[0-9]+H(?:[0-9]+M)?(?:[0-9]+(?:\.[0-9]+)?S)?|[0-9]+M(?:[0-9]+(?:\.[0-9]+)?S)?|[0-9]+(?:\.[0-9]+)?S))?|T(?:[0-9]+H(?:[0-9]+M)?(?:[0-9]+(?:\.[0-9]+)?S)?|[0-9]+M(?:[0-9]+(?:\.[0-9]+)?S)?|[0-9]+(?:\.[0-9]+)?S)|[0-9]+W)$/passe 36/36 cas
Comparatif des patterns existants
| Source | pattern | Score sur le corpus | ReDoS |
|---|---|---|---|
| moment.js — tous les champs facultatifs et nombres vides : « P », « PT », « PY » passent ; signes et virgules partout (« P-1,5Y ») ; semaines mêlées aux jours | ^(-|\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$ | 27 / 36 | sûr |
| iso8601-duration (npm) — non ancrée ; « P » et « PT » seuls passent, « xP1Dx » passe ; semaines mêlées aux jours | P(?:(\d+Y)?(\d+M)?(\d+W)?(\d+D)?(?:T(\d+(?:[\.,]\d+)?H)?(\d+(?:[\.,]\d+)?M)?(\d+(?:[\.,]\d+)?S)?)?) | 19 / 36 | indéterminé |
| isodate (Python) — « P » seul refusé mais « PT » passe ; décimales sur tous les champs, semaines mêlées aux jours, signe accepté | ^(?<sign>[+-])?P(?!\b)(?<years>[0-9]+([,.][0-9]+)?Y)?(?<months>[0-9]+([,.][0-9]+)?M)?(?<weeks>[0-9]+([,.][0-9]+)?W)?(?<days>[0-9]+([,.][0-9]+)?D)?((?<separator>T)(?<hours>[0-9]+([,.][0-9]+)?H)?(?<minutes>[0-9]+([,.][0-9]+)?M)?(?<seconds>[0-9]+([,.][0-9]+)?S)?)?$ | 31 / 36 | indéterminé |
Implémentation
const re = new RegExp("^P(?:(?:[0-9]+Y(?:[0-9]+M)?(?:[0-9]+D)?|[0-9]+M(?:[0-9]+D)?|[0-9]+D)(?:T(?:[0-9]+H(?:[0-9]+M)?(?:[0-9]+(?:\\.[0-9]+)?S)?|[0-9]+M(?:[0-9]+(?:\\.[0-9]+)?S)?|[0-9]+(?:\\.[0-9]+)?S))?|T(?:[0-9]+H(?:[0-9]+M)?(?:[0-9]+(?:\\.[0-9]+)?S)?|[0-9]+M(?:[0-9]+(?:\\.[0-9]+)?S)?|[0-9]+(?:\\.[0-9]+)?S)|[0-9]+W)$", "");
re.test(input);Limites
- Valide seulement la FORME : aucun contrôle de plausibilité (« PT90M », « P99Y » sont acceptés) ni de cohérence calendaire ; une durée n'a pas de date de départ, « P1M » n'a donc pas de longueur fixe en jours.
- Refusé volontairement : durée négative (« -P1D », absente d'ISO 8601-1:2004 mais admise par XML Schema), virgule décimale, décimale sur un champ autre que les secondes, semaines mêlées à d'autres champs (« P1W2D »), minuscules. Certaines bibliothèques (Java, moment.js) acceptent tout ou partie : ajustez le motif si vous les consommez.
- ISO 8601 n'a pas été consultée (norme payante) : la grammaire retenue est celle de XML Schema 1.1 et de la RFC 3339, qui divergent sur « P1Y2D » (XML Schema l'accepte, la RFC ne l'écrit pas). La forme alternative « P0003-06-04T12:30:05 » n'est pas gérée.