Regex Swedish personnummer
A personnummer is written '640823-3234' (10 digits, hyphen, or '+' after age 100) or in 12 digits with the century. The check is a Luhn computed on the last 10 digits only. A real trap I reproduced: validator.js 13.15.35 applies Luhn to all 12 digits, so Skatteverket's official example '19640823-3234' is rejected while '19640823-3233', a wrong check digit, is accepted. Other real attempts get the month wrong (13 passes), the length, or serial number 000. Here's a pattern bounding months and days (samordningsnummer included) and excluding serial 000, compared against those attempts.
/^(?:(?:18|19|20)[0-9]{2}|[0-9]{2})(?:0[1-9]|1[0-2])(?:0[1-9]|[12][0-9]|3[01]|6[1-9]|[78][0-9]|9[01])[-+]?(?!000)[0-9]{3}[0-9]$/passes 26/26 cases
Comparison of existing patterns
| Source | pattern | Score on the corpus | ReDoS |
|---|---|---|---|
| validator.js (sv-SE) — forme large : mois 13, jour 00 et série 000 passent ; surtout, sa clé est calculée sur 12 chiffres, donc « 19640823-3234 » (exemple officiel) est rejeté et « 19640823-3233 » accepté | ^(\d{6}[-+]{0,1}\d{4}|(18|19|20)\d{6}[-+]{0,1}\d{4})$ | 21 / 26 | safe |
| personnummer (npm, la plus utilisée) — exclut bien la série 000 et exige la clé, mais mois 13, jour 00 et jour 92 passent la regex ; la date et le Luhn sont validés ensuite dans le code | ^(\d{2}){0,1}(\d{2})(\d{2})(\d{2})([+-]?)((?!000)\d{3}|[TRSUWXJKLMN]\d{2})(\d)$ | 22 / 26 | safe |
| Microsoft Presidio (détecteur de données personnelles) — « \d{6,8} » accepte 6, 7 ou 8 chiffres de date, donc un numéro à 11 chiffres passe ; non ancrée avec \b ; aucune contrainte de mois ni de jour | \b(\d{6,8})([-+]?)\d{4}\b | 20 / 26 | safe |
Implementation
const re = new RegExp("^(?:(?:18|19|20)[0-9]{2}|[0-9]{2})(?:0[1-9]|1[0-2])(?:0[1-9]|[12][0-9]|3[01]|6[1-9]|[78][0-9]|9[01])[-+]?(?!000)[0-9]{3}[0-9]$", "");
re.test(input);Limits
- Valide seulement la FORME : ne calcule pas la clé (Luhn sur les 10 derniers chiffres). « 640823-3235 » et « 880320-0018 » ont la bonne forme mais une clé fausse et passent ce motif ; seul un calcul dans le code les rejette. Ne valide pas non plus le jour dans le mois (« 640230-3231 », 30 février, passe).
- Le séparateur « + » (personne de plus de 100 ans) dépend de la date du jour : une regex ne peut pas savoir qu'un « 640823+3234 » est incohérent. Le motif accepte « + » ou « - » avec les deux formes de siècle.
- Les jours 61 à 91 (samordningsnummer, jour + 60) sont acceptés, ainsi que les 12 chiffres avec ou sans séparateur. Si vous ne gérez que les personnes enregistrées, restreignez le jour à 01-31. La série 000 est exclue (SKV 704 : 001 à 999).
- Le comportement de validator.js sur les 12 chiffres a été testé sur la version 13.15.35 : la bibliothèque a pu être corrigée depuis. N'utilisez pas de vrais numéros dans vos tests ; les exemples viennent de SKV 704, de python-stdnum et de jeux de tests publics.