Skip to main content

id_validator/
check.rs

1use crate::region::REGION_CODES;
2use chrono::NaiveDate;
3
4// 加权因子
5static WEIGHT: [u32; 18] = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2, 1];
6// 校验码数组
7static CHECK_CODES: &str = "10X98765432";
8
9pub fn is_valid(id: &str) -> bool {
10    if id.len() != 18 {
11        return false;
12    }
13    is_valid_18(id)
14}
15
16fn is_valid_18(id: &str) -> bool {
17    if !is_valid_date(&id[6..14]) {
18        return false;
19    }
20
21    // 取第18位校验码
22    let check_code = &id[17..].chars().next().unwrap();
23    // 计算实际校验码
24    let cal_code = cal_check_code(id);
25    cal_code.eq(check_code)
26}
27
28// 计算校验码
29pub(crate) fn cal_check_code(id: &str) -> char {
30    let mut sum = 0;
31    for (i, c) in id[..17].chars().enumerate() {
32        if let Some(digit) = c.to_digit(10) {
33            sum += digit * WEIGHT[i];
34        }
35    }
36    CHECK_CODES.chars().nth((sum % 11) as usize).unwrap()
37}
38
39// 检查日期
40fn is_valid_date(date_str: &str) -> bool {
41    if date_str.len() != 8 {
42        return false;
43    }
44    NaiveDate::parse_from_str(date_str, "%Y%m%d").is_ok()
45}
46
47#[derive(Debug)]
48pub struct IdInfo {
49    pub id: String,
50    pub code: String,
51    pub region: String,
52    pub date: Option<NaiveDate>,
53}
54
55// 获取身份证信息
56pub fn get_info(id: &str) -> Option<IdInfo> {
57    is_valid(id).then_some(IdInfo {
58        id: id.to_string(),
59        code: id[..6].to_string(),
60        region: REGION_CODES.get(&id[..6]).unwrap_or(&"未知").to_string(),
61        date: NaiveDate::parse_from_str(&id[6..14], "%Y%m%d").ok(),
62    })
63}