File Coverage

blib/lib/Business/CN/IdentityCard.pm
Criterion Covered Total %
statement 56 79 70.8
branch 13 32 40.6
condition 2 6 33.3
subroutine 10 12 83.3
pod 4 7 57.1
total 85 136 62.5


line stmt bran cond sub pod time code
1             package Business::CN::IdentityCard;
2            
3 1     1   47301 use strict;
  1         3  
  1         73  
4 1     1   7 use vars qw($VERSION);
  1         3  
  1         84  
5             $VERSION = '0.05';
6 1     1   7 use base 'Class::Accessor::Fast';
  1         7  
  1         1081  
7 1     1   23038 use Date::Simple; # for validate_birthday
  1         13607  
  1         12052  
8            
9             __PACKAGE__->mk_accessors(qw/err errstr province birthday/);
10            
11             sub new {
12 1     1 1 12 my ($proto, $id) = @_; # $id = the IdentityCard string
13 1   33     9 my $class = ref($proto) || $proto;
14 1         3 my $self = bless { }, $class;
15            
16 1 50       6 $self->_parse($id) if ($id);
17            
18 1         4 return $self;
19             }
20            
21             sub _parse {
22 2     2   6 my $self = shift;
23 2         5 my $id = lc shift;
24            
25 2 50       12 unless ($id =~ /^(\d{17}(\d|x)|\d{15})$/) {
26 0         0 $self->err('LENGTH');
27 0         0 $self->errstr("Must be 15 or 18 length number!");
28 0         0 return 0;
29             }
30            
31 2         13 $self->{id} = $id;
32            
33             # parse
34 2 50       23 ( $self->{benti},
35             $self->{province_code}, $self->{district_code},
36             $self->{birthday}, $self->{serial_number},
37             $self->{postfix} )
38            
39             = ( length($id) == 18 )
40             ? ( $id =~ /((\d{2})(\d{4})(\d{8})(\d{3}))(\w)/ )
41             : ( $id =~ /((\d{2})(\d{4})(\d{6})(\d{3}))/ );
42 2         6 return 1;
43             }
44            
45             sub validate {
46 2     2 1 9 my ($self, $id) = @_;
47            
48             # we support new($id)+validate and new()+validate($id)
49 2 50       8 unless($id) { $id = $self->{id}; }
  0         0  
50 2         16 $self->_parse($id);
51            
52 2 50 33     6 $self->validate_province() and
53             $self->validate_birthday() and
54             $self->validate_postfix();
55            
56 2 100       7 return 0 if ($self->err);
57 1         18 return 1;
58             }
59            
60             sub validate_province {
61 2     2 0 15 my $self = shift;
62            
63 2         41 my @province = ('','','','','','','','','','','','北京','天津','河北','山西','内蒙古','','','','','','辽宁','吉林','黑龙江','','','','','','','','上海','江苏','浙江','安微','福建','江西','山东','','','','河南','湖北','湖南','广东','广西','海南','','','','重庆','四川','贵州','云南','西藏','','','','','','','陕西','甘肃','青海','宁夏','新疆','','','','','','台湾','','','','','','','','','','香港','澳门','','','','','','','','','国外');
64 2         5 my $province = substr($self->{id}, 0, 2);
65 2 50       7 if (! $province[$self->{province_code}]) {
66 0         0 $self->err('PROVINCE');
67 0         0 $self->errstr('Province is faked');
68 0         0 return 0;
69             } else {
70 2         9 $self->province($province[$self->{province_code}]);
71 2         27 return 1;
72             }
73             }
74            
75             sub validate_birthday {
76 2     2 0 4 my $self = shift;
77            
78 2         6 my ($year,$month,$day) = ( $self->birthday =~ /(\d{2,4})(\d{2})(\d{2})$/ );
79 2 50       20 $year = ( length $year == 4 ) ? $year : '19'.$year;
80 2         5 my $birthday = "$year-$month-$day";
81            
82 2         12 my $date = Date::Simple->new($birthday);
83            
84 2 50       219 if ($date) {
85 2         14 $self->birthday($birthday);
86 2         18 return 1;
87             } else {
88 0         0 $self->err('BIRTHDAY');
89 0         0 $self->errstr(sprintf("birthday: %s is invalid!", $self->birthday));
90 0         0 return 0;
91             }
92             }
93            
94             sub validate_postfix {
95 2     2 0 21 my $self = shift;
96 2 50       5 return 1 if (length($self->{id}) == 15);
97            
98 2         7 my @gene = (7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2);
99 2         5 my @v_code = ('1','0','x','9','8','7','6','5','4','3','2');
100 2         14 my @id = split(//, $self->{benti});
101            
102 2         4 my $sum;
103 2         3 map { $sum += $id[$_] * $gene[$_] } (0..16);
  34         51  
104            
105 2 100       9 if ($self->{postfix} ne $v_code[ $sum % 11 ]) {
106 1         4 $self->err('POSTFIX');
107 1         8 $self->errstr('postfix is invalid!');
108 1         54 return 0;
109             }
110 1         4 return 1;
111             }
112            
113             sub gender {
114 0     0 1   my ($self, $format) = @_;
115 0 0         $format = 'CN' unless ($format);
116 0 0         if ($self->{serial_number} % 2 == 0 ) {
117 0 0         return ($format eq 'CN') ? '女' : 'Female';
118             } else {
119 0 0         return ($format eq 'CN') ? '男' : 'Male';
120             }
121             }
122            
123             sub district {
124 0     0 1   my $self = shift;
125 0           eval('require Business::CN::IdentityCard::District;');
126 0           my $key = $self->{province_code} . $self->{district_code};
127 0 0         if (exists $Business::CN::IdentityCard::District::district{$key}) {
128 0           return $Business::CN::IdentityCard::District::district{$key};
129             } else {
130 0           $self->err('DISTRICT');
131 0           $self->errstr(sprintf("district code: %s is invalid or unkown district!", $key ));
132 0           return undef;
133             }
134             }
135            
136             1;
137             __END__