Regex numéro de TVA néerlandais (btw-nummer)
Le numéro de TVA néerlandais s'écrit « NL », 9 chiffres, la lettre « B » et un compteur de 2 chiffres, soit 12 caractères après le préfixe. La FAQ VIES de la Commission le décrit par une ligne de 12 caractères génériques « NLSSSSSSSSSSSS », sans détailler les positions ; la structure précise (9 chiffres, « B », 2 chiffres) vient de python-stdnum et de jsvat. Depuis le 1er janvier 2020, le numéro d'un entrepreneur individuel ne contient plus le numéro BSN : sa clé n'est plus le test « 11 » mais un modulo 97. Les quatre tentatives relues ne vérifient que la forme ; celle de validator.js rend le préfixe « NL » facultatif, et aucune ne refuse un compteur « B00 » ni un bloc de zéros. Voici un motif ancré qui les refuse, comparé à ces tentatives.
/^NL(?!0{9})[0-9]{9}B(?!00)[0-9]{2}$/Comparatif des patterns existants
| Source | pattern | Score sur le corpus | ReDoS |
|---|---|---|---|
| validator.js (isVAT) — préfixe « NL » facultatif (« 004495445B01 » nu passe) ; « NL004495445B00 » et un bloc de neuf zéros passent | ^(NL)?\d{9}B\d{2}$ | 23 / 26 | sûr |
| jsvat (netherlands.ts) — ancrée, préfixe obligatoire ; « NL004495445B00 » et « NL000000000B01 » passent (la clé est contrôlée à part par calcFn) | ^(NL)(\d{9}B\d{2})$ | 24 / 26 | sûr |
| valvat (syntax.rb, A et Z remplacés par ^ et $) — ancrée, préfixe obligatoire ; « NL004495445B00 » et « NL000000000B01 » passent | ^NL[0-9]{9}B[0-9]{2}$ | 24 / 26 | sûr |
| Regular Expressions Cookbook, recette 4.21 (27 pays en une regex, insensible à la casse) — préfixe facultatif (« 004495445B01 » nu), minuscules acceptées (« nl004495445b01 »), « B00 » et bloc de zéros passent | ^((AT)?U[0-9]{8}|(BE)?0[0-9]{9}|(BG)?[0-9]{9,10}|(CY)?[0-9]{8}L|(CZ)?[0-9]{8,10}|(DE)?[0-9]{9}|(DK)?[0-9]{8}|(EE)?[0-9]{9}|(EL|GR)?[0-9]{9}|(ES)?[0-9A-Z][0-9]{7}[0-9A-Z]|(FI)?[0-9]{8}|(FR)?[0-9A-Z]{2}[0-9]{9}|(GB)?([0-9]{9}([0-9]{3})?|[A-Z]{2}[0-9]{3})|(HU)?[0-9]{8}|(IE)?[0-9]S[0-9]{5}L|(IT)?[0-9]{11}|(LT)?([0-9]{9}|[0-9]{12})|(LU)?[0-9]{8}|(LV)?[0-9]{11}|(MT)?[0-9]{8}|(NL)?[0-9]{9}B[0-9]{2}|(PL)?[0-9]{10}|(PT)?[0-9]{9}|(RO)?[0-9]{2,10}|(SE)?[0-9]{12}|(SI)?[0-9]{8}|(SK)?[0-9]{10})$ | 21 / 26 | sûr |
Implémentation
const re = new RegExp("^NL(?!0{9})[0-9]{9}B(?!00)[0-9]{2}$", "");
re.test(input);Limites
- Valide seulement la FORME : « NL123456789B90 » a la bonne structure mais ne vérifie ni le test « 11 » ni le modulo 97 et passe ce motif. Les deux algorithmes ne sont pas recalculés par la regex.
- La FAQ VIES de la Commission décrit seulement 12 caractères génériques (lettres, chiffres, « + » ou « * ») ; ce motif suit la structure plus stricte de python-stdnum et jsvat. Si VIES acceptait un jour d'autres caractères, ce motif les refuserait.
- Le refus du compteur « B00 » et d'un bloc de neuf zéros vient de python-stdnum (sources secondaires). Aucune vérification d'existence (VIES, Belastingdienst).
- Ne pas confondre avec le BSN (burgerservicenummer, 9 chiffres) ni avec le numéro de KvK, qui ont d'autres formats. Pas de tolérance pour espaces ou minuscules ; normalisez avant validation.