Skip to content

Instantly share code, notes, and snippets.

@Alex4386
Last active April 4, 2025 01:07
Show Gist options
  • Select an option

  • Save Alex4386/e1f7d4391607860eb3e3ac3543cdbe4b to your computer and use it in GitHub Desktop.

Select an option

Save Alex4386/e1f7d4391607860eb3e3ac3543cdbe4b to your computer and use it in GitHub Desktop.
한국 주민등록번호 파싱용 스크립트
const koNatlIDRegex = /^([0-9]{6})(-|)([0-9]{7})$/;
function getBirthdayFromNatlID(idNo: string): {
year: number;
month: number;
date: number;
} | null {
const formatValidityCheck = koNatlIDRegex.test(idNo);
if (!formatValidityCheck) return null;
const parsedResult = koNatlIDRegex.exec(idNo);
const birthdayRegion = parsedResult[1];
const identifierRegion = parsedResult[3];
const birthdayParsed = /^([0-9]{2})([0-9]{2})([0-9]{2})$/.exec(birthdayRegion);
if (!birthdayParsed) return null;
const yearRegionIdentifier = identifierRegion.charAt(0);
let yearRange = 0;
switch (yearRegionIdentifier) {
// backwards compatibility for 1800s
case '9':
case '0':
yearRange = 1800;
break;
case '1':
case '2':
case '5':
case '6':
yearRange = 1900;
break;
case '3':
case '4':
case '7':
case '8':
yearRange = 2000;
break;
}
if (yearRange === 0) return null;
const year = parseInt(birthdayParsed[1], 10) + yearRange;
const month = parseInt(birthdayParsed[2], 10);
const date = parseInt(birthdayParsed[3], 10);
return {
year,
month,
date,
};
}
function getRegistrationDetailsFromNatlIdNo(idNo: string): null | {
isForeigner: boolean;
region: {
raw: number;
name: 'SEOUL' | 'BUSAN' | 'INCHEON' | 'GYEONGGI' | 'GANGWON' | 'CHUNGBUK' | 'DAEJEON' | 'CHUNGNAM' | 'SEJONG' | 'JEONBUK' | 'JEONNAM' | 'GWANGJU' | 'DAEGU' | 'GYEONGBUK' | 'GYEONGNAM' | 'ULSAN' | 'JEJU';
detail?: {
isMetropolitan?: Boolean;
};
};
office: {
raw: number;
};
registerOrder: number;
} {
const formatValidityCheck = koNatlIDRegex.test(idNo);
if (!formatValidityCheck) return null;
const parsedResult = koNatlIDRegex.exec(idNo);
let isForeigner = false;
const birthRangeId = parseInt(parsedResult[3].charAt(0));
switch (birthRangeId) {
case 5:
case 6:
case 7:
case 8:
isForeigner = true;
}
let regionName: 'SEOUL' | 'BUSAN' | 'INCHEON' | 'GYEONGGI' | 'GANGWON' | 'CHUNGBUK' | 'DAEJEON' | 'CHUNGNAM' | 'SEJONG' | 'JEONBUK' | 'JEONNAM' | 'GWANGJU' | 'DAEGU' | 'GYEONGBUK' | 'GYEONGNAM' | 'ULSAN' | 'JEJU' | undefined = undefined;
let isMetropolitan: boolean | undefined = undefined;
const regionCode = parseInt(parsedResult[3].slice(1, 3));
if (0 <= regionCode && regionCode <= 8) {
regionName = 'SEOUL';
} else if (9 <= regionCode && regionCode <= 12) {
regionName = 'BUSAN';
} else if (13 <= regionCode && regionCode <= 15) {
regionName = 'INCHEON';
} else if (16 <= regionCode && regionCode <= 18) {
regionName = 'GYEONGGI';
isMetropolitan = true;
} else if (19 <= regionCode && regionCode <= 25) {
regionName = 'GYEONGGI';
isMetropolitan = false;
} else if (26 <= regionCode && regionCode <= 34) {
regionName = 'GANGWON';
} else if (35 <= regionCode && regionCode <= 39) {
regionName = 'CHUNGBUK';
} else if (regionCode === 40) {
regionName = 'DAEJEON';
} else if (regionCode === 44 || regionCode === 96) {
regionName = 'SEJONG';
} else if (41 <= regionCode && regionCode <= 47) {
regionName = 'CHUNGNAM';
} else if (48 <= regionCode && regionCode <= 54) {
regionName = 'JEONBUK';
} else if (55 <= regionCode && regionCode <= 64) {
regionName = 'JEONNAM';
} else if (65 <= regionCode && regionCode <= 66) {
regionName = 'GWANGJU';
} else if (67 <= regionCode && regionCode <= 70) {
regionName = 'DAEGU';
} else if (71 <= regionCode && regionCode <= 80) {
regionName = 'GYEONGBUK';
} else if (regionCode === 85) {
regionName = 'ULSAN';
} else if (81 <= regionCode && regionCode <= 90) {
regionName = 'GYEONGNAM';
} else if (91 <= regionCode && regionCode <= 95) {
regionName = 'JEJU';
} else {
return null;
}
const officeCode = parseInt(parsedResult[3].slice(3, 5));
const registerOrder = parseInt(parsedResult[3].charAt(5));
return {
isForeigner,
region: {
raw: regionCode,
name: regionName,
detail: isMetropolitan ? {
isMetropolitan,
} : undefined,
},
office: {
raw: officeCode,
},
registerOrder,
};
}
function validateNatlIDNo(idNo: string): boolean {
const formatValidityCheck = koNatlIDRegex.test(idNo);
if (!formatValidityCheck) return false;
const parsedResult = koNatlIDRegex.exec(idNo);
if (parsedResult === null) return false;
const data = parsedResult[1] + parsedResult[3];
const checkSum = parseInt(parsedResult[3].charAt(6));
let checkSumCalcTempSum = 0;
for (let i = 0; i < 8; i++) {
const thisCharacter = data.charAt(i);
const multiplyTarget = (i + 2);
checkSumCalcTempSum += (parseInt(thisCharacter, 10) * multiplyTarget);
}
for (let offset = 0; offset < 4; offset++) {
const thisCharacter = data.charAt(offset + 8);
const multiplyTarget = (offset + 2);
checkSumCalcTempSum += (parseInt(thisCharacter, 10) * multiplyTarget);
}
const checkSumMod = checkSumCalcTempSum % 11;
const calculatedCheckSum = (11 - checkSumMod) % 10;
return calculatedCheckSum === checkSum;
}
function parseNatlId(idNo: string) {
const formatValidityCheck = koNatlIDRegex.test(idNo);
if (!formatValidityCheck) return null;
return {
isValid: validateNatlIDNo(idNo),
birthday: getBirthdayFromNatlID(idNo),
details: getRegistrationDetailsFromNatlIdNo(idNo),
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment