public IDNumberValidationResult Validate(string number) { var result = new IDNumberValidationResult(number); result.AdditionalData = new NRNumberAdditionalData(); result.CleanProvidedValue = number.ToAlphaNumericOnly().TrimStart("BE").TrimStart("be").Trim(); try { if (!string.IsNullOrEmpty(result.CleanProvidedValue)) { var nonAllowedCharacters = new Regex(@"[^0-9. -]"); var rrnumber = string.Copy(result.CleanProvidedValue); if (nonAllowedCharacters.IsMatch(rrnumber)) { result.IsValid = false; result.ValidationErrors.Add("Number contains non-allowed character"); } else { //LENGTH MUST BE 11 DIGITS if (rrnumber.Length != 11) { result.IsValid = false; result.ValidationErrors.Add("Length != 11"); } else { var birthDateOK = false; var counterOK = false; var controlOK = false; var unknownBirthDay = false; var born2kOrLater = false; var gender = "(unknown)"; //FIRST 6 DIGITS ARE BIRTHDATE IN FORMAT YYMMDD var birthDatePart = rrnumber.Substring(0, 6); //NEXT 3 ARE COUNTER var counterPart = rrnumber.Substring(6, 3); //LAST 2 ARE CONTROLNUMBER var controlPart = rrnumber.Substring(9, 2); /* 1. CONTROL NUMBER CHECKING */ /******************************/ //CALCULATE CONTROLNUMER (= MOD 97 OF FIRST 9 DIGITS) var calculatedControl = 97 - (int) (long.Parse(birthDatePart + counterPart)%97); if (calculatedControl != int.Parse(controlPart)) { /* IF THE CALCULATED CONTROL PART IS DIFFERENT THAN THE ONE IN THE INPUTSTRING * ADD A "2" IN FRONT OF THE BIRTHDATEPART AND RECALCULATE. THIS WAS INTRODUCED TO * ALLOW BIRTHDATES OF YEAR 2000 AND LATER */ calculatedControl = 97 - (int) (long.Parse("2" + birthDatePart + counterPart)%97); if (calculatedControl != int.Parse(controlPart)) { /* THE CALCULATION STILL DOESN'T MATCH THE CONTROLNUMER, SO THIS IS AN INVALID * REGISTRY NUMBER */ controlOK = false; } else { born2kOrLater = true; controlOK = true; } } else controlOK = true; /* 2. BIRTHDATE CHECKING */ /*************************/ var d = birthDatePart; //BUILD THE BIRTHDATE TO CHECK if (born2kOrLater) d = "20" + d; else d = "19" + d; //END BUILD var format = "yyyyMMdd"; DateTime birthDate; birthDateOK = DateTime.TryParseExact(d, format, CultureInfo.CurrentCulture, DateTimeStyles.None, out birthDate); if (!birthDateOK) { //MONTH AND/OR DAY CAN BE 00 IF THESE ARE UNKNOWN. IF THIS IS THE CASE, FLAG THE BIRTHDATE AS VALID ANYWAY if (birthDatePart.Substring(2, 2).Equals("00") || birthDatePart.Substring(4, 2).Equals("00")) { unknownBirthDay = true; birthDateOK = true; } } /* 3. COUNTER CHECKING */ /***********************/ /* COUNTERPART MUST BE BETWEEN 001 AND 997 * EVEN FOR FEMALE * ODD FOR MALE */ var counter = int.Parse(counterPart); if (counter < 1 || counter > 997) counterOK = false; else if (counter%2 == 0) //EVEN { counterOK = true; gender = "F"; //FEMALE } else { counterOK = true; gender = "M"; //MALE } /* 4. PROCESS RESULTS */ /**********************/ if (!birthDateOK) { result.IsValid = false; result.ValidationErrors.Add("Birthdate part not valid"); } if (!counterOK) { result.IsValid = false; result.ValidationErrors.Add("Counter part not valid"); } if (!controlOK) { result.IsValid = false; result.ValidationErrors.Add("Controlnumber part not valid"); } if (!unknownBirthDay && birthDateOK) ((NRNumberAdditionalData)(result.AdditionalData)).BirthDate = birthDate; if (gender == "F") ((NRNumberAdditionalData)(result.AdditionalData)).Gender = GenderEnum.Female; else if (gender == "M") ((NRNumberAdditionalData)(result.AdditionalData)).Gender = GenderEnum.Male; } } if (!result.IsValid.HasValue) { result.IsValid = true; result.ValidatedValue = rrnumber; } else //result.IsValid == false { result.AdditionalData = null; } } else throw new Exception("Number is empty"); } catch (Exception ex) { result.ValidationException = ex; result.IsValid = null; result.ValidatedValue = null; result.AdditionalData = null; } return result; }
public IDNumberValidationResult Validate(string number) { var result = new IDNumberValidationResult(number); result.AdditionalData = new NationalIDAdditionalData(); result.CleanProvidedValue = number.ToAlphaNumericOnly().TrimStart("ZA").TrimStart("za").Trim(); try { if (!string.IsNullOrEmpty(result.CleanProvidedValue)) { var nonAllowedCharacters = new Regex(@"[^0-9. -]"); var idnumber = string.Copy(result.CleanProvidedValue); if (nonAllowedCharacters.IsMatch(idnumber)) { result.IsValid = false; result.ValidationErrors.Add("Number contains non-allowed character"); } else { //LENGTH MUST BE 11 DIGITS if (idnumber.Length != 13) { result.IsValid = false; result.ValidationErrors.Add("Length != 13"); } else { var birthDateOK = false; var counterOK = false; var citizenshipOK = false; var raceOK = false; var controlOK = false; DateTime birthDate = new DateTime(); var gender = "(unknown)"; int? citizenship; int? race; //FIRST 6 DIGITS ARE BIRTHDATE IN FORMAT YYMMDD var birthDatePart = idnumber.Substring(0, 6); //NEXT 4 ARE COUNTER var counterPart = idnumber.Substring(6, 4); //NEXT 1 IS CITIZENSHIP var citizenshipPart = idnumber.Substring(10, 1); //NEXT 1 IS RACE (prior to 1994) var racePart = idnumber.Substring(11, 1); //LAST 1 IS CONTROLNUMBER var controlPart = idnumber.Substring(12, 1); /* CONTROL NUMBER CHECKING */ /******************************/ /* * The checksum digit is calculated using the Luhn algorithm:[3] A = The sum of the digits in the ID number in the odd positions (Excluding Z) B = The number formed by the concatenation of the digits in the ID number in the even positions C = The sum of the digits in (2 * B) D = A + C Z = 10 - (D mod 10) */ int A = 0; string strB = ""; int B = 0; string strBtimes2 = ""; int C = 0; int D = 0; int Z = 0; for (int odd = 0; odd <= 10; odd = odd + 2) { A += (int) Char.GetNumericValue(result.CleanProvidedValue[odd]); } for (int even = 1; even <= 12; even = even + 2) { strB += result.CleanProvidedValue[even].ToString(); } B = Int32.Parse(strB); strBtimes2 = (B*2).ToString(); for (int i = 0; i < strBtimes2.Length; i++) { C += (int) Char.GetNumericValue(strBtimes2[i]); } D = A + C; Z = 10 - (D%10); if (Z == (Int32.Parse(controlPart))) controlOK = true; /* COUNTER CHECKING */ /***********************/ /* COUNTERPART MUST BE BETWEEN 0000 AND 9999 * 0000 to 4999 FOR FEMALE * 5000 to 9999 ODD FOR MALE */ var counter = int.Parse(counterPart); if (counter < 5000) { counterOK = true; gender = "F"; //FEMALE } else { counterOK = true; gender = "M"; //MALE } /* CITIZENSHIP CHECKING */ /*************************/ citizenship = Int32.Parse(citizenshipPart); citizenshipOK = true; /* RACE CHECKING */ /*************************/ race = Int32.Parse(racePart); raceOK = true; /* BIRTHDATE CHECKING */ /*************************/ //remark: system below assumes provided national id numbers contain birthdates from today or earlier, and the cutoff limit is 100 years back var d = birthDatePart; int currentYear = DateTime.Now.Year; string currentYearCentury = currentYear.ToString().Substring(0, 2); d = currentYearCentury + d; var format = "yyyyMMdd"; birthDateOK = DateTime.TryParseExact(d, format, CultureInfo.CurrentCulture, DateTimeStyles.None, out birthDate); if (birthDate > DateTime.Now.Date) { birthDate = birthDate.AddYears(-100); } /* 4. PROCESS RESULTS */ /**********************/ if (!birthDateOK) { result.IsValid = false; result.ValidationErrors.Add("Birthdate part not valid"); } if (!counterOK) { result.IsValid = false; result.ValidationErrors.Add("Counter part not valid"); } if (!citizenshipOK) { result.IsValid = false; result.ValidationErrors.Add("Citizenship part not valid"); } if (!raceOK) { result.IsValid = false; result.ValidationErrors.Add("Race part not valid"); } if (!controlOK) { result.IsValid = false; result.ValidationErrors.Add("Controlnumber part not valid"); } if (birthDateOK) ((NationalIDAdditionalData)(result.AdditionalData)).BirthDate = birthDate; if (gender == "F") ((NationalIDAdditionalData)(result.AdditionalData)).Gender = GenderEnum.Female; else if (gender == "M") ((NationalIDAdditionalData)(result.AdditionalData)).Gender = GenderEnum.Male; } } if (!result.IsValid.HasValue) { result.IsValid = true; result.ValidatedValue = idnumber; } else //result.IsValid == false { result.AdditionalData = null; } } else throw new Exception("Number is empty"); } catch (Exception ex) { result.ValidationException = ex; result.IsValid = null; result.ValidatedValue = null; result.AdditionalData = null; } return result; }
public IDNumberValidationResult Validate(string number) { var result = new IDNumberValidationResult(number); result.CleanProvidedValue = number.ToAlphaNumericOnly().TrimStart("BE").TrimStart("be").Trim(); try { if (!string.IsNullOrEmpty(result.CleanProvidedValue)) { var cbenumber = string.Copy(result.CleanProvidedValue); if (cbenumber.Length == 9) //possibly old VAT numbers consist of only 9 characters { cbenumber = "0" + cbenumber; } if (cbenumber.Length != 10) { result.IsValid = false; result.ValidationErrors.Add("Length != 10"); } else { var numberBody = cbenumber.Substring(0, 8); var controlNumber = cbenumber.Substring(8, 2); if (numberBody[0].Equals('9')) //must start by 0 - 8 { result.IsValid = false; result.ValidationErrors.Add("Number must start by 0 - 8"); } else { //CALCULATE CONTROLNUMER (= MOD 97 OF FIRST 9 DIGITS) var calculatedControl = 97 - (int)(long.Parse(numberBody) % 97); if (calculatedControl != int.Parse(controlNumber)) { result.IsValid = false; result.ValidationErrors.Add("Controlnumber part not valid"); } } } if (!result.IsValid.HasValue) { result.IsValid = true; result.ValidatedValue = cbenumber; } } else throw new Exception("Number is empty"); } catch (Exception ex) { result.ValidationException = ex; result.IsValid = null; result.ValidatedValue = null; result.AdditionalData = null; } return result; }