All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.joekerouac.common.tools.util.IDCardUtil Maven / Gradle / Ivy

The newest version!
// Generated by delombok at Fri Mar 14 11:41:38 CST 2025
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE
 * file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file
 * to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
 * License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */
package com.github.joekerouac.common.tools.util;

import java.nio.charset.Charset;
import java.util.Calendar;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.github.joekerouac.common.tools.area.Area;
import com.github.joekerouac.common.tools.area.AreaUtil;
import com.github.joekerouac.common.tools.constant.StringConst;

/**
 * 身份证工具类
 *
 * @author JoeKerouac
 * @date 2022-10-17 19:27
 * @since 2.0.0
 */
public class IDCardUtil {
    @java.lang.SuppressWarnings("all")
    @lombok.Generated
    private static final com.github.joekerouac.common.tools.log.Logger LOGGER = com.github.joekerouac.common.tools.log.LoggerFactory.getLogger(IDCardUtil.class.getName());
    /**
     * 加权表
     */
    private static int[] POWER = new int[] {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2};
    /**
     * 加权因子
     */
    private static char[] DIVISOR = new char[] {'1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'};
    /**
     * 身份证正则
     */
    private static final Pattern ID_CARD_PATTERN = Pattern.compile("[0-9]{17}[0-9xX]");

    /**
     * 根据出生日期随机生成一个身份证号
     *
     * @param birthday
     *            出生日期,格式为yyyyMMdd(本方法不严格验证参数准确性,只会验证长度和是否数字)
     * @return 根据指定出生日期生成的身份证号,参数格式错误时返回null
     */
    public static String create(String birthday) {
        if (!Pattern.matches("[0-9]{8}", birthday)) {
            return null;
        }
        // 身份证号
        String card = StringConst.EMPTY;
        {
            // 随机挑出一个省市
            Map areaMap = getAreaMap(birthday);
            int r1 = (int) (Math.random() * areaMap.size());
            int i = 0;
            for (String befor : areaMap.keySet()) {
                if (r1 == i) {
                    card += befor;
                    break;
                } else {
                    i++;
                }
            }
        }
        {
            // 拼接生日
            card += birthday;
        }
        {
            // 生成三位随机数
            int r2 = (int) (Math.random() * 899 + 100);
            card += r2;
        }
        // 生成最后一位校验码
        int mod = calcMod(card);
        char calcLast = DIVISOR[mod];
        card += calcLast;
        return card;
    }

    /**
     * 检查身份证号是否符合格式
     *
     * @param idCard
     *            身份证号
     * @return 如果身份证号符合身份证格式则返回true
     */
    public static boolean check(String idCard) {
        // 验证身份证格式
        Matcher matcher = ID_CARD_PATTERN.matcher(idCard);
        if (!matcher.matches()) {
            // 格式不对
            LOGGER.error("身份证格式不对{}", idCard);
            return false;
        }
        // 验证最后一位加权码
        byte[] idCardByte = idCard.getBytes(Charset.defaultCharset());
        int mod = calcMod(idCard);
        int calcLast = DIVISOR[mod];
        char last;
        if (idCardByte[17] == 'x' || idCardByte[17] == 'X') {
            last = 'X';
        } else {
            last = (char) idCardByte[17];
        }
        if (last != calcLast) {
            // 格式不对
            LOGGER.error("加权码错误{}", idCard);
            return false;
        }
        return true;
    }

    /**
     * 获取用户所属省份,可能不准(因为有些人出生后很久才上户口,此时可能行政区划代码已经更改了)
     *
     * @param idCard
     *            用户身份证号
     * @return 用户所属省份
     */
    public static String getProvince(String idCard) {
        String birthday = getBirthday(idCard);
        String code = getAreaCode(idCard);
        Map areaMap = getAreaMap(birthday);
        Area area = areaMap == null ? null : areaMap.get(code);
        if (area == null) {
            LOGGER.warn("地区[{}]不存在或者地区已不在最新行政区划代码中,无法获取所属省份", code);
            return null;
        }
        return AreaUtil.getProvince(area, areaMap).getName();
    }

    /**
     * 获取用户所属县市,可能不准(因为有些人出生后很久才上户口,此时可能行政区划代码已经更改了)
     *
     * @param idCard
     *            用户身份证号
     * @return 用户所属县市
     */
    public static String getArea(String idCard) {
        String code = getAreaCode(idCard);
        Map areaMap = getAreaMap(getBirthday(idCard));
        // 用户所属地区
        Area area = areaMap == null ? null : areaMap.get(code);
        if (area == null) {
            LOGGER.warn("地区[{}]不存在或者地区已不在最新行政区划代码中", code);
            return null;
        }
        return AreaUtil.getFullName(area, areaMap);
    }

    /**
     * 获取用于生日
     *
     * @param idCard
     *            身份证号
     * @return 生日,格式yyyyMMdd
     */
    public static String getBirthday(String idCard) {
        return idCard.substring(6, 14);
    }

    /**
     * 获取身份证的区域编码
     *
     * @param idCard
     *            身份证
     * @return 区域编码
     */
    public static String getAreaCode(String idCard) {
        return idCard.substring(0, 6);
    }

    /**
     * 获取用户性别,0是女,1是男
     *
     * @param idCard
     *            用户身份证号
     * @return 用户性别
     */
    public static int getSex(String idCard) {
        // 判断身份证用户性别
        int sexInt = Integer.parseInt(idCard.substring(16, 17));
        return sexInt % 2;
    }

    /**
     * 获取用户年龄,如果2020.01.01出生,那么到2021.01.01都返回1岁,到2021.01.02就返回2岁了
     *
     * @param idCard
     *            用户身份证号
     * @return 用户年龄
     */
    public static int getAge(String idCard) {
        int age;
        // 计算用户年龄
        int year = Integer.parseInt(idCard.substring(6, 10));
        int monthDay = Integer.parseInt(idCard.substring(10, 14));
        Calendar calendar = Calendar.getInstance();
        int nowYear = calendar.get(Calendar.YEAR);
        int nowMonthDay = calendar.get(Calendar.MONTH) * 100 + calendar.get(Calendar.DATE);
        if (nowMonthDay > monthDay) {
            age = nowYear - year + 1;
        } else {
            age = nowYear - year;
        }
        if (age < 0) {
            LOGGER.warn("身份证年龄应该大于等于0,但是实际年龄为{}", age);
        }
        return age;
    }

    /**
     * 计算校验和
     *
     * @param card
     *            身份证号,长度不得低于17位,使用前17位计算校验和
     * @return 校验和
     */
    private static int calcMod(String card) {
        // 生成最后一位校验码
        byte[] idCardByte = card.getBytes(Charset.defaultCharset());
        int sum = 0;
        for (int j = 0; j < 17; j++) {
            sum += (((int) idCardByte[j]) - 48) * POWER[j];
        }
        return sum % 11;
    }

    /**
     * 获取指定年份的区域集合
     *
     * @param birthday
     *            生日,格式:yyyyMMdd
     * @return 该生日对应的区域集合
     */
    private static Map getAreaMap(String birthday) {
        return AreaUtil.getArea(birthday.substring(0, 4) + "." + birthday.substring(4, 6) + "." + birthday.substring(6, 8));
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy