Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@remato/personal-code-to-birthday

Package Overview
Dependencies
Maintainers
0
Versions
5
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@remato/personal-code-to-birthday - npm Package Compare versions

Comparing version 1.1.0 to 1.1.1

8

package.json
{
"name": "@remato/personal-code-to-birthday",
"version": "1.1.0",
"version": "1.1.1",
"description": "Converts personal identification codes from various countries into birthdate",
"license": "MIT",
"main": "src/index.ts",
"repository": {
"url": "https://github.com/rematocorp/personal-code-to-birthday"
},
"scripts": {

@@ -50,3 +53,4 @@ "build": "ncc build src/index.ts",

]
}
},
"prettier": "@remato/prettier-config"
}

@@ -12,4 +12,5 @@ import personalCodeToBirthday from './index'

test('Latvia', () => {
expect(personalCodeToBirthday('050393-12344')).toEqual({ day: 5, month: 3, year: 1993 })
expect(personalCodeToBirthday('050303-22344')).toEqual({ day: 5, month: 3, year: 2003 })
expect(personalCodeToBirthday('240860-28074')).toEqual({ day: 24, month: 8, year: 1960 })
expect(personalCodeToBirthday('071023-38935')).toEqual({ day: 7, month: 10, year: 2023 })
expect(personalCodeToBirthday('240860-20090')).toEqual({ day: 24, month: 8, year: 1960 })
})

@@ -23,3 +24,2 @@

expect(personalCodeToBirthday('101085-7001')).toEqual({ day: 10, month: 10, year: 1985 })
expect(personalCodeToBirthday('101085+789W')).toEqual({ day: 10, month: 10, year: 1885 })
expect(personalCodeToBirthday('150752-308N')).toEqual({ day: 15, month: 7, year: 1952 })

@@ -30,13 +30,23 @@ expect(personalCodeToBirthday('010100A123D')).toEqual({ day: 1, month: 1, year: 2000 })

test('Sweden', () => {
expect(personalCodeToBirthday('199001011234')).toEqual({ day: 1, month: 1, year: 1990 })
expect(personalCodeToBirthday('8112289874')).toEqual({ day: 28, month: 12, year: 1981 })
expect(personalCodeToBirthday('811228-9874')).toEqual({ day: 28, month: 12, year: 1981 })
expect(personalCodeToBirthday('670919-9530')).toEqual({ day: 19, month: 9, year: 1967 })
expect(personalCodeToBirthday('230919-9533')).toEqual({ day: 19, month: 9, year: 2023 })
expect(personalCodeToBirthday('196709199530')).toEqual({ day: 19, month: 9, year: 1967 })
expect(personalCodeToBirthday('19670919-9530')).toEqual({ day: 19, month: 9, year: 1967 })
expect(personalCodeToBirthday('20230919-9533')).toEqual({ day: 19, month: 9, year: 2023 })
})
test('Norway', () => {
expect(personalCodeToBirthday('01020352345')).toEqual({ day: 1, month: 2, year: 2003 })
expect(personalCodeToBirthday('10021559844')).toEqual({ day: 10, month: 2, year: 2015 })
expect(personalCodeToBirthday('100215 59844')).toEqual({ day: 10, month: 2, year: 2015 })
expect(personalCodeToBirthday('10028439895')).toEqual({ day: 10, month: 2, year: 1984 })
expect(personalCodeToBirthday('27081439368')).toEqual({ day: 27, month: 8, year: 2014 })
})
test('Denmark', () => {
expect(personalCodeToBirthday('050305-4567')).toEqual({ day: 5, month: 3, year: 2005 })
expect(personalCodeToBirthday('050384-1235')).toEqual({ day: 5, month: 3, year: 1984 })
expect(personalCodeToBirthday('070589-8901')).toEqual({ day: 7, month: 5, year: 1989 })
expect(personalCodeToBirthday('230309-4456')).toEqual({ day: 23, month: 3, year: 2009 })
expect(personalCodeToBirthday('050384-4567')).toEqual({ day: 5, month: 3, year: 1984 })
expect(personalCodeToBirthday('230309-3020')).toEqual({ day: 23, month: 3, year: 1909 })
})

@@ -63,3 +73,4 @@

test('Latvia', () => {
expect(personalCodeToBirthday('123456-12345')).toBeNull()
expect(personalCodeToBirthday('071023-38934')).toBeNull()
expect(personalCodeToBirthday('071523-38937')).toBeNull()
})

@@ -79,5 +90,4 @@

test('Sweden', () => {
expect(personalCodeToBirthday('1990010123456')).toBeNull()
expect(personalCodeToBirthday('199013011234')).toBeNull()
expect(personalCodeToBirthday('199001341234')).toBeNull()
expect(personalCodeToBirthday('8112289875')).toBeNull()
expect(personalCodeToBirthday('8115289871')).toBeNull()
})

@@ -87,2 +97,4 @@

expect(personalCodeToBirthday('32129999999')).toBeNull()
expect(personalCodeToBirthday('32132811465')).toBeNull()
expect(personalCodeToBirthday('32112812600')).toBeNull()
})

@@ -92,6 +104,8 @@

expect(personalCodeToBirthday('999999-1234')).toBeNull()
expect(personalCodeToBirthday('051484-4569')).toBeNull()
expect(personalCodeToBirthday('230309-3080')).toBeNull()
})
test('Ukraine', () => {
expect(personalCodeToBirthday('3406105373')).toBeNull()
expect(personalCodeToBirthday('3416105373')).toBeNull()
})

@@ -98,0 +112,0 @@

@@ -12,4 +12,6 @@ import denmarkParser from './parsers/denmark'

export default function personalCodeToBirthday(code: string): ParsedDate | null {
code = code.replace(' ', '')
if (/^\d{10}$/.test(code)) {
return ukraineParser(code)
return ukraineParser(code) || swedenParser(code)
} else if (/^\d{11}$/.test(code)) {

@@ -23,4 +25,8 @@ return estoniaLithuaniaParser(code) || polandParser(code) || norwayParser(code)

return finlandParser(code)
} else if (/^\d{6}[+-A]\d{4}$/.test(code)) {
return finlandParser(code) || denmarkParser(code)
} else if (/^\d{6}[-]\d{4}$/.test(code)) {
return finlandParser(code) || swedenParser(code) || denmarkParser(code)
} else if (/^\d{6}[A]\d{4}$/.test(code)) {
return finlandParser(code)
} else if (/^\d{8}[-]\d{4}$/.test(code)) {
return swedenParser(code)
}

@@ -27,0 +33,0 @@

@@ -5,5 +5,3 @@ import { ParsedDate } from '../types'

export default function denmarkParser(code: string): ParsedDate | null {
if (code.includes('-')) {
code = code.replace('-', '')
}
code = code.replace('-', '')

@@ -26,2 +24,8 @@ const day = parseInt(code.slice(0, 2), 10)

// Checksum was dropped in 2007
// https://en.wikipedia.org/wiki/Personal_identification_number_(Denmark)#New_development_in_2007
if (year < 2007 && !isValidChecksum(code)) {
return null
}
if (!isValidDate(day, month, year)) {

@@ -33,1 +37,21 @@ return null

}
function isValidChecksum(code: string): boolean {
const weights = [4, 3, 2, 7, 6, 5, 4, 3, 2] // Weights for the first 9 digits
// Convert the first 9 digits to an array of numbers
const digits = code.substring(0, 9).split('').map(Number)
// Calculate the weighted sum
const sum = digits.reduce((acc, digit, index) => acc + digit * weights[index], 0)
// Calculate the check digit (modulo 11)
const checksum = sum % 11 === 0 ? 0 : 11 - (sum % 11)
// If the modulo result is 10, it's considered invalid (CPR could not use this number)
if (checksum === 10) {
return false
}
return checksum === parseInt(code[9], 10)
}

@@ -17,5 +17,3 @@ import { ParsedDate } from '../types'

// Handle the century based on the marker
if (centuryMarker === '+') {
year += 1800
} else if (centuryMarker === '-') {
if (centuryMarker === '-') {
year += 1900

@@ -26,3 +24,2 @@ } else if (centuryMarker === 'A') {

// Validate the parsed date
if (!isValidDate(day, month, year)) {

@@ -29,0 +26,0 @@ return null

@@ -6,9 +6,14 @@ import { ParsedDate } from '../types'

export default function latviaParser(code: string): ParsedDate | null {
code = code.replace('-', '')
if (!isValidChecksum(code)) {
return null
}
const day = parseInt(code.slice(0, 2), 10)
const month = parseInt(code.slice(2, 4), 10)
let year = parseInt(code.slice(4, 6), 10)
const centuryCode = parseInt(code[7], 10)
const centuryCode = parseInt(code[6], 10)
// Latvian codes are always from the 20th century, so add 1900
if (centuryCode === 2) {
if (centuryCode === 3) {
year += 2000

@@ -19,3 +24,2 @@ } else {

// Validate the parsed date
if (!isValidDate(day, month, year)) {

@@ -27,1 +31,21 @@ return null

}
// Function to validate the check digit of a Latvian personal code
function isValidChecksum(personalCode: string): boolean {
const weights = [1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
// Convert the first 10 digits to an array of numbers
const digits = personalCode.substring(0, 10).split('').map(Number)
// Calculate the weighted sum
const sum = digits.reduce((acc, digit, index) => acc + digit * weights[index], 0)
// Calculate the check digit
let checksum = sum % 11
if (checksum === 10) {
checksum = 0 // If modulo result is 10, check digit is 0
}
// Return true if the calculated check digit matches the provided check digit
return checksum === parseInt(personalCode[10], 10)
}

@@ -5,2 +5,6 @@ import { ParsedDate } from '../types'

export default function norwayParser(code: string): ParsedDate | null {
if (!isValidChecksum(code)) {
return null
}
const day = parseInt(code.slice(0, 2), 10)

@@ -10,12 +14,11 @@ const month = parseInt(code.slice(2, 4), 10)

const individual = parseInt(code.slice(6, 9), 10)
const centuryMarker = parseInt(code.slice(6, 7), 10)
// Determine century based on individual number and year
if (individual <= 499 || (individual >= 500 && year >= 40)) {
year += 1900
} else if (individual <= 999 && year <= 39) {
// Read more https://en.wikipedia.org/wiki/National_identity_number_(Norway)#Numerical_components
if (centuryMarker <= 4 || centuryMarker === 9) {
year += year >= 40 ? 1900 : 2000
} else if (centuryMarker >= 5 && centuryMarker <= 8) {
year += 2000
}
// Now validate the date after adjusting the century
if (!isValidDate(day, month, year)) {

@@ -27,1 +30,39 @@ return null

}
// Function to validate the check digits of a Norwegian personal code
function isValidChecksum(code: string): boolean {
const weights1 = [3, 7, 6, 1, 8, 9, 4, 5, 2] // Weights for the first checksum
const weights2 = [5, 4, 3, 2, 7, 6, 5, 4, 3, 2] // Weights for the second checksum, including first checksum
// Extract the first 9 digits for checksum calculation
const digits = code.substring(0, 9).split('').map(Number)
// Extract the check digits provided in the input
const providedFirstCheckDigit = parseInt(code[9], 10)
const providedSecondCheckDigit = parseInt(code[10], 10)
// First checksum calculation
const sum1 = digits.reduce((sum, digit, index) => sum + digit * weights1[index], 0)
let firstCheckDigit = 11 - (sum1 % 11)
if (firstCheckDigit === 11) {
firstCheckDigit = 0 // Handle special case where remainder is 11
}
if (firstCheckDigit === 10) {
return false // Invalid first check digit
}
// Add the first check digit for the second checksum calculation
const digitsWithFirstCheck = [...digits, firstCheckDigit]
// Second checksum calculation
const sum2 = digitsWithFirstCheck.reduce((sum, digit, index) => sum + digit * weights2[index], 0)
let secondCheckDigit = 11 - (sum2 % 11)
if (secondCheckDigit === 11) {
secondCheckDigit = 0 // Handle special case where remainder is 11
}
if (secondCheckDigit === 10) {
return false // Invalid second check digit
}
return firstCheckDigit === providedFirstCheckDigit && secondCheckDigit === providedSecondCheckDigit
}

@@ -5,6 +5,28 @@ import { ParsedDate } from '../types'

export default function swedenParser(code: string): ParsedDate | null {
const year = parseInt(code.slice(0, 4), 10)
const month = parseInt(code.slice(4, 6), 10)
const day = parseInt(code.slice(6, 8), 10)
code = code.replace('-', '')
if (!isValidChecksum(code)) {
return null
}
let year: number
let month: number
let day: number
if (code.length === 12) {
year = parseInt(code.slice(0, 4), 10)
month = parseInt(code.slice(4, 6), 10)
day = parseInt(code.slice(6, 8), 10)
} else {
year = parseInt(code.slice(0, 2), 10)
month = parseInt(code.slice(2, 4), 10)
day = parseInt(code.slice(4, 6), 10)
if (new Date().getFullYear() - (1900 + year) > 99) {
year += 2000
} else {
year += 1900
}
}
if (!isValidDate(day, month, year)) {

@@ -16,1 +38,24 @@ return null

}
function isValidChecksum(code: string) {
// Use the last 9 digits without checksum number
const digits = code.length === 12 ? code.slice(2, 11) : code.slice(0, 9)
let sum = 0
for (let i = 0; i < digits.length; i++) {
let digit = parseInt(digits[i])
if (i % 2 === 0) {
digit *= 2
if (digit > 9) {
digit -= 9
}
}
sum += digit
}
const expectedChecksum = (10 - (sum % 10)) % 10
return expectedChecksum === Number(code[code.length - 1])
}
import { ParsedDate } from '../types'
import { isValidDate } from '../utils'

@@ -19,2 +20,6 @@ export default function ukrainianParser(code: string): ParsedDate | null {

if (!isValidDate(day, month, year)) {
return null
}
return { day, month, year }

@@ -21,0 +26,0 @@ }

export function isValidDate(day: number, month: number, year: number): boolean {
const date = new Date(year, month - 1, day) // month is zero-indexed in JS Date
if (year < 1900 || year > new Date().getFullYear()) {
return false
}
return date.getFullYear() === year && date.getMonth() === month - 1 && date.getDate() === day
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc