Skip to content

Commit

Permalink
Merge pull request #503 from personnummer/feat/tp-number
Browse files Browse the repository at this point in the history
Add interim number support
  • Loading branch information
frozzare authored Mar 9, 2023
2 parents 323bc93 + 04b2d05 commit 635ab20
Show file tree
Hide file tree
Showing 4 changed files with 271 additions and 159 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ node_modules
*.log
/index.js
yarn.lock
/main.ts
4 changes: 2 additions & 2 deletions pinefile.js → pinefile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ export default {
log.info('Building esm');
await build(buildOptions('esm'));
},
test: async () => {
test: async (args) => {
const files = isCI ? ['./dist/cjs', './dist/esm'] : ['./src'];

await series(
files.map((file) => async () => {
log.info(`Running tests with ${file}\n`);
await run(`FILE=${file} vitest`);
await run(`FILE=${file} vitest ${args._}`);
})
);
},
Expand Down
65 changes: 49 additions & 16 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { PersonnummerError } from './errors';
import { diffInYears, luhn, testDate } from './utils';

type OptionsType = {
[key: string]: boolean | number | string;
allowCoordinationNumber: boolean;
allowInterimNumber: boolean;
};

class Personnummer {
Expand Down Expand Up @@ -137,23 +138,27 @@ class Personnummer {
/**
* Personnummer constructor.
*
* @param {string} ssn
* @param {string} pin
* @param {object} options
*/
constructor(ssn: string, options?: OptionsType) {
this.parse(ssn, options);
constructor(pin: string, options?: OptionsType) {
this.parse(pin, {
allowCoordinationNumber: true,
allowInterimNumber: false,
...options,
});
}

/**
* Parse personnummer.
*
* @param {string} ssn
* @param {string} pin
* @param {object} options
*
* @return {Personnummer}
*/
static parse(ssn: string, options?: OptionsType): Personnummer {
return new Personnummer(ssn, options);
static parse(pin: string, options?: OptionsType): Personnummer {
return new Personnummer(pin, options);
}

/**
Expand All @@ -164,9 +169,9 @@ class Personnummer {
*
* @return {boolean}
*/
static valid(ssn: string, options?: OptionsType): boolean {
static valid(pin: string, options?: OptionsType): boolean {
try {
Personnummer.parse(ssn, options);
Personnummer.parse(pin, options);
return true;
} catch (e) {
return false;
Expand All @@ -176,13 +181,18 @@ class Personnummer {
/**
* Parse personnummer and set class properties.
*
* @param {string} ssn
* @param {string} pin
* @param {object} options
*/
// eslint-disable-next-line
private parse(ssn: string, options?: OptionsType) {
const reg = /^(\d{2}){0,1}(\d{2})(\d{2})(\d{2})([+-]?)((?!000)\d{3})(\d)$/;
const match = reg.exec(ssn);
private parse(pin: string, options?: OptionsType) {
if (pin.length < 10 || pin.length > 13) {
throw new PersonnummerError();
}

const reg =
/^(\d{2}){0,1}(\d{2})(\d{2})(\d{2})([+-]?)((?!000)\d{3}|[TRSUWXJKLMN]\d{2})(\d)$/;

const match = reg.exec(pin);

if (!match) {
throw new PersonnummerError();
Expand Down Expand Up @@ -232,6 +242,16 @@ class Personnummer {
if (!this.valid()) {
throw new PersonnummerError();
}

// throw error if coordination numbers is not allowed.
if (!options?.allowCoordinationNumber && this.isCoordinationNumber()) {
throw new PersonnummerError();
}

// throw error if interim numbers is not allowed.
if (!options?.allowInterimNumber && this.isInterimNumber()) {
throw new PersonnummerError();
}
}

/**
Expand All @@ -241,8 +261,12 @@ class Personnummer {
*/
private valid(): boolean {
const valid =
luhn(this.year + this.month + this.day + this.num) === +this.check &&
!!this.check;
luhn(
this.year +
this.month +
this.day +
this.num.replace(/[TRSUWXJKLMN]/, '1')
) === +this.check && !!this.check;

if (
valid &&
Expand Down Expand Up @@ -307,6 +331,15 @@ class Personnummer {
return new Date(ageDate);
}

/**
* Check if a Swedish personal identity number is a interim number or not.
*
* @return {boolean}
*/
isInterimNumber(): boolean {
return /[TRSUWXJKLMN]/.test(this.num[0]);
}

/**
* Check if a Swedish personal identity number is a coordination number or not.
*
Expand Down
Loading

0 comments on commit 635ab20

Please sign in to comment.