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