Skip to content

Commit

Permalink
Merge pull request #89 from koblas/be_nn
Browse files Browse the repository at this point in the history
fix: small updates to the Belgian number parsing
  • Loading branch information
koblas authored Jul 10, 2023
2 parents 5cf671c + 2c48f89 commit bac69a9
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 72 deletions.
50 changes: 11 additions & 39 deletions src/be/nn.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,28 +38,18 @@ describe('be/nn', () => {
expect(result.error).toBeInstanceOf(InvalidFormat);
});

it('validate:88022999297', () => {
const result = validate('88022999297');
test.each([
'88022999297',
'85073003328',
'20070199922',
'20070199951',
'80000099902',
'00000199938',
'99000099913',
])('validate:%s', value => {
const result = validate(value);

expect(result.isValid && result.compact).toEqual('88022999297');
});

it('validate:85073003328', () => {
const result = validate('85073003328');

expect(result.isValid && result.compact).toEqual('85073003328');
});

it('validate:20070199922', () => {
const result = validate('20070199922');

expect(result.isValid && result.compact).toEqual('20070199922');
});

it('validate:20070199951', () => {
const result = validate('20070199951');

expect(result.isValid && result.compact).toEqual('20070199951');
expect(result.isValid).toEqual(true);
});

it('validate:20070199952', () => {
Expand All @@ -68,12 +58,6 @@ describe('be/nn', () => {
expect(result.error).toBeInstanceOf(InvalidChecksum);
});

it('validate:80000099902', () => {
const result = validate('80000099902');

expect(result.isValid && result.compact).toEqual('80000099902');
});

it('validate:80000099903', () => {
const result = validate('80000099903');

Expand All @@ -86,18 +70,6 @@ describe('be/nn', () => {
expect(result.error).toBeInstanceOf(InvalidChecksum);
});

it('validate:00000199938', () => {
const result = validate('00000199938');

expect(result.isValid && result.compact).toEqual('00000199938');
});

it('validate:99000099913', () => {
const result = validate('99000099913');

expect(result.isValid && result.compact).toEqual('99000099913');
});

it('validate:99000099942', () => {
const result = validate('99000099942');

Expand Down
13 changes: 7 additions & 6 deletions src/be/nn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
* Source
* https://nl.wikipedia.org/wiki/Rijksregisternummer
* https://fr.wikipedia.org/wiki/Numéro_de_registre_national
* https://www.ibz.rrn.fgov.be/fileadmin/user_upload/nl/rr/instructies/IT-lijst/IT000_Rijksregisternummer.pdf
*
* PERSON
*/
Expand Down Expand Up @@ -45,27 +46,27 @@ const impl: Validator = {
return value;
},
validate(input: string): ValidateReturn {
const number = impl.compact(input);
const value = impl.compact(input);

if (!strings.isdigits(number) || parseInt(number, 10) <= 0) {
if (!strings.isdigits(value) || parseInt(value, 10) <= 0) {
return { isValid: false, error: new exceptions.InvalidFormat() };
}

if (number.length !== 11) {
if (value.length !== 11) {
return { isValid: false, error: new exceptions.InvalidLength() };
}

if (!validStructure(number)) {
if (!validStructure(value)) {
return { isValid: false, error: new exceptions.InvalidFormat() };
}

if (!validChecksum(number)) {
if (!validChecksum(value)) {
return { isValid: false, error: new exceptions.InvalidChecksum() };
}

return {
isValid: true,
compact: number,
compact: value,
isIndividual: true,
isCompany: false,
};
Expand Down
43 changes: 30 additions & 13 deletions src/be/personIdentifierHelpers.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { validStructure, validChecksum, toDateArray } from './personIdentifierHelpers';
import {
validStructure,
validChecksum,
toDateArray,
} from './personIdentifierHelpers';

describe('personIdentifierHelpers', () => {
const toDob = (string: string): string => {
Expand Down Expand Up @@ -43,6 +47,7 @@ describe('personIdentifierHelpers', () => {
});
});
});

describe('validChecksum', () => {
const range = Array.from({ length: 100 }, (_, n) => n);

Expand All @@ -58,7 +63,7 @@ describe('personIdentifierHelpers', () => {

it('returns false when the checksum is invalid', () => {
const toCheck = range.filter(x => x !== checksum);
toCheck.forEach((cs) => {
toCheck.forEach(cs => {
const string = `${baseString}${cs}`;
expect(validChecksum(string)).toEqual(false);
});
Expand All @@ -76,7 +81,7 @@ describe('personIdentifierHelpers', () => {

it('returns false when the checksum is invalid', () => {
const toCheck = range.filter(x => x !== checksum);
toCheck.forEach((cs) => {
toCheck.forEach(cs => {
const string = `${baseString}${cs}`;
expect(validChecksum(string)).toEqual(false);
});
Expand All @@ -99,9 +104,12 @@ describe('personIdentifierHelpers', () => {
});

it('returns false when the checksum is invalid', () => {
const excludedChecksums = [twentiethCenturyChecksum, twentyfirstCenturyChecksum];
const excludedChecksums = [
twentiethCenturyChecksum,
twentyfirstCenturyChecksum,
];
const toCheck = range.filter(x => !excludedChecksums.includes(x));
toCheck.forEach((cs) => {
toCheck.forEach(cs => {
const string = `${baseString}${cs}`;
expect(validChecksum(string)).toEqual(false);
});
Expand All @@ -124,9 +132,12 @@ describe('personIdentifierHelpers', () => {
});

it('returns false when the checksum is invalid', () => {
const excludedChecksums = [twentiethCenturyChecksum, twentyfirstCenturyChecksum];
const excludedChecksums = [
twentiethCenturyChecksum,
twentyfirstCenturyChecksum,
];
const toCheck = range.filter(x => !excludedChecksums.includes(x));
toCheck.forEach((cs) => {
toCheck.forEach(cs => {
const string = `${baseString}${cs}`;
expect(validChecksum(string)).toEqual(false);
});
Expand All @@ -146,7 +157,7 @@ describe('personIdentifierHelpers', () => {

it('returns false when the checksum is invalid', () => {
const toCheck = range.filter(x => x !== checksum);
toCheck.forEach((cs) => {
toCheck.forEach(cs => {
const string = `${baseString}${cs}`;
expect(validChecksum(string, toDob)).toEqual(false);
});
Expand All @@ -164,7 +175,7 @@ describe('personIdentifierHelpers', () => {

it('returns false when the checksum is invalid', () => {
const toCheck = range.filter(x => x !== checksum);
toCheck.forEach((cs) => {
toCheck.forEach(cs => {
const string = `${baseString}${cs}`;
expect(validChecksum(string, toDob)).toEqual(false);
});
Expand All @@ -187,9 +198,12 @@ describe('personIdentifierHelpers', () => {
});

it('returns false when the checksum is invalid', () => {
const excludedChecksums = [twentiethCenturyChecksum, twentyfirstCenturyChecksum];
const excludedChecksums = [
twentiethCenturyChecksum,
twentyfirstCenturyChecksum,
];
const toCheck = range.filter(x => !excludedChecksums.includes(x));
toCheck.forEach((cs) => {
toCheck.forEach(cs => {
const string = `${baseString}${cs}`;
expect(validChecksum(string, toDob)).toEqual(false);
});
Expand All @@ -212,9 +226,12 @@ describe('personIdentifierHelpers', () => {
});

it('returns false when the checksum is invalid', () => {
const excludedChecksums = [twentiethCenturyChecksum, twentyfirstCenturyChecksum];
const excludedChecksums = [
twentiethCenturyChecksum,
twentyfirstCenturyChecksum,
];
const toCheck = range.filter(x => !excludedChecksums.includes(x));
toCheck.forEach((cs) => {
toCheck.forEach(cs => {
const string = `${baseString}${cs}`;
expect(validChecksum(string, toDob)).toEqual(false);
});
Expand Down
47 changes: 33 additions & 14 deletions src/be/personIdentifierHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ function getApproximatelyNow() {
}

function isInPast(date: string | number): boolean {
return new Date(`${date}`) <= getApproximatelyNow();
return new Date(String(date)) <= getApproximatelyNow();
}

function getFullYears(yy: string | number): Array<number> {
return [parseInt(`19${yy}`, 10), parseInt(`20${yy}`, 10)];
function getFullYears(yy: string | number): number[] {
const yval = typeof yy === 'string' ? parseInt(yy, 10) : yy;

return [1900 + yval, 2000 + yval];
}

function getFirstSix(number: string): string {
Expand All @@ -26,20 +28,28 @@ function getChecksum(number: string): number {
return parseInt(checksumString, 10);
}

export function toDateArray(number: string): Array<string> {
return strings.splitAt(number, 2, 4, 6).slice(0, 3);
export function toDateArray(number: string): string[] {
const [yy, mm, dd] = strings.splitAt(number, 2, 4, 6);

return [yy, mm, dd];
}

function getValidPastDates(yymmdd: string): Array<string> {
const [yy, mm, dd] = toDateArray(yymmdd);

return getFullYears(yy)
.filter(yyyy => isValidDateCompactYYYYMMDD(`${yyyy}${mm}${dd}`))
.map(yyyy => `${yyyy}-${mm}-${dd}`)
.filter(isInPast);
}

function isUnknownDob(dob: string): boolean {
if (['000001', '002001', '004001'].includes(dob)) {
return true;
}

const [yy, mm, dd] = toDateArray(dob);

return strings.isdigits(yy) && mm === '00' && strings.isdigits(dd);
}

Expand All @@ -55,19 +65,23 @@ function defaultToDob(origFirstSix: string): string {
return origFirstSix;
}

function isValidFirstSix(firstSix: string, toDob: typeof defaultToDob): boolean {
function isValidFirstSix(
firstSix: string,
toDob: typeof defaultToDob,
): boolean {
const dob = toDob(firstSix);
return isUnknownDob(dob) || isValidDob(dob);
}

export function validStructure(number: string, toDob: typeof defaultToDob = defaultToDob): boolean {
export function validStructure(
number: string,
toDob: typeof defaultToDob = defaultToDob,
): boolean {
const firstSix = getFirstSix(number);
return isValidFirstSix(firstSix, toDob);
}

function getChecksumBasesUnknownDob(
baseNumber: string,
): Array<number> {
function getChecksumBasesUnknownDob(baseNumber: string): Array<number> {
const firstSix = getFirstSix(baseNumber);
const [yy] = toDateArray(firstSix);

Expand All @@ -89,13 +103,15 @@ function getChecksumBasesForStandardDob(
return validPastYears.map(year => toChecksumBasis(year, baseNumber));
}

function getChecksumBases(number: string, toDob: typeof defaultToDob): Array<number> {
function getChecksumBases(
number: string,
toDob: typeof defaultToDob,
): Array<number> {
const firstSix = getFirstSix(number);
const dob = toDob(firstSix);
const baseNumber = getBaseNumber(number);

if (isUnknownDob(dob))
return getChecksumBasesUnknownDob(baseNumber);
if (isUnknownDob(dob)) return getChecksumBasesUnknownDob(baseNumber);

return getChecksumBasesForStandardDob(baseNumber, toDob);
}
Expand All @@ -104,7 +120,10 @@ function isValidChecksumPair(checksumBasis: number, checksum: number): boolean {
return !((checksumBasis + checksum) % 97);
}

export function validChecksum(number: string, toDob: typeof defaultToDob = defaultToDob): boolean {
export function validChecksum(
number: string,
toDob: typeof defaultToDob = defaultToDob,
): boolean {
const checksumBases = getChecksumBases(number, toDob);
const checksum = getChecksum(number);
return checksumBases.some(csb => isValidChecksumPair(csb, checksum));
Expand Down

0 comments on commit bac69a9

Please sign in to comment.