using System;
namespace RSAID
{
public sealed class ValidatedRSAID
{
public readonly string IDNumber;
public readonly bool IsValid;
public readonly DateTime? DateOfBirth;
public readonly Gender? Gender;
public readonly bool? IsCitizen;
public ValidatedRSAID(string idNumber)
{
IDNumber = idNumber;
DateOfBirth = null;
Gender = null;
IsCitizen = false;
IsValid = false;
// empty string or not 13 digits? then invalid - return.
if (string.IsNullOrEmpty(IDNumber) || IDNumber.Length != 13)
return;
int i;
// ensure the number contains 13 digits, so we can avoid the expense of a try-catch block later on
for (i = 0; i < 13; i++)
if (idNumber[i] < \'0\' || idNumber[i] > \'9\')
return;
i = 0;
// Get all the digits
var yy = ((idNumber[i++] - \'0\') * 10) + (idNumber[i++] - \'0\');
var mm = ((idNumber[i++] - \'0\') * 10) + (idNumber[i++] - \'0\');
var dd = ((idNumber[i++] - \'0\') * 10) + (idNumber[i++] - \'0\');
var genderValue = (idNumber[i++] - \'0\');
i += 3; // series. 3 digits
var citizenValue = (idNumber[i++] - \'0\');
i++; // uniform. 1 digits.
int checkDigit = (idNumber[i] - \'0\');
// Derive member values
Gender = (genderValue < 5 ? RSAID.Gender.Female : RSAID.Gender.Male);
IsCitizen = citizenValue == 0;
// detect invalid date of birth digits
if (yy < 0 || yy > 99 || mm < 1 || mm > 12 || dd < 1 || dd > 31 ||
(dd > 30 && (mm == 4 || mm == 9 || mm == 6 || mm == 11)))
return;
var currentYear = DateTime.Now.Year % 100;
yy += (yy > currentYear ? 1900 : 2000);
// detect invalid date for febrauary
if (mm == 2 && dd > (28 + (DateTime.IsLeapYear(yy) ? 1 : 0)))
return;
DateOfBirth = new DateTime(yy, mm, dd);
var check = CalculateControlDigit(IDNumber);
IsValid = checkDigit == check;
}
private static Int32 CalculateControlDigit(string idNumber)
{
var oddDigits = 0;
var evenDigits = 0;
var evenCheck = 0;
for (var i = 0; i < 12; i++)
{
var digit = idNumber[i] - \'0\';
if (i % 2 == 0)
oddDigits += digit;
else
evenDigits = (evenDigits * 10) + digit;
}
for (evenDigits *= 2; evenDigits > 0; evenDigits = evenDigits / 10)
evenCheck += evenDigits % 10;
return 10 - ((oddDigits + evenCheck) % 10);
}
}
}