身份证号码规则简介
早期的身份证号码即第一代身份证,为15位,1999年开始更名为公民身份证号码,即第二代身份证,为18位,且终身不变。
15位数升为18位数的一般规则是:第一步,在原15位数的第六位数后面补19 ,这样号码即为17位数;第二步,按照国家规定的统一公式计算出第十八位数即校验码。
公民身份号码是特征组合码,由前十七位数字本体码和最后一位数字校验码组成。排列顺序从左至右依次为六位数字地址码,八位数字出生日期码,三位数字顺序码和一位数字校验码。
身份证号码的组成(18位)
- 地址码 身份证号码前六位数,表示编码对象常住户口所在省(直辖市、自治区)、地(市),以及县(市、旗、区)的行政区划代码,按照GB/T2260的规定执行。其中:前一、二位数字表示所在省(直辖市、自治区)的代码;第三、四位数字表示所在地(市)的代码;第五、六位数字表示:所在县(市、旗、区)的代码。
- 出生日期码 身份证号码第七位至十四位:表示编码对象出生的年、月、日,按GB/T7408的规定执行,年、月、日代码之间不用分隔符。
- 顺序码 身份证号码第十五位至十七位:表示在同一地址码所标识的区域范围内,对同年、同月、同日出生的人编定的顺序号,第十七位的奇数分配给男性,偶数分配给女性。
- 校验码 第十八位数:根据本体码,通过采用ISO 7064:1983,MOD 11-2校验码系统计算出校验码。算法可参考下文。前面有提到数字校验码,我们知道校验码也有X的,实质上为罗马字符X,相当于10
15位与18位身份证号码差异
-
出生日期码:15位身份证号码中出生日期码为4位,其中年份代码仅有2位,如590328,代表1959年生。
-
校验码:15位身份证号码中无校验位。
校验码算法
将本体码各位数字乘以对应加权因子并求和,除以11得到余数,根据余数通过校验码对照表查得校验码。
加权因子:
校验码:
算法举例:
本体码为:11010519491231002
第一步: 各位数与对应加权因子乘积求和1 * 7+1 * 9 + 0 * 10 + 1 * 5+ *** = 167
第二步:对求和进行除11得余数167%11=2
第三步:根据余数2对照校验码得X
因此完整身份证号为:11010519491231002X
编码规则的应用
如上所述,身份证号码是基于一定的规则的,从身份证号码中可以挖掘以下信息:
- 身份证长度验证:身份证长度仅存在15位或18位两种长度;
- 身份证输入正确性验证:如身份证号为18位,可根据本体码与校验码得关系验证身份证号码输入有误,需要指出的是,验证通过并不代表身份证号码的真实性;
- 获得户籍注册地信息:大部分情况下,该地即为出身地或籍贯。对应时需留意行政区划代码的更新迭代;
- 出生年月及其正确性;
- 性别:顺序码中奇数为男性,偶数位为女性;
- 根据18位与15位身份证号码编码规则及校验码算法,将15位身份证号码转换为18位身份证号码;
- 小心坑:因校验码为X,实际上有大小写输入不同,请注意兼容或统一。
js代码封装
// idCard.js文件
export default {
/*省,直辖市代码表*/
provinceAndCitys: {
'11': '北京',
'12': '天津',
'13': '河北',
'14': '山西',
'15': '内蒙古',
'21': '辽宁',
'22': '吉林',
'23': '黑龙江',
'31': '上海',
'32': '江苏',
'33': '浙江',
'34': '安徽',
'35': '福建',
'36': '江西',
'37': '山东',
'41': '河南',
'42': '湖北',
'43': '湖南',
'44': '广东',
'45': '广西',
'46': '海南',
'50': '重庆',
'51': '四川',
'52': '贵州',
'53': '云南',
'54': '西藏',
'61': '陕西',
'62': '甘肃',
'63': '青海',
'64': '宁夏',
'65': '新疆',
'71': '台湾',
'81': '香港',
'82': '澳门',
'91': '国外',
},
/*每位加权因子*/
powers: [
'7',
'9',
'10',
'5',
'8',
'4',
'2',
'1',
'6',
'3',
'7',
'9',
'10',
'5',
'8',
'4',
'2',
],
/*第18位校检码*/
parityBit: ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'],
/*性别*/
genders: { male: '男', female: '女' },
/*校验地址码*/
checkAddressCode: function(addressCode) {
let check = /^[1-9]\d{5}$/.test(addressCode);
if (!check) return false;
if (this.provinceAndCitys[addressCode.substring(0, 2)]) {
return true;
} else {
return false;
}
},
/*校验日期码*/
checkBirthDayCode: function(birDayCode) {
let check = /^[1-9]\d{3}((0[1-9])|(1[0-2]))((0[1-9])|([1-2][0-9])|(3[0-1]))$/.test(
birDayCode
);
if (!check) return false;
let yyyy = parseInt(birDayCode.substring(0, 4), 10);
let mm = parseInt(birDayCode.substring(4, 6), 10);
let dd = parseInt(birDayCode.substring(6), 10);
let xdata = new Date(yyyy, mm - 1, dd);
if (xdata > new Date()) {
return false; //生日不能大于当前日期
} else if (
xdata.getFullYear() == yyyy &&
xdata.getMonth() == mm - 1 &&
xdata.getDate() == dd
) {
return true;
} else {
return false;
}
},
/*计算校检码*/
getParityBit: function(idCardNo) {
let id17 = idCardNo.substring(0, 17);
/*加权 */
let power = 0;
for (let i = 0; i < 17; i++) {
power += parseInt(id17.charAt(i), 10) * parseInt(this.powers[i]);
}
/*取模*/
let mod = power % 11;
return this.parityBit[mod];
},
/*验证校检码*/
checkParityBit: function(idCardNo) {
let parityBit = idCardNo.charAt(17).toUpperCase();
if (this.getParityBit(idCardNo) == parityBit) {
return true;
} else {
return false;
}
},
/*校验15位或18位的身份证号码*/
checkIdCardNo: function(idCardNo) {
//15位和18位身份证号码的基本校验
let check = /^\d{15}|(\d{17}(\d|x|X))$/.test(idCardNo);
if (!check) return false;
//判断长度为15位或18位
if (idCardNo.length == 15) {
return this.check15IdCardNo(idCardNo);
} else if (idCardNo.length == 18) {
return this.check18IdCardNo(idCardNo);
} else {
return false;
}
},
//校验15位的身份证号码
check15IdCardNo: function(idCardNo) {
//15位身份证号码的基本校验
let check = /^[1-9]\d{7}((0[1-9])|(1[0-2]))((0[1-9])|([1-2][0-9])|(3[0-1]))\d{3}$/.test(
idCardNo
);
if (!check) return false;
//校验地址码
let addressCode = idCardNo.substring(0, 6);
check = this.checkAddressCode(addressCode);
if (!check) return false;
let birDayCode = '19' + idCardNo.substring(6, 12);
//校验日期码
return this.checkBirthDayCode(birDayCode);
},
//校验18位的身份证号码
check18IdCardNo: function(idCardNo) {
//18位身份证号码的基本格式校验
let check = /^[1-9]\d{5}[1-9]\d{3}((0[1-9])|(1[0-2]))((0[1-9])|([1-2][0-9])|(3[0-1]))\d{3}(\d|x|X)$/.test(
idCardNo
);
if (!check) return false;
//校验地址码
let addressCode = idCardNo.substring(0, 6);
check = this.checkAddressCode(addressCode);
if (!check) return false;
//校验日期码
let birDayCode = idCardNo.substring(6, 14);
check = this.checkBirthDayCode(birDayCode);
if (!check) return false;
//验证校检码
return this.checkParityBit(idCardNo);
},
formateDateCN: function(day) {
let yyyy = day.substring(0, 4);
let mm = day.substring(4, 6);
let dd = day.substring(6);
return yyyy + '-' + mm + '-' + dd;
},
//获取性别、出生日期、年龄信息
getIdCardInfo: function(idCardNo) {
let idCardInfo = {
gender: '', //性别
birthday: '', // 出生日期(yyyy-mm-dd)
age: '',
};
if (idCardNo.length == 15) {
let aday = '19' + idCardNo.substring(6, 12);
idCardInfo.birthday = this.formateDateCN(aday);
if (parseInt(idCardNo.charAt(14)) % 2 == 0) {
idCardInfo.gender = this.genders.female;
} else {
idCardInfo.gender = this.genders.male;
}
} else if (idCardNo.length == 18) {
let aday = idCardNo.substring(6, 14);
idCardInfo.birthday = this.formateDateCN(aday);
if (parseInt(idCardNo.charAt(16)) % 2 == 0) {
idCardInfo.gender = this.genders.female;
} else {
idCardInfo.gender = this.genders.male;
}
}
idCardInfo.age = this.calculationAge(idCardInfo.birthday);
return idCardInfo;
},
/**
* 通过出生日期计算年龄
* @param dateStr yyyy-MM-DD
*/
calculationAge: function(dateStr){
var r = dateStr.match(/^(\d{1,4})(-|\/)(\d{1,2})\2(\d{1,2})$/);
if (r == null) return false;
var d = new Date(r[1], r[3] - 1, r[4]);
if (
d.getFullYear() == r[1] &&
d.getMonth() + 1 == r[3] &&
d.getDate() == r[4]
) {
var Y = new Date().getFullYear()
var M = this.zero0(new Date().getMonth() + 1)
var D = this.zero0(new Date().getDate())
if (M > r[3] || (M === r[3] && D >= r[4])) return Y - r[1]
return Y - r[1] - 1
}
return false;
},
zero0: function(num) {
return `${num < 10 ? '0'+num : num}`
},
/*18位转15位*/
getId15: function(idCardNo) {
if (idCardNo.length == 15) {
return idCardNo;
} else if (idCardNo.length == 18) {
return idCardNo.substring(0, 6) + idCardNo.substring(8, 17);
} else {
return null;
}
},
/*15位转18位*/
getId18: function(idCardNo) {
if (idCardNo.length == 15) {
let id17 = idCardNo.substring(0, 6) + '19' + idCardNo.substring(6);
let parityBit = this.getParityBit(id17);
return id17 + parityBit;
} else if (idCardNo.length == 18) {
return idCardNo;
} else {
return null;
}
},
};
使用:
import idCardTool from './idCard.js';
// 校验身份证
if (!idCardTool.checkIdCardNo(idCard)) {
console.log('请填写正确的证件号码')
}
// 获取身份证的性别、年龄、出生日期
const cardInfo = idCardTool.getIdCardInfo(idCard)
console.log('性别', cardInfo.gender, '年龄',cardInfo.age, '出生日期', cardInfo.birthday)