Java的身份证号码工具类
001/** 002 * Copyright (C) 2009-2010 Yichuan, Fuchun All rights reserved. 003 * Licensed to the Apache Software Foundation (ASF) under one or more 004 * contributor license agreements. See the NOTICE file distributed with 005 * this work for additional information regarding copyright ownership. 006 * The ASF licenses this file to You under the Apache License, Version 2.0 007 * (the "License"); you may not use this file except in compliance with 008 * the License. You may obtain a copy of the License at 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * @(#) IdcardUtils.java Date: 2010-06-17 016 */ 017package my.tools; 018 019import java.text.ParseException; 020import java.text.SimpleDateFormat; 021import java.util.Calendar; 022import java.util.Date; 023import java.util.HashMap; 024import java.util.Map; 025 026import org.apache.commons.lang.StringUtils; 027 028/** 029 * 身份证工具类 030 * 031 * @author June 032 * @version 1.0, 2010-06-17 033 */ 034public class IdcardUtils extends StringUtils { 035 036 /** 中国公民身份证号码最小长度。 */ 037 public static final int CHINA_ID_MIN_LENGTH = 15; 038 039 /** 中国公民身份证号码最大长度。 */ 040 public static final int CHINA_ID_MAX_LENGTH = 18; 041 042 /** 省、直辖市代码表 */ 043 public static final String cityCode[] = { 044 "11", "12", "13", "14", "15", "21", "22", "23", "31", "32", "33", "34", "35", "36", "37", "41", 045 "42", "43", "44", "45", "46", "50", "51", "52", "53", "54", "61", "62", "63", "64", "65", "71", 046 "81", "82", "91" 047 }; 048 049 /** 每位加权因子 */ 050 public static final int power[] = { 051 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2 052 }; 053 054 /** 第18位校检码 */ 055 public static final String verifyCode[] = { 056 "1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2" 057 }; 058 /** 最低年限 */ 059 public static final int MIN = 1930; 060 public static Map<String, String> cityCodes = new HashMap<String, String>(); 061 /** 台湾身份首字母对应数字 */ 062 public static Map<String, Integer> twFirstCode = new HashMap<String, Integer>(); 063 /** 香港身份首字母对应数字 */ 064 public static Map<String, Integer> hkFirstCode = new HashMap<String, Integer>(); 065 static { 066 cityCodes.put("11", "北京"); 067 cityCodes.put("12", "天津"); 068 cityCodes.put("13", "河北"); 069 cityCodes.put("14", "山西"); 070 cityCodes.put("15", "内蒙古"); 071 cityCodes.put("21", "辽宁"); 072 cityCodes.put("22", "吉林"); 073 cityCodes.put("23", "黑龙江"); 074 cityCodes.put("31", "上海"); 075 cityCodes.put("32", "江苏"); 076 cityCodes.put("33", "浙江"); 077 cityCodes.put("34", "安徽"); 078 cityCodes.put("35", "福建"); 079 cityCodes.put("36", "江西"); 080 cityCodes.put("37", "山东"); 081 cityCodes.put("41", "河南"); 082 cityCodes.put("42", "湖北"); 083 cityCodes.put("43", "湖南"); 084 cityCodes.put("44", "广东"); 085 cityCodes.put("45", "广西"); 086 cityCodes.put("46", "海南"); 087 cityCodes.put("50", "重庆"); 088 cityCodes.put("51", "四川"); 089 cityCodes.put("52", "贵州"); 090 cityCodes.put("53", "云南"); 091 cityCodes.put("54", "西藏"); 092 cityCodes.put("61", "陕西"); 093 cityCodes.put("62", "甘肃"); 094 cityCodes.put("63", "青海"); 095 cityCodes.put("64", "宁夏"); 096 cityCodes.put("65", "新疆"); 097 cityCodes.put("71", "台湾"); 098 cityCodes.put("81", "香港"); 099 cityCodes.put("82", "澳门"); 100 cityCodes.put("91", "国外"); 101 twFirstCode.put("A", 10); 102 twFirstCode.put("B", 11); 103 twFirstCode.put("C", 12); 104 twFirstCode.put("D", 13); 105 twFirstCode.put("E", 14); 106 twFirstCode.put("F", 15); 107 twFirstCode.put("G", 16); 108 twFirstCode.put("H", 17); 109 twFirstCode.put("J", 18); 110 twFirstCode.put("K", 19); 111 twFirstCode.put("L", 20); 112 twFirstCode.put("M", 21); 113 twFirstCode.put("N", 22); 114 twFirstCode.put("P", 23); 115 twFirstCode.put("Q", 24); 116 twFirstCode.put("R", 25); 117 twFirstCode.put("S", 26); 118 twFirstCode.put("T", 27); 119 twFirstCode.put("U", 28); 120 twFirstCode.put("V", 29); 121 twFirstCode.put("X", 30); 122 twFirstCode.put("Y", 31); 123 twFirstCode.put("W", 32); 124 twFirstCode.put("Z", 33); 125 twFirstCode.put("I", 34); 126 twFirstCode.put("O", 35); 127 hkFirstCode.put("A", 1); 128 hkFirstCode.put("B", 2); 129 hkFirstCode.put("C", 3); 130 hkFirstCode.put("R", 18); 131 hkFirstCode.put("U", 21); 132 hkFirstCode.put("Z", 26); 133 hkFirstCode.put("X", 24); 134 hkFirstCode.put("W", 23); 135 hkFirstCode.put("O", 15); 136 hkFirstCode.put("N", 14); 137 } 138 139 /** 140 * 将15位身份证号码转换为18位 141 * 142 * @param idCard 143 * 15位身份编码 144 * @return 18位身份编码 145 */ 146 public static String conver15CardTo18(String idCard) { 147 String idCard18 = ""; 148 if (idCard.length() != CHINA_ID_MIN_LENGTH) { 149 return null; 150 } 151 if (isNum(idCard)) { 152 // 获取出生年月日 153 String birthday = idCard.substring(6, 12); 154 Date birthDate = null; 155 try { 156 birthDate = new SimpleDateFormat("yyMMdd").parse(birthday); 157 } catch (ParseException e) { 158 e.printStackTrace(); 159 } 160 Calendar cal = Calendar.getInstance(); 161 if (birthDate != null) 162 cal.setTime(birthDate); 163 // 获取出生年(完全表现形式,如:2010) 164 String sYear = String.valueOf(cal.get(Calendar.YEAR)); 165 idCard18 = idCard.substring(0, 6) + sYear + idCard.substring(8); 166 // 转换字符数组 167 char[] cArr = idCard18.toCharArray(); 168 if (cArr != null) { 169 int[] iCard = converCharToInt(cArr); 170 int iSum17 = getPowerSum(iCard); 171 // 获取校验位 172 String sVal = getCheckCode18(iSum17); 173 if (sVal.length() > 0) { 174 idCard18 += sVal; 175 } else { 176 return null; 177 } 178 } 179 } else { 180 return null; 181 } 182 return idCard18; 183 } 184 185 /** 186 * 验证身份证是否合法 187 */ 188 public static boolean validateCard(String idCard) { 189 String card = idCard.trim(); 190 if (validateIdCard18(card)) { 191 return true; 192 } 193 if (validateIdCard15(card)) { 194 return true; 195 } 196 String[] cardval = validateIdCard10(card); 197 if (cardval != null) { 198 if (cardval[2].equals("true")) { 199 return true; 200 } 201 } 202 return false; 203 } 204 205 /** 206 * 验证18位身份编码是否合法 207 * 208 * @param idCard 身份编码 209 * @return 是否合法 210 */ 211 public static boolean validateIdCard18(String idCard) { 212 boolean bTrue = false; 213 if (idCard.length() == CHINA_ID_MAX_LENGTH) { 214 // 前17位 215 String code17 = idCard.substring(0, 17); 216 // 第18位 217 String code18 = idCard.substring(17, CHINA_ID_MAX_LENGTH); 218 if (isNum(code17)) { 219 char[] cArr = code17.toCharArray(); 220 if (cArr != null) { 221 int[] iCard = converCharToInt(cArr); 222 int iSum17 = getPowerSum(iCard); 223 // 获取校验位 224 String val = getCheckCode18(iSum17); 225 if (val.length() > 0) { 226 if (val.equalsIgnoreCase(code18)) { 227 bTrue = true; 228 } 229 } 230 } 231 } 232 } 233 return bTrue; 234 } 235 236 /** 237 * 验证15位身份编码是否合法 238 * 239 * @param idCard 240 * 身份编码 241 * @return 是否合法 242 */ 243 public static boolean validateIdCard15(String idCard) { 244 if (idCard.length() != CHINA_ID_MIN_LENGTH) { 245 return false; 246 } 247 if (isNum(idCard)) { 248 String proCode = idCard.substring(0, 2); 249 if (cityCodes.get(proCode) == null) { 250 return false; 251 } 252 String birthCode = idCard.substring(6, 12); 253 Date birthDate = null; 254 try { 255 birthDate = new SimpleDateFormat("yy").parse(birthCode.substring(0, 2)); 256 } catch (ParseException e) { 257 e.printStackTrace(); 258 } 259 Calendar cal = Calendar.getInstance(); 260 if (birthDate != null) 261 cal.setTime(birthDate); 262 if (!valiDate(cal.get(Calendar.YEAR), Integer.valueOf(birthCode.substring(2, 4)), 263 Integer.valueOf(birthCode.substring(4, 6)))) { 264 return false; 265 } 266 } else { 267 return false; 268 } 269 return true; 270 } 271 272 /** 273 * 验证10位身份编码是否合法 274 * 275 * @param idCard 身份编码 276 * @return 身份证信息数组 277 * <p> 278 * [0] - 台湾、澳门、香港 [1] - 性别(男M,女F,未知N) [2] - 是否合法(合法true,不合法false) 279 * 若不是身份证件号码则返回null 280 * </p> 281 */ 282 public static String[] validateIdCard10(String idCard) { 283 String[] info = new String[3]; 284 String card = idCard.replaceAll("[//(|//)]", ""); 285 if (card.length() != 8 && card.length() != 9 && idCard.length() != 10) { 286 return null; 287 } 288 if (idCard.matches("^[a-zA-Z][0-9]{9}$")) { // 台湾 289 info[0] = "台湾"; 290 System.out.println("11111"); 291 String char2 = idCard.substring(1, 2); 292 if (char2.equals("1")) { 293 info[1] = "M"; 294 System.out.println("MMMMMMM"); 295 } else if (char2.equals("2")) { 296 info[1] = "F"; 297 System.out.println("FFFFFFF"); 298 } else { 299 info[1] = "N"; 300 info[2] = "false"; 301 System.out.println("NNNN"); 302 return info; 303 } 304 info[2] = validateTWCard(idCard) ? "true" : "false"; 305 } else if (idCard.matches("^[1|5|7][0-9]{6}//(?[0-9A-Z]//)?$")) { // 澳门 306 info[0] = "澳门"; 307 info[1] = "N"; 308 // TODO 309 } else if (idCard.matches("^[A-Z]{1,2}[0-9]{6}//(?[0-9A]//)?$")) { // 香港 310 info[0] = "香港"; 311 info[1] = "N"; 312 info[2] = validateHKCard(idCard) ? "true" : "false"; 313 } else { 314 return null; 315 } 316 return info; 317 } 318 319 /** 320 * 验证台湾身份证号码 321 * 322 * @param idCard 323 * 身份证号码 324 * @return 验证码是否符合 325 */ 326 public static boolean validateTWCard(String idCard) { 327 String start = idCard.substring(0, 1); 328 String mid = idCard.substring(1, 9); 329 String end = idCard.substring(9, 10); 330 Integer iStart = twFirstCode.get(start); 331 Integer sum = iStart / 10 + (iStart % 10) * 9; 332 char[] chars = mid.toCharArray(); 333 Integer iflag = 8; 334 for (char c : chars) { 335 sum = sum + Integer.valueOf(c + "") * iflag; 336 iflag--; 337 } 338 return (sum % 10 == 0 ? 0 : (10 - sum % 10)) == Integer.valueOf(end) ? true : false; 339 } 340 341 /** 342 * 验证香港身份证号码(存在Bug,部份特殊身份证无法检查) 343 * <p> 344 * 身份证前2位为英文字符,如果只出现一个英文字符则表示第一位是空格,对应数字58 前2位英文字符A-Z分别对应数字10-35 345 * 最后一位校验码为0-9的数字加上字符"A","A"代表10 346 * </p> 347 * <p> 348 * 将身份证号码全部转换为数字,分别对应乘9-1相加的总和,整除11则证件号码有效 349 * </p> 350 * 351 * @param idCard 身份证号码 352 * @return 验证码是否符合 353 */ 354 public static boolean validateHKCard(String idCard) { 355 String card = idCard.replaceAll("[//(|//)]", ""); 356 Integer sum = 0; 357 if (card.length() == 9) { 358 sum = (Integer.valueOf(card.substring(0, 1).toUpperCase().toCharArray()[0]) - 55) * 9 359 + (Integer.valueOf(card.substring(1, 2).toUpperCase().toCharArray()[0]) - 55) * 8; 360 card = card.substring(1, 9); 361 } else { 362 sum = 522 + (Integer.valueOf(card.substring(0, 1).toUpperCase().toCharArray()[0]) - 55) * 8; 363 } 364 String mid = card.substring(1, 7); 365 String end = card.substring(7, 8); 366 char[] chars = mid.toCharArray(); 367 Integer iflag = 7; 368 for (char c : chars) { 369 sum = sum + Integer.valueOf(c + "") * iflag; 370 iflag--; 371 } 372 if (end.toUpperCase().equals("A")) { 373 sum = sum + 10; 374 } else { 375 sum = sum + Integer.valueOf(end); 376 } 377 return (sum % 11 == 0) ? true : false; 378 } 379 380 /** 381 * 将字符数组转换成数字数组 382 * 383 * @param ca 384 * 字符数组 385 * @return 数字数组 386 */ 387 public static int[] converCharToInt(char[] ca) { 388 int len = ca.length; 389 int[] iArr = new int[len]; 390 try { 391 for (int i = 0; i < len; i++) { 392 iArr[i] = Integer.parseInt(String.valueOf(ca[i])); 393 } 394 } catch (NumberFormatException e) { 395 e.printStackTrace(); 396 } 397 return iArr; 398 } 399 400 /** 401 * 将身份证的每位和对应位的加权因子相乘之后,再得到和值 402 * 403 * @param iArr 404 * @return 身份证编码。 405 */ 406 public static int getPowerSum(int[] iArr) { 407 int iSum = 0; 408 if (power.length == iArr.length) { 409 for (int i = 0; i < iArr.length; i++) { 410 for (int j = 0; j < power.length; j++) { 411 if (i == j) { 412 iSum = iSum + iArr[i] * power[j]; 413 } 414 } 415 } 416 } 417 return iSum; 418 } 419 420 /** 421 * 将power和值与11取模获得余数进行校验码判断 422 * 423 * @param iSum 424 * @return 校验位 425 */ 426 public static String getCheckCode18(int iSum) { 427 String sCode = ""; 428 switch (iSum % 11) { 429 case 10: 430 sCode = "2"; 431 break; 432 case 9: 433 sCode = "3"; 434 break; 435 case 8: 436 sCode = "4"; 437 break; 438 case 7: 439 sCode = "5"; 440 break; 441 case 6: 442 sCode = "6"; 443 break; 444 case 5: 445 sCode = "7"; 446 break; 447 case 4: 448 sCode = "8"; 449 break; 450 case 3: 451 sCode = "9"; 452 break; 453 case 2: 454 sCode = "x"; 455 break; 456 case 1: 457 sCode = "0"; 458 break; 459 case 0: 460 sCode = "1"; 461 break; 462 } 463 return sCode; 464 } 465 466 /** 467 * 根据身份编号获取年龄 468 * 469 * @param idCard 470 * 身份编号 471 * @return 年龄 472 */ 473 public static int getAgeByIdCard(String idCard) { 474 int iAge = 0; 475 if (idCard.length() == CHINA_ID_MIN_LENGTH) { 476 idCard = conver15CardTo18(idCard); 477 } 478 String year = idCard.substring(6, 10); 479 Calendar cal = Calendar.getInstance(); 480 int iCurrYear = cal.get(Calendar.YEAR); 481 iAge = iCurrYear - Integer.valueOf(year); 482 return iAge; 483 } 484 485 /** 486 * 根据身份编号获取生日 487 * 488 * @param idCard 身份编号 489 * @return 生日(yyyyMMdd) 490 */ 491 public static String getBirthByIdCard(String idCard) { 492 Integer len = idCard.length(); 493 if (len < CHINA_ID_MIN_LENGTH) { 494 return null; 495 } else if (len == CHINA_ID_MIN_LENGTH) { 496 idCard = conver15CardTo18(idCard); 497 } 498 return idCard.substring(6, 14); 499 } 500 501 /** 502 * 根据身份编号获取生日年 503 * 504 * @param idCard 身份编号 505 * @return 生日(yyyy) 506 */ 507 public static Short getYearByIdCard(String idCard) { 508 Integer len = idCard.length(); 509 if (len < CHINA_ID_MIN_LENGTH) { 510 return null; 511 } else if (len == CHINA_ID_MIN_LENGTH) { 512 idCard = conver15CardTo18(idCard); 513 } 514 return Short.valueOf(idCard.substring(6, 10)); 515 } 516 517 /** 518 * 根据身份编号获取生日月 519 * 520 * @param idCard 521 * 身份编号 522 * @return 生日(MM) 523 */ 524 public static Short getMonthByIdCard(String idCard) { 525 Integer len = idCard.length(); 526 if (len < CHINA_ID_MIN_LENGTH) { 527 return null; 528 } else if (len == CHINA_ID_MIN_LENGTH) { 529 idCard = conver15CardTo18(idCard); 530 } 531 return Short.valueOf(idCard.substring(10, 12)); 532 } 533 534 /** 535 * 根据身份编号获取生日天 536 * 537 * @param idCard 538 * 身份编号 539 * @return 生日(dd) 540 */ 541 public static Short getDateByIdCard(String idCard) { 542 Integer len = idCard.length(); 543 if (len < CHINA_ID_MIN_LENGTH) { 544 return null; 545 } else if (len == CHINA_ID_MIN_LENGTH) { 546 idCard = conver15CardTo18(idCard); 547 } 548 return Short.valueOf(idCard.substring(12, 14)); 549 } 550 551 /** 552 * 根据身份编号获取性别 553 * 554 * @param idCard 身份编号 555 * @return 性别(M-男,F-女,N-未知) 556 */ 557 public static String getGenderByIdCard(String idCard) { 558 String sGender = "N"; 559 if (idCard.length() == CHINA_ID_MIN_LENGTH) { 560 idCard = conver15CardTo18(idCard); 561 } 562 String sCardNum = idCard.substring(16, 17); 563 if (Integer.parseInt(sCardNum) % 2 != 0) { 564 sGender = "M"; 565 } else { 566 sGender = "F"; 567 } 568 return sGender; 569 } 570 571 /** 572 * 根据身份编号获取户籍省份 573 * 574 * @param idCard 身份编码 575 * @return 省级编码。 576 */ 577 public static String getProvinceByIdCard(String idCard) { 578 int len = idCard.length(); 579 String sProvince = null; 580 String sProvinNum = ""; 581 if (len == CHINA_ID_MIN_LENGTH || len == CHINA_ID_MAX_LENGTH) { 582 sProvinNum = idCard.substring(0, 2); 583 } 584 sProvince = cityCodes.get(sProvinNum); 585 return sProvince; 586 } 587 588 /** 589 * 数字验证 590 * 591 * @param val 592 * @return 提取的数字。 593 */ 594 public static boolean isNum(String val) { 595 return val == null || "".equals(val) ? false : val.matches("^[0-9]*$"); 596 } 597 598 /** 599 * 验证小于当前日期 是否有效 600 * 601 * @param iYear 602 * 待验证日期(年) 603 * @param iMonth 604 * 待验证日期(月 1-12) 605 * @param iDate 606 * 待验证日期(日) 607 * @return 是否有效 608 */ 609 public static boolean valiDate(int iYear, int iMonth, int iDate) { 610 Calendar cal = Calendar.getInstance(); 611 int year = cal.get(Calendar.YEAR); 612 int datePerMonth; 613 if (iYear < MIN || iYear >= year) { 614 return false; 615 } 616 if (iMonth < 1 || iMonth > 12) { 617 return false; 618 } 619 switch (iMonth) { 620 case 4: 621 case 6: 622 case 9: 623 case 11: 624 datePerMonth = 30; 625 break; 626 case 2: 627 boolean dm = ((iYear % 4 == 0 && iYear % 100 != 0) || (iYear % 400 == 0)) 628 && (iYear > MIN && iYear < year); 629 datePerMonth = dm ? 29 : 28; 630 break; 631 default: 632 datePerMonth = 31; 633 } 634 return (iDate >= 1) && (iDate <= datePerMonth); 635 } 636}