summaryrefslogtreecommitdiff
path: root/index.js
diff options
context:
space:
mode:
Diffstat (limited to 'index.js')
-rw-r--r--index.js125
1 files changed, 125 insertions, 0 deletions
diff --git a/index.js b/index.js
new file mode 100644
index 0000000..be7bf61
--- /dev/null
+++ b/index.js
@@ -0,0 +1,125 @@
+module.exports = {
+ parse: parse,
+ validate: validate,
+ parseDateOfBirth: parseDateOfBirth,
+ parseIsMale: parseIsMale,
+ parseIsFemale: parseIsFemale,
+ parseIsSouthAfricanCitizen: parseIsSouthAfricanCitizen
+};
+
+function parse(idNumber) {
+ if (typeof(idNumber) !== 'string') {
+ return undefined;
+ }
+
+ var isValid = validate(idNumber);
+ if (!isValid) {
+ return {
+ valid: false
+ };
+ }
+
+ return {
+ isValid: isValid,
+ dateOfBirth: parseDateOfBirth(idNumber),
+ isMale: parseIsMale(idNumber),
+ isFemale: parseIsFemale(idNumber),
+ isSouthAfricanCitizen: parseIsSouthAfricanCitizen(idNumber)
+ };
+}
+
+function validate(idNumber) {
+ if (!regexpValidate(idNumber) || !datePartValidate(idNumber) || !controlDigitValidate(idNumber)) {
+ return false;
+ }
+
+ return true;
+}
+
+function regexpValidate(idNumber) {
+ if (typeof(idNumber) !== 'string') {
+ return false;
+ }
+ var regexp = /^[0-9]{13}$/;
+ return regexp.test(idNumber);
+}
+
+function datePartValidate(idNumber) {
+ var dateOfBirth = parseDateOfBirth(idNumber);
+ return !!dateOfBirth;
+}
+
+function controlDigitValidate(idNumber) {
+ var checkDigit = parseInt(idNumber[idNumber.length - 1], 10);
+
+ var oddDigitsSum = 0;
+
+ for (var i = 0; i < idNumber.length - 1; i+=2) {
+ oddDigitsSum += parseInt(idNumber[i], 10);
+ }
+ var evenDigits = '';
+ for (var j = 1; j < idNumber.length - 1; j+=2) {
+ evenDigits += idNumber[j];
+ }
+ evenDigits = parseInt(evenDigits, 10);
+ evenDigits *= 2;
+ evenDigits = '' + evenDigits;
+
+ var sumOfEvenDigits = 0;
+ for (var k = 0; k < evenDigits.length; k++) {
+ sumOfEvenDigits += parseInt(evenDigits[k], 10);
+ }
+ var total = sumOfEvenDigits + oddDigitsSum;
+ var computedCheckDigit = 10 - (total % 10);
+
+ if (computedCheckDigit === 10) {
+ computedCheckDigit = 0;
+ }
+ return computedCheckDigit === checkDigit;
+}
+
+function parseDateOfBirth(idNumber) {
+ if (!regexpValidate(idNumber)) {
+ return undefined;
+ }
+
+ //get year, and assume century
+ var currentYear = new Date().getFullYear();
+ var currentCentury = Math.floor(currentYear/100)*100;
+ var yearPart = currentCentury + Number(idNumber.substring(0,2));
+ if (yearPart > currentYear) {
+ yearPart -= 100; //must be last century
+ }
+
+ //In Javascript, Jan=0. In ID Numbers, Jan=1.
+ var monthPart = Number(idNumber.substring(2,4))-1;
+
+ var dayPart = Number(idNumber.substring(4,6));
+
+ var dateOfBirth = new Date(yearPart, monthPart, dayPart);
+
+ //validate that date is in a valid range by making sure that it wasn't 'corrected' during construction
+ if (!dateOfBirth || dateOfBirth.getFullYear() !== yearPart || dateOfBirth.getMonth() !== monthPart || dateOfBirth.getDate() !== dayPart) {
+ return undefined;
+ }
+
+ return dateOfBirth;
+}
+
+function parseIsMale(idNumber) {
+ return !parseIsFemale(idNumber);
+}
+
+function parseIsFemale(idNumber) {
+ if (!regexpValidate(idNumber)) {
+ return undefined;
+ }
+ return Number(idNumber[6]) <= 4;
+}
+
+function parseIsSouthAfricanCitizen(idNumber) {
+ if (!regexpValidate(idNumber)) {
+ return undefined;
+ }
+ return Number(idNumber[6]) === 0;
+}