summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJustin Worthe <justin.worthe@gmail.com>2015-12-13 20:58:37 +0200
committerJustin Worthe <justin.worthe@gmail.com>2015-12-13 20:58:37 +0200
commitf40fb252a30d78740e5fbdb246e05cd8ff4f888b (patch)
treef3238e3a014e8b992d369e250c1225e0d3579475
Initial commit
-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"
+}