Java 居民身份证号校验工具类

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * Description:
 *
 * @date 2021/12/30
 */
public class IDCardValid {
    /**
     * 身份证号码中的出生日期的格式
     */
    private final static String BIRTH_DATE_FORMAT = "yyyyMMdd";

    /**
     * 18位身份证中最后一位校验码
     */
    private final static char[] VERIFY_CODE = { '1', '0', 'X', '9', '8', '7',
            '6', '5', '4', '3', '2' };

    /**
     * 18位身份证中,各个数字的生成校验码时的权值
     */
    private final static int[] VERIFY_CODE_WEIGHT = { 7, 9, 10, 5, 8, 4, 2, 1,
            6, 3, 7, 9, 10, 5, 8, 4, 2 };

    final static Map<Integer, String> zoneNum = new HashMap<>();
    static {
        zoneNum.put(11, "北京");
        zoneNum.put(12, "天津");
        zoneNum.put(13, "河北");
        zoneNum.put(14, "山西");
        zoneNum.put(15, "内蒙古");
        zoneNum.put(21, "辽宁");
        zoneNum.put(22, "吉林");
        zoneNum.put(23, "黑龙江");
        zoneNum.put(31, "上海");
        zoneNum.put(32, "江苏");
        zoneNum.put(33, "浙江");
        zoneNum.put(34, "安徽");
        zoneNum.put(35, "福建");
        zoneNum.put(36, "江西");
        zoneNum.put(37, "山东");
        zoneNum.put(41, "河南");
        zoneNum.put(42, "湖北");
        zoneNum.put(43, "湖南");
        zoneNum.put(44, "广东");
        zoneNum.put(45, "广西");
        zoneNum.put(46, "海南");
        zoneNum.put(50, "重庆");
        zoneNum.put(51, "四川");
        zoneNum.put(52, "贵州");
        zoneNum.put(53, "云南");
        zoneNum.put(54, "西藏");
        zoneNum.put(61, "陕西");
        zoneNum.put(62, "甘肃");
        zoneNum.put(63, "青海");
        zoneNum.put(64, "宁夏");
        zoneNum.put(65, "新疆");
        zoneNum.put(71, "台湾");
        zoneNum.put(81, "香港");
        zoneNum.put(82, "澳门");
        zoneNum.put(91, "外国");
    }

    /**
     * 判断18位身份证的合法性
     * 根据〖中华人民共和国国家标准GB11643-1999〗中有关公民身份号码的规定,公民身份号码是特征组合码,由十七位数字本体码和一位数字校验码组成。
     * 排列顺序从左至右依次为:六位数字地址码,八位数字出生日期码,三位数字顺序码和一位数字校验码。
     *
     * 顺序码: 表示在同一地址码所标识的区域范围内,对同年、同月、同 日出生的人编定的顺序号,顺序码的奇数分配给男性,偶数分配给女性。
     *
     * 1.前1、2位数字表示:所在省份的代码;
     * 2.第3、4位数字表示:所在城市的代码;
     * 3.第5、6位数字表示:所在区县的代码;
     * 4.第7~14位数字表示:出生年、月、日;
     * 5.第15、16位数字表示:所在地的派出所的代码;
     * 6.第17位数字表示性别:奇数表示男性,偶数表示女性;
     * 7.第18位数字是校检码:也有的说是个人信息码,一般是随计算机的随机产生,用来检验身份证的正确性。校检码可以是0~9的数字,有时也用x表示。
     *
     * 第十八位数字(校验码)的计算方法为:
     * 1.将前面的身份证号码17位数分别乘以不同的系数。从第一位到第十七位的系数分别为:7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4 2
     * 2.将这17位数字和系数相乘的结果相加。
     * 3.用加出来和除以11,看余数是多少?
     * 4.余数只可能有0 1 2 3 4 5 6 7 8 9 10这11个数字。其分别对应的最后一位身份证的号码为1 0 X 9 8 7 6 5 4 3 2
     * 5.通过上面得知如果余数是2,就会在身份证的第18位数字上出现罗马数字的Ⅹ。如果余数是10,身份证的最后一位号码就是2。
     *
     * @param cardNumber
     * @return
     */
    public static boolean isIDCard(String cardNumber){
        if (cardNumber == null || (cardNumber.length() != 15 && cardNumber.length() != 18)) {
            return false;
        }
        final char[] cs = cardNumber.toUpperCase().toCharArray();
        // 校验位数
        int power = 0;
        for (int i = 0; i < cs.length; i++) {
            if (i == cs.length - 1 && cs[i] == 'X') {
                break;// 最后一位可以 是X或x
            }
            if (cs[i] < '0' || cs[i] > '9') {
                return false;
            }
            if (i < cs.length - 1) {
                power += (cs[i] - '0') * VERIFY_CODE_WEIGHT[i];
            }
        }

        // 校验区位码
        if (!zoneNum.containsKey(Integer.valueOf(cardNumber.substring(0, 2)))) {
            return false;
        }

        // 校验年份
        String year = cardNumber.length() == 15 ? "19" + cardNumber.substring(6, 8) : cardNumber.substring(6, 10);

        final int iyear = Integer.parseInt(year);
        if (iyear < 1900 || iyear > Calendar.getInstance().get(Calendar.YEAR)) {
            // 1900年的PASS,超过今年的PASS
            return false;
        }

        // 出生月份
        String month = cardNumber.length() == 15 ? cardNumber.substring(8, 10) : cardNumber.substring(10, 12);
        // 出生天数
        String day = cardNumber.length() == 15 ? cardNumber.substring(10, 12) : cardNumber.substring(12, 14);
        String birthDateStr = year + month + day;
        /**
         * 出生日期中的年、月、日必须正确,比如月份范围是[1,12],
         * 日期范围是[1,31],还需要校验闰年、大月、小月的情况时,
         * 月份和日期相符合
         */
        try {
            // 以真正存在的日期作为出生日期,若是20001131会自动转为20001201
            Date birthDate = new SimpleDateFormat(BIRTH_DATE_FORMAT).parse(birthDateStr);
            String birthdayPart = new SimpleDateFormat(BIRTH_DATE_FORMAT).format(birthDate);
            if (!birthDateStr.equals(birthdayPart)){
                return false;
            }
        } catch (Exception e){
            return false;
        }
        // 校验"校验码"
        return cardNumber.length() == 15 || cs[cs.length - 1] == VERIFY_CODE[power % 11];
    }

    public static void main(String[] args) {
        //15位
        String idcard15 = "130503670401001";
        //18位
        String idcard18 = "370102199111317178";
        System.out.println(isIDCard(idcard15));
        System.out.println(isIDCard(idcard18));
    }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值