-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.js
118 lines (105 loc) · 3.6 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
/*
* ZA National Id Validation
* Based of: https://www.westerncape.gov.za/general-publication/decoding-your-south-african-id-number-0
* ----------------------------------
* Validation rules:
* 13 - digit number
* {6} (YYMMDD) date of birth definition
* {4} (SSSS) used to define your gender
* Females are assigned numbers in the range 0000 - 4999 and males from 5000 - 9999
* {1} (C) classify citizenship
* SA citizen: 0, Permanent resident: 1
* {1} no longer used
* {1} checksum digit for verification
*/
// Uses the Luhn Algorithm
// Adapted from https://github.com/tiaanduplessis/mod10-check-digit/blob/master/index.js
export const mod10CheckDigit = ({ number }) => {
if (typeof number !== 'string') {
throw new Error('Input should be string');
}
const doubledSum = number
.split('')
.map((val, index) => {
if ((index + number.length) % 2 !== 0) {
const double = Number.parseInt(val, 10) * 2;
return double > 9 ? double - 9 : double;
}
return val;
})
.reduce(
(acc, curr) => Number.parseInt(acc, 10) + Number.parseInt(curr, 10)
);
return (doubledSum * 9) % 10;
};
// Based on date-fns answer: https://github.com/date-fns/date-fns/issues/800#issuecomment-403581282
export const isValidDate = dateString => {
const dateObject = new Date(dateString);
let [year, month, day] = dateString.split('/');
// need to reduce month value by 1 to accommodate new Date formats the month
const formattedMonth = month - 1;
if (
dateObject.getFullYear() == year &&
dateObject.getMonth() == formattedMonth &&
dateObject.getDate() == day
) {
return true;
}
return false;
};
const NATIONAL_ID_VALID_LENGTH = 13;
export default function nationalIdNumber({
number,
minAge = 18,
errorMessages = {
format: false,
date: false,
age: false,
checksum: false,
short: false,
long: false
}
}) {
// Check it's a string
// -----------------------------------------
if (typeof number !== 'string') {
throw new Error('Input should be string');
}
if (number.length < NATIONAL_ID_VALID_LENGTH) {
return errorMessages.short;
}
if (number.length > NATIONAL_ID_VALID_LENGTH) {
return errorMessages.long;
}
// Basic format check first
// -----------------------------------------
const re = /^\d{2}[0-1][0-9][0-3]\d\d{4}[0-1]\d{2}$/;
if (!re.test(number)) {
return errorMessages.format;
}
// Now the date checks
// -----------------------------------------
const currentYear = new Date().getFullYear().toString();
// Dob year is only YY, so we need to guess whether the user is born in 2000s or 1900s
const century =
number.substring(0, 2) < currentYear.substring(2, 4) ? '20' : '19';
const year = `${century}${number.substring(0, 2)}`;
const month = number.substring(2, 4);
const day = number.substring(4, 6);
// Check if valid date
if (!isValidDate(`${year}/${month}/${day}`)) {
return errorMessages.date;
}
// Check if user in age rage
if (currentYear - year < minAge) {
return errorMessages.age;
}
// Lastly the CheckSum
// -----------------------------------------
const numberToValidate = number.substring(0, 12);
const checksumDigit = Number.parseInt(number.substring(12, 13), 10);
if (mod10CheckDigit({ number: numberToValidate }) !== checksumDigit) {
return errorMessages.checksum;
}
return true;
}