Regex Belgian national register number
The Belgian national number has 11 digits: the YYMMDD birth date, 3 serial digits and 2 check digits (97 minus the remainder modulo 97, with prefix 2 for births since 2000). It is written '85.07.30-033.28' or, in official IBZ documents, '42.01.22 051-81'. Month and day are 00 when the date is incomplete, and bis numbers (foreigners without a national number) add 20 or 40 to the month. The real attempts I read are digit regexes: validator.js only accepts 11 unspaced digits, validate-insz requires the strict dotted format, and a TypeScript library accepts '85.07.30.033.28' as well as '85-07-30-033-28'. Here's a pattern bounding month, day and serial, compared against those three attempts.
/^[0-9]{2}[. ]?(?:0[0-9]|1[0-2]|2[0-9]|3[0-2]|4[0-9]|5[0-2])[. ]?(?:0[0-9]|[12][0-9]|3[01])[- ]?(?!000|999)[0-9]{3}[-.]?[0-9]{2}$/passes 23/23 cases
Comparison of existing patterns
| Source | pattern | Score on the corpus | ReDoS |
|---|---|---|---|
| validator.js (fr-BE / nl-BE, partagée avec l'ensemble des numéros fiscaux) — « ^\d{11}$ » : mois 13, jour 32 et série 000 passent, et la forme officielle « 42.01.22 051-81 » est rejetée (le nettoyage préalable ne retire pas points et espaces) | ^\d{11}$ | 19 / 23 | safe |
| validate-insz (npm, deux regex réunies par |) — format pointé strict ou 11 chiffres, sans contrainte de plage : « 85.99.99-001.89 » passe ; la forme « 42.01.22 051-81 » de l'IBZ est rejetée ; le mois et la série sont testés ensuite dans le code | ^\d{2}\.\d{2}\.\d{2}-\d{3}\.\d{2}$|^\d{11}$ | 16 / 23 | safe |
| ts-rijksregisternummer (npm) — chaque séparateur est facultatif et indépendant : « 85.07.30.033.28 » et « 85-07-30-033-28 » passent ; aucune contrainte de mois, jour ni série | ^[0-9]{2}[.\- ]?[0-9]{2}[.\- ]?[0-9]{2}[.\- ]?[0-9]{3}[.\- ]?[0-9]{2}$ | 16 / 23 | safe |
Implementation
const re = new RegExp("^[0-9]{2}[. ]?(?:0[0-9]|1[0-2]|2[0-9]|3[0-2]|4[0-9]|5[0-2])[. ]?(?:0[0-9]|[12][0-9]|3[01])[- ]?(?!000|999)[0-9]{3}[-.]?[0-9]{2}$", "");
re.test(input);Limits
- Valide seulement la FORME : ne calcule pas la clé modulo 97. « 85073003329 » et « 17073003385 » ont la bonne forme mais une clé fausse et passent ce motif. Le siècle n'est pas codé dans le numéro : seule la clé permet de distinguer 1985 de 2085 (préfixe 2).
- Le mois accepte 00 à 12 (date incomplète comprise), 20 à 32 et 40 à 52 (numéros bis, relevés dans le code de validate-insz, non dans le document IBZ consulté) ; le jour accepte 00 à 31 sans contrôler le nombre de jours du mois. La série 000 et 999 est exclue ; l'IBZ réserve les séries impaires aux hommes et paires aux femmes, ce que ce motif ne vérifie pas.
- Tolère les séparateurs usuels (points entre année, mois et jour, tiret ou espace avant la série, tiret ou point avant la clé) mais refuse les mélanges arbitraires : « 85.07.30.033.28 » est rejeté. Normalisez en 11 chiffres avant stockage.
- Les exemples viennent du document IBZ et de python-stdnum ; ne mettez jamais de vrais numéros nationaux dans vos tests. Le document IBZ n'était plus servi à son adresse d'origine : il a été lu via une copie Wayback.