Regex South African ID number
A South African ID number has 13 digits: YYMMDD, 4 serial digits (gender), a citizenship digit, a 12th digit and a Luhn check. The 12th digit is normally 8, and 9 when the day's series runs out, per a 2000 regulatory amendment: a Microsoft Q&A regex forces it to 8 and so rejects valid numbers. The citizenship rule is also poorly handled: 0 or 1 in the regulation text, 2 for refugees in libraries. Here's a pattern bounding months and days, accepting 0 to 2 for citizenship and not forcing the 12th digit, compared against those attempts.
/^[0-9]{2}(?:0[1-9]|1[0-2])(?:0[1-9]|[12][0-9]|3[01])[0-9]{4}[0-2][0-9][0-9]$/passes 22/22 cases
Comparison of existing patterns
| Source | pattern | Score on the corpus | ReDoS |
|---|---|---|---|
| Microsoft Q&R (réponse sur les numéros d'identité) — force le 12e chiffre à 8 : rejette « 7007076000098 » (12e chiffre 9, valide) ; rejette aussi la citoyenneté 2 ; jour 31 pour tous les mois | \b\d{2}(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01])\d{4}[01]8\d\b | 21 / 22 | safe |
| south-africa-national-id-validation (npm, ClearScore) — mois [0-1][0-9] accepte 00 et 13-19, jour [0-3]\d accepte 00 et 32-39 (la date est revérifiée dans le code) ; citoyenneté [0-1] rejette 2 | ^\d{2}[0-1][0-9][0-3]\d\d{4}[0-1]\d{2}$ | 18 / 22 | safe |
| Smile ID (documentation de l'API) — longueur seule : mois 13, jour 32, citoyenneté 3 et clé fausse passent tous ; honnête comme pré-contrôle, pas comme validation | ^[0-9]{13}$ | 17 / 22 | safe |
Implementation
const re = new RegExp("^[0-9]{2}(?:0[1-9]|1[0-2])(?:0[1-9]|[12][0-9]|3[01])[0-9]{4}[0-2][0-9][0-9]$", "");
re.test(input);Limits
- Valide seulement la FORME : ne calcule pas la clé Luhn (13e chiffre), ne vérifie pas le jour dans le mois ni les années bissextiles (« 0002305009082 », un 30 février, passe), et ne connaît pas le siècle : AAMMJJ est ambigu (« 0402290001187 » est valide comme 29 février 2004, pas comme 1904).
- Le 12e chiffre vaut normalement 8, et 9 quand la série du jour est épuisée (amendement de 2000). Les anciens numéros peuvent porter d'autres valeurs : le motif accepte n'importe quel chiffre pour ne pas rejeter de numéros valides, au prix de ne pas contrôler cette position.
- La citoyenneté 0 ou 1 vient du texte réglementaire (copie secondaire consultée, la Gazette étant protégée) ; 2 (réfugié) vient de Wikipédia et de bibliothèques tierces. Le motif accepte 0 à 2 : si vous suivez strictement le texte de 1998, restreignez à [01].
- Les numéros du corpus sont synthétiques, avec une clé Luhn recalculée : aucun jeu d'exemples officiel n'existe. N'utilisez jamais de vrais numéros d'identité dans vos tests. L'existence réelle d'un numéro ne peut être confirmée que par le Département des Affaires intérieures.