From f40fb252a30d78740e5fbdb246e05cd8ff4f888b Mon Sep 17 00:00:00 2001 From: Justin Worthe Date: Sun, 13 Dec 2015 20:58:37 +0200 Subject: Initial commit --- index.js | 125 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 17 ++++++++ 2 files changed, 142 insertions(+) create mode 100644 index.js create mode 100644 package.json 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 ", + "license": "ISC" +} -- cgit v1.2.3