UNPKG

finnish-ssn

Version:

Small utility for validating and creating Finnish social security numbers. No more, no less, no dependencies.

151 lines 5.75 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.FinnishSSN = void 0; var Sex; (function (Sex) { Sex["FEMALE"] = "female"; Sex["MALE"] = "male"; })(Sex || (Sex = {})); class FinnishSSN { /** * Parse parameter given SSN string into Object representation. * @param ssn - {String} SSN to parse */ static parse(ssn) { // Sanity and format check, which allows to make safe assumptions on the format. if (!SSN_REGEX.test(ssn)) { throw new Error('Not valid SSN format'); } const dayOfMonth = parseInt(ssn.substring(0, 2), 10); const month = ssn.substring(2, 4); const centuryId = ssn.charAt(6); const year = parseInt(ssn.substring(4, 6), 10) + centuryMap.get(centuryId); const rollingId = ssn.substring(7, 10); const checksum = ssn.substring(10, 11); const sex = parseInt(rollingId, 10) % 2 ? this.MALE : this.FEMALE; const daysInMonth = daysInGivenMonth(year, month); if (!daysInMonthMap.get(month) || dayOfMonth > daysInMonth) { throw new Error('Not valid SSN'); } const checksumBase = parseInt(ssn.substring(0, 6) + rollingId, 10); const dateOfBirth = new Date(year, parseInt(month, 10) - 1, dayOfMonth, 0, 0, 0, 0); const today = new Date(); return { valid: checksum === checksumTable[checksumBase % 31], sex, dateOfBirth, ageInYears: ageInYears(dateOfBirth, today), }; } /** * Validates parameter given SSN. Returns true if SSN is valid, otherwise false. * @param ssn - {String} For example '010190-123A' */ static validate(ssn) { try { return this.parse(ssn).valid; } catch (error) { return false; } } /** * Creates a valid SSN using the given age (Integer). Creates randomly male and female SSN'n. * In case an invalid age is given, throws exception. * * @param age as Integer. Min valid age is 1, max valid age is 200 */ static createWithAge(age) { if (age < MIN_AGE || age > MAX_AGE) { throw new Error(`Given age (${age}) is not between sensible age range of ${MIN_AGE} and ${MAX_AGE}`); } const today = new Date(); let year = today.getFullYear() - age; const month = randomMonth(); let dayOfMonth = randomDay(year, month); const rollingId = randomNumber(800) + 99; // No need for padding when rollingId >= 100 if (!birthDayPassed(new Date(year, Number(month) - 1, Number(dayOfMonth)), today)) { if (this.isLeapYear(year)) { if (dayOfMonth === '29' && month === february) { dayOfMonth = '28'; } } year--; } const possibleCenturySigns = []; centuryMap.forEach((value, key) => { if (value === Math.floor(year / 100) * 100) { possibleCenturySigns.push(key); } }); const centurySign = possibleCenturySigns[Math.floor(Math.random() * possibleCenturySigns.length)]; year = year % 100; const yearString = yearToPaddedString(year); const checksumBase = parseInt(dayOfMonth + month + yearString + rollingId, 10); const checksum = checksumTable[checksumBase % 31]; return dayOfMonth + month + yearString + centurySign + rollingId + checksum; } static isLeapYear(year) { return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0; } } exports.FinnishSSN = FinnishSSN; FinnishSSN.FEMALE = Sex.FEMALE; FinnishSSN.MALE = Sex.MALE; const centuryMap = new Map(); centuryMap.set('F', 2000); centuryMap.set('E', 2000); centuryMap.set('D', 2000); centuryMap.set('C', 2000); centuryMap.set('B', 2000); centuryMap.set('A', 2000); centuryMap.set('U', 1900); centuryMap.set('V', 1900); centuryMap.set('W', 1900); centuryMap.set('X', 1900); centuryMap.set('Y', 1900); centuryMap.set('-', 1900); centuryMap.set('+', 1800); const february = '02'; const daysInMonthMap = new Map(); daysInMonthMap.set('01', 31); daysInMonthMap.set('02', 28); daysInMonthMap.set('03', 31); daysInMonthMap.set('04', 30); daysInMonthMap.set('05', 31); daysInMonthMap.set('06', 30); daysInMonthMap.set('07', 31); daysInMonthMap.set('08', 31); daysInMonthMap.set('09', 30); daysInMonthMap.set('10', 31); daysInMonthMap.set('11', 30); daysInMonthMap.set('12', 31); const checksumTable = '0123456789ABCDEFHJKLMNPRSTUVWXY'.split(''); const MIN_AGE = 1; const MAX_AGE = 200; const SSN_REGEX = /^(0[1-9]|[12]\d|3[01])(0[1-9]|1[0-2])([5-9]\d\+|\d\d[-U-Y]|[012]\d[A-F])\d{3}[\dA-Z]$/; function randomMonth() { return `00${randomNumber(12)}`.substr(-2, 2); } function yearToPaddedString(year) { return year % 100 < 10 ? `0${year}` : year.toString(); } function randomDay(year, month) { const maxDaysInMonth = daysInGivenMonth(year, month); return `00${randomNumber(maxDaysInMonth)}`.substr(-2, 2); } function daysInGivenMonth(year, month) { const daysInMonth = daysInMonthMap.get(month); return month === february && FinnishSSN.isLeapYear(year) ? daysInMonth + 1 : daysInMonth; } function randomNumber(max) { return Math.floor(Math.random() * max) + 1; // no zero } function ageInYears(dateOfBirth, today) { return today.getFullYear() - dateOfBirth.getFullYear() - (birthDayPassed(dateOfBirth, today) ? 0 : 1); } function birthDayPassed(dateOfBirth, today) { return (dateOfBirth.getMonth() < today.getMonth() || (dateOfBirth.getMonth() === today.getMonth() && dateOfBirth.getDate() <= today.getDate())); } //# sourceMappingURL=finnish-ssn.js.map