验证中国大陆身份证号码

/**
 * 验证中国大陆身份证号码
 * @param {string} idCard 身份证号码
 * @returns {boolean} 是否有效
 */
function validateIdCard(idCard) {
    // 1. 输入验证
    if (typeof idCard !== 'string') return false;

    // 2. 清理和标准化输入
    const cleanedId = idCard.trim().toUpperCase();

    // 3. 长度验证
    if (cleanedId.length !== 15 && cleanedId.length !== 18) return false;

    // 4. 正则表达式验证
    const reg18 = /^[1-9]\d{5}(19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dX]$/;
    const reg15 = /^[1-9]\d{5}\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}$/;

    if (!(reg18.test(cleanedId) || reg15.test(cleanedId))) {
        return false;
    }

    // 5. 18位身份证详细验证
    if (cleanedId.length === 18) {
        // 5.1 校验码验证
        const weightFactors = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
        const checkCodes = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];

        const sum = weightFactors.reduce((acc, factor, index) =>
            acc + parseInt(cleanedId[index], 10) * factor, 0);

        const checkBit = cleanedId[17];
        if (checkCodes[sum % 11] !== checkBit) {
            return false;
        }

        // 5.2 日期验证
        const year = parseInt(cleanedId.substring(6, 10));
        const month = parseInt(cleanedId.substring(10, 12)) - 1;
        const day = parseInt(cleanedId.substring(12, 14));

        // 创建日期对象并验证
        const date = new Date(year, month, day);

        // 检查日期是否有效
        if (
            date.getFullYear() !== year ||
            date.getMonth() !== month ||
            date.getDate() !== day
        ) {
            return false;
        }

        // 5.3 特殊日期检查(如2月29日)
        if (month === 1) { // 2月
            const isLeapYear = (year % 400 === 0) || (year % 100 !== 0 && year % 4 === 0);
            if (day > (isLeapYear ? 29 : 28)) return false;
        }

        // 5.4 其他月份天数检查
        const daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
        if (day > daysInMonth[month]) {
            return false;
        }

        // 5.5 地区代码验证
        const provinceCode = cleanedId.substring(0, 2);
        const validProvinceCodes = new Set([
            '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'
        ]);

        if (!validProvinceCodes.has(provinceCode)) {
            return false;
        }
    }

    // 6. 15位身份证验证
    if (cleanedId.length === 15) {
        const month = parseInt(cleanedId.substring(8, 10)) - 1;
        const day = parseInt(cleanedId.substring(10, 12));

        // 简单日期验证
        if (month < 0 || month > 11 || day < 1 || day > 31) {
            return false;
        }
    }

    return true;
}

 

posted @ 2025-07-03 17:49  龙卷风吹毁停车场  阅读(15)  评论(0)    收藏  举报