Java 根据身份证号获取性别、年龄、出生日期等信息
介绍
1985年开始颁发15位的第一代居民身份证,自2013年1月1日起,第一代居民身份证将退出历史舞台。
2004年起开始换发18位的第二代居民身份证,把原本在申领时才确立的15位居民身份证号码,替换为出生时编排的终身不变的唯一的18位代码。
1、15 位居民身份证
15 位身份证号码各个位数的含义:
- 第1、2位表示:所在省份的代码
- 第3、4位表示:所在城市的代码
- 第5、6位表示:所在区县的代码
- 第7、8位表示:出生年份(19xx年)
- 第9、10位表示:出生月份
- 第11、12位表示:出生日期
- 第13、14、15位表示:出生顺序编号(其中第15位表示:性别,奇数表示男性,偶数表示女性)
示例:
位数 | 1、2 | 3、4 | 5、6 | 7、8 | 9、10 | 11、12 | 13、14、15 |
---|---|---|---|---|---|---|---|
示例 | 45 | 26 | 26 | 98 | 11 | 20 | 999 |
含义 | 省份代码 | 城市代码 | 县区代码 | 出生年份(1998) | 出生月份 | 出生日期 | 顺序号(15位表示:性别,奇数表示男性,偶数表示女性) |
2、18 位居民身份证
18位身份证号码由17位数字和1位检验码组成,从最左侧的数字开始,不同位数的数字有不同的含义:
-
第1、2位表示:所在省份的代码
-
第3、4位表示:所在城市的代码
-
第5、6位表示:所在区县的代码
-
第7~10位表示:出生年份
-
第11、12位表示:出生月份
-
第13、14位表示:出生日期
-
第15、16位表示:所在地派出所的代码
-
第17位表示:性别(奇数表示男性,偶数表示女性)
-
第18位表示:检验码
示例:
位数 | 1、2 | 3、4 | 5、6 | 7、8、9、10 | 11、12 | 13、14 | 15、16 | 17 | 18 |
---|---|---|---|---|---|---|---|---|---|
示例 | 45 | 26 | 26 | 1998 | 11 | 20 | 99 | 9 | 9 |
含义 | 省份代码 | 城市代码 | 县区代码 | 出生年份 | 出生月份 | 出生日期 | 所在地派出所的代码 | 男 | 检验码 |
检验规则:
1、将身份证号码的前17位分别乘于不同的系数。系数的计算方法为:
第 i 位的系数为:
W
i
=
2
18
−
i
m
o
d
11
;
i
=
{
1
、
2
、
3...17
}
W_i={2^{18-i} \ mod \ 11} \ ;\ i= \{1、2、3...17\}
Wi=218−i mod 11 ; i={1、2、3...17}
2、将这17位数字和系数相乘的结果相加:
S
u
m
=
∑
i
=
1
17
(
W
i
∗
I
d
C
a
r
d
[
i
]
)
Sum = \sum_{i=1}^{17}{(W_i \ * \ IdCard[i] ) }
Sum=i=1∑17(Wi ∗ IdCard[i])
3、将相加的结果取模11,看余数为多少:
i
n
d
e
x
=
S
u
m
m
o
d
11
index = Sum \ mod \ 11
index=Sum mod 11
4、将余数替换成以下对应的值:
余数 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|---|
替换值 | 1 | 0 | X | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 |
3、身份证工具类
/**
* @desc 身份证工具类
* @auth llp
* @date 2022/7/7 16:13
*/
public class IdCardNumberUtils {
/** 检验码:余数作为索引,值代表所替换的值 */
private static final String[] CHECK_INDEX = {"1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2"};
/** 居民身份证 正则表达式 */
private static final String SECOND_ID_CARD_REGULAR_EXP = "(^\\d{18}$)|(^\\d{17}(\\d|X|x)$|(^\\d{15}$))";
/** 居民身份证 年份所在位数 */
private static final int ID_CARD_YEAR_INDEX = 6;
/** 第一代居民身份证长度 */
private static final int FIRST_ID_CARD_LENGTH = 15;
/** 第一代居民身份证 年份值 */
private static final String FIRST_ID_CARD_YEAR = "19";
/** 第二代居民身份证长度 */
private static final int SECOND_ID_CARD_LENGTH = 18;
/** 第二代居民身份证 校验码的模 */
private static final int SECOND_ID_CARD_CHECK_MOD = 11;
/** 性别表示的值 */
private static final int MALE_SEX_INT = 1;
private static final String MALE_SEX_STRING = "男";
private static final int FEMALE_SEX_INT = 2;
private static final String FEMALE_SEX_STRING = "女";
/** 性别 map 中的 key 值 */
public static final String SEX_BY_INT_MAP_KEY = "sex_by_int";
public static final String SEX_BY_STRING_MAP_KEY = "sex_by_string";
/**
* @desc 通过身份证获取年龄
* @auth llp
* @date 2022/7/11 17:09
* @param idCard 身份证
* @return java.lang.String
*/
public static int getAgeFromIdCard(String idCard){
if (!isIdCard(idCard)){
return -1;
}
// 获取身份证的出生年月日串
String birth = getIdCardBirthDayStr(idCard);
// 获取年月日
int year = Integer.parseInt(birth.substring(0, 4));
int month = Integer.parseInt(birth.substring(4, 6));
int day = Integer.parseInt(birth.substring(6, 8));
// 计算年龄
Calendar cal = Calendar.getInstance();
int age = cal.get(Calendar.YEAR) - year;
// 周岁计算
boolean monthLtFlag = cal.get(Calendar.MONTH)+1 < month;
boolean monthDayFlag = cal.get(Calendar.MONTH)+1 == month && cal.get(Calendar.DATE) < day;
if ( monthLtFlag || monthDayFlag){
age--;
}
return age;
}
/**
* @desc 通过身份证获取出生日期
* @auth llp
* @date 2022/7/15 16:39
* @param idCard 身份证
* @return java.lang.String
*/
public static String getBirthDayFromIdCard(String idCard){
if (!isIdCard(idCard)){
return "idCard error!";
}
// 获取身份证的出生年月日串
String birth = getIdCardBirthDayStr(idCard);
// 获取年月日
int year = Integer.parseInt(birth.substring(0, 4));
int month = Integer.parseInt(birth.substring(4, 6));
int day = Integer.parseInt(birth.substring(6, 8));
return year + "-" + month + "-" + day;
}
/**
* @desc 通过身份证获取性别
* @auth llp
* @date 2022/7/15 16:10
* @param idCard 身份证
* @return java.util.Map<java.lang.String,java.lang.Object>
*/
public static Map<String, Object> getSexFromIdCard(String idCard){
if (!isIdCard(idCard)){
return Collections.emptyMap();
}
Map<String, Object> sexMap = new HashMap<>();
// 默认值
int sexInt = -1;
String sexStr = "未知";
// 15 位身份证
if (idCard.length() == FIRST_ID_CARD_LENGTH){
String sex = idCard.substring(14, 15);
// 偶数表示女性,奇数表示男性
if (Integer.parseInt(sex) % 2 == 0){
sexInt = FEMALE_SEX_INT;
sexStr = FEMALE_SEX_STRING;
}else {
sexInt = MALE_SEX_INT;
sexStr = MALE_SEX_STRING;
}
}
// 18 位身份证
if (idCard.length() == SECOND_ID_CARD_LENGTH){
String sex = idCard.substring(16, 17);
// 偶数表示女性,奇数表示男性
if (Integer.parseInt(sex) % 2 == 0){
sexInt = FEMALE_SEX_INT;
sexStr = FEMALE_SEX_STRING;
}else {
sexInt = MALE_SEX_INT;
sexStr = MALE_SEX_STRING;
}
}
// 结果
sexMap.put(SEX_BY_INT_MAP_KEY, sexInt);
sexMap.put(SEX_BY_STRING_MAP_KEY, sexStr);
return sexMap;
}
/**
* @desc 检测是否为身份证
* @auth llp
* @date 2022/7/11 15:32
* @param idCard 身份证
* @return boolean
*/
public static boolean isIdCard(String idCard){
if (StringUtils.isBlank(idCard)){
return false;
}
StringBuilder idCardBuilder = new StringBuilder(idCard);
// 正则表达式
boolean matches = idCard.matches(SECOND_ID_CARD_REGULAR_EXP);
// 第二代身份证的验证
if (matches && idCardBuilder.length() == SECOND_ID_CARD_LENGTH){
int index = getIdCardCheckIndex(idCardBuilder);
// 获取传入身份证的检验码
String check = String.valueOf(idCardBuilder.charAt(idCardBuilder.length()-1));
// 检验码校验
return StringUtils.equalsIgnoreCase(CHECK_INDEX[index], check);
}
return matches;
}
/**
* @desc 将 15位身份证号码转为 18位身份证号码
* @auth llp
* @date 2022/7/11 15:13
* @param idCard 身份证号码
* @return java.lang.String
*/
public static String idCardNumber15To18(String idCard){
if (!isIdCard(idCard) || idCard.length() != FIRST_ID_CARD_LENGTH){
return "idCard error!";
}
StringBuilder idCardBuilder = new StringBuilder(idCard);
// 在 第6位后插入年份
idCardBuilder.insert(ID_CARD_YEAR_INDEX, FIRST_ID_CARD_YEAR);
int index =getIdCardCheckIndex(idCardBuilder);
// 根据余数在校验码数组里取值
idCardBuilder.append(CHECK_INDEX[index]);
return idCardBuilder.toString();
}
/**
* @desc 将 18位身份证号码转为 15位身份证号码
* @auth llp
* @date 2022/7/15 16:40
* @param idCard 身份证号码
* @return java.lang.String
*/
public static String idCardNumber18To15(String idCard){
if (!isIdCard(idCard) || idCard.length() != SECOND_ID_CARD_LENGTH){
return "idCard error!";
}
// 去掉18位身份证的年份前两位 去掉校验码
return idCard.substring(0, 5)+idCard.substring(8, 17);
}
/**
* @desc 计算出校验码所在校验码数组的下标值
* @auth llp
* @date 2022/7/13 9:51
* @param idCardBuilder 身份证
* @return int 检验码下标
*/
private static int getIdCardCheckIndex(StringBuilder idCardBuilder){
// 判断传入的是17位还是18位身份证号
int length = idCardBuilder.length() == SECOND_ID_CARD_LENGTH ? idCardBuilder.length()-1 : idCardBuilder.length();
// 计算出校验码
int sum = 0;
// length=17, i=0、2、3...16
for (int i = 0; i < length; i++) {
// 前17位数字
int numVal = Integer.parseInt(String.valueOf(idCardBuilder.charAt(i)));
int numMultiple = (int) (Math.pow(2, length - i) % SECOND_ID_CARD_CHECK_MOD);
sum += (numVal * numMultiple);
}
// 总和取模11
return sum % SECOND_ID_CARD_CHECK_MOD;
}
/**
* @desc 通过身份证获取出生日期的 str 串
* @auth llp
* @date 2022/7/15 16:37
* @param idCard 身份证
* @return java.lang.String
*/
private static String getIdCardBirthDayStr(String idCard){
// 获取身份证的出生年月日串
if (idCard.length() == FIRST_ID_CARD_LENGTH){
return FIRST_ID_CARD_YEAR + idCard.substring(ID_CARD_YEAR_INDEX, 12);
}
if (idCard.length() == SECOND_ID_CARD_LENGTH){
return idCard.substring(ID_CARD_YEAR_INDEX, 14);
}
return StringUtils.EMPTY;
}
}