summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--index.js125
-rw-r--r--package.json17
2 files changed, 142 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;
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..35fea77
--- /dev/null
+++ b/package.json
@@ -0,0 +1,17 @@
+{
+ "name": "south-african-id-parser",
+ "version": "1.0.0",
+ "description": "A library for parsing and validating South African ID Numbers. In South Africa, all residents are assigned an ID Number. From this number, you can derive some information, like ther person's date of birth and their sex. The number also includes a check digit, that you can use to check that it has been captured correctly.",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "keywords": [
+ "South",
+ "Africa",
+ "ID",
+ "Number"
+ ],
+ "author": "Justin Worthe <justin.worthe@gmail.com>",
+ "license": "ISC"
+}