File Coverage

blib/lib/Person/ID/CZ/RC.pm
Criterion Covered Total %
statement 102 108 94.4
branch 21 24 87.5
condition 5 6 83.3
subroutine 19 19 100.0
pod 11 11 100.0
total 158 168 94.0


line stmt bran cond sub pod time code
1             package Person::ID::CZ::RC;
2              
3 13     13   232614 use strict;
  13         56  
  13         616  
4 13     13   139 use warnings;
  13         28  
  13         985  
5              
6 13     13   7772 use Class::Utils qw(set_params);
  13         191938  
  13         316  
7 13     13   14573 use DateTime;
  13         8387559  
  13         896  
8 13     13   151 use English qw(-no_match_vars);
  13         37  
  13         108  
9 13     13   6162 use Error::Pure qw(err);
  13         60  
  13         18373  
10              
11             our $VERSION = 0.05;
12              
13             # Constructor.
14             sub new {
15 38     38 1 4110299 my ($class, @params) = @_;
16              
17             # Create object.
18 38         140 my $self = bless {}, $class;
19              
20             # RC number.
21 38         208 $self->{'rc'} = undef;
22              
23             # Process parameters.
24 38         201 set_params($self, @params);
25              
26             # Check RC.
27 36 100       566 if (! defined $self->{'rc'}) {
28 1         7 err "Parameter 'rc' is required.";
29             }
30              
31             # Parse.
32 35         191 $self->_parse;
33              
34             # Object.
35 35         248 return $self;
36             }
37              
38             # Get alternate flag.
39             sub alternate {
40 3     3 1 16 my $self = shift;
41 3         10 return $self->{'alternate'};
42             }
43              
44             # Get checksum.
45             sub checksum {
46 3     3 1 14 my $self = shift;
47 3         12 return $self->{'checksum'};
48             }
49              
50             # Get day.
51             sub day {
52 3     3 1 20 my $self = shift;
53 3         8 return $self->{'day'};
54             }
55              
56             # Get error.
57             sub error {
58 5     5 1 22 my $self = shift;
59 5         14 return $self->{'error'};
60             }
61              
62             # Check validity.
63             sub is_valid {
64 3     3 1 12 my $self = shift;
65 3         6 return $self->{'validity'};
66             }
67              
68             # Get month.
69             sub month {
70 3     3 1 15 my $self = shift;
71 3         8 return $self->{'month'};
72             }
73              
74             # Get rc.
75             sub rc {
76 3     3 1 18 my $self = shift;
77 3         9 return $self->{'rc'};
78             }
79              
80             # Get serial.
81             sub serial {
82 3     3 1 15 my $self = shift;
83 3         11 return $self->{'serial'};
84             }
85              
86             # Get sex.
87             sub sex {
88 5     5 1 24 my $self = shift;
89 5         16 return $self->{'sex'};
90             }
91              
92             # Get year.
93             sub year {
94 3     3 1 10 my $self = shift;
95 3         6 return $self->{'year'};
96             }
97              
98             # Check validity.
99             sub _check_validity {
100 23     23   41 my $self = shift;
101 23         52 my $number = $self->{'rc'};
102 23         89 $number =~ s/\///ms;
103 23         67 $number = substr $number, 0, 9;
104 23         66 my $checksum = $number % 11;
105 23 100       90 if ($checksum == 10) {
106 10         66 $checksum = 0;
107             }
108 23 100       130 if ($self->{'checksum'} == $checksum) {
109 22         91 $self->{'validity'} = 1;
110             } else {
111 1         4 $self->{'error'} = "Checksum isn't valid.";
112 1         4 $self->{'validity'} = 0;
113             }
114 23         73 return;
115             }
116              
117             # Parse.
118             sub _parse {
119 35     35   76 my $self = shift;
120 35 100 100     447 if ($self->{'rc'} =~ m/^(\d\d)(\d\d)(\d\d)\/(\d\d\d)(\d)$/ms
    100 66        
121             || $self->{'rc'} =~ m/^(\d\d)(\d\d)(\d\d)(\d\d\d)(\d)$/ms) {
122              
123 23         119 $self->{'year'} = int(1900 + $1);
124 23 50       150 if ($2 > 70) {
    100          
    100          
125 0         0 $self->{'alternate'} = 1;
126 0         0 $self->{'month'} = int($2 - 70);
127 0         0 $self->{'sex'} = 'female';
128             } elsif ($2 > 50) {
129 2         7 $self->{'month'} = int($2 - 50);
130 2         5 $self->{'sex'} = 'female';
131 2         5 $self->{'alternate'} = 0;
132             } elsif ($2 > 20) {
133 9         38 $self->{'month'} = int($2 - 20);
134 9         26 $self->{'alternate'} = 1;
135 9         25 $self->{'sex'} = 'male';
136             } else {
137 12         34 $self->{'alternate'} = 0;
138 12         62 $self->{'month'} = int($2);
139 12         36 $self->{'sex'} = 'male';
140             }
141 23         116 $self->{'day'} = int($3);
142 23         123 $self->{'serial'} = $4;
143 23         81 $self->{'checksum'} = $5;
144              
145             # Check validity.
146 23         91 $self->_check_validity;
147              
148             # To 31. 12.1953.
149             } elsif ($self->{'rc'} =~ m/^(\d\d)(\d\d)(\d\d)\/(\d\d\d)$/ms
150             || $self->{'rc'} =~ m/^(\d\d)(\d\d)(\d\d)(\d\d\d)$/ms) {
151              
152 1         4 $self->{'year'} = int(1900 + $1);
153 1 50       5 if ($2 > 50) {
154 0         0 $self->{'month'} = int($2 - 50);
155 0         0 $self->{'sex'} = 'female';
156             } else {
157 1         2 $self->{'month'} = int($2);
158 1         2 $self->{'sex'} = 'male';
159             }
160 1         2 $self->{'day'} = int($3);
161 1         2 $self->{'serial'} = $4;
162 1         4 $self->{'checksum'} = '-';
163 1 50       3 if ($self->{'year'} <= 1953) {
164 0         0 $self->{'validity'} = 1;
165             } else {
166 1         2 $self->{'error'} = "Format of rc identification ".
167             "hasn't checksum.";
168 1         13 $self->{'validity'} = 0;
169             }
170 1         3 $self->{'alternate'} = 0;
171              
172             # Not valid.
173             } else {
174 11         37 $self->{'alternate'} = '-';
175 11         34 $self->{'checksum'} = '-';
176 11         33 $self->{'year'} = '-';
177 11         34 $self->{'month'} = '-';
178 11         33 $self->{'day'} = '-';
179 11         31 $self->{'serial'} = '-';
180 11         34 $self->{'sex'} = '-';
181 11         32 $self->{'error'} = "Format of rc identification isn't valid.";
182 11         27 $self->{'validity'} = 0;
183             }
184              
185             # Check date.
186 35 100       144 if ($self->{'validity'}) {
187 22         41 eval {
188             DateTime->new(
189             'year' => $self->{'year'},
190             'month' => $self->{'month'},
191 22         164 'day' => $self->{'day'},
192             );
193             };
194 22 100       10338 if ($EVAL_ERROR) {
195 1         2 $self->{'error'} = "Date isn't valid.";
196 1         2 $self->{'validity'} = 0;
197             }
198             }
199              
200 35         81 return;
201             }
202              
203             1;
204              
205             __END__
206              
207             =pod
208              
209             =encoding utf8
210              
211             =head1 NAME
212              
213             Person::ID::CZ::RC - Perl class for Czech RC identification.
214              
215             =head1 SYNOPSIS
216              
217             use Person::ID::CZ::RC;
218              
219             my $obj = Person::ID::CZ::RC->new(%params);
220             my $alternate = $obj->alternate;
221             my $checksum = $obj->checksum;
222             my $day = $obj->day;
223             my $error = $obj->error;
224             my $is_valid = $obj->is_valid;
225             my $month = $obj->month;
226             my $rc = $obj->rc;
227             my $serial = $obj->serial;
228             my $sex = $obj->sex;
229             my $year = $obj->year;
230              
231             =head1 METHODS
232              
233             =head2 C<new>
234              
235             my $obj = Person::ID::CZ::RC->new(%params);
236              
237             Constructor.
238              
239             =over 8
240              
241             =item * C<rc>
242              
243             Input Czech RC identification.
244              
245             It is required.
246              
247             =back
248              
249             Returns instance of object.
250              
251             =head2 C<alternate>
252              
253             my $alternate = $obj->alternate;
254              
255             Get flag, that means alternate RC identification.
256              
257             Returns 0/1.
258              
259             =head2 C<checksum>
260              
261             my $checksum = $obj->checksum;
262              
263             Get checksum.
264              
265             Returns string with one number character or '-'.
266              
267             =head2 C<day>
268              
269             my $day = $obj->day;
270              
271             Get day of birth.
272              
273             Returns string with day.
274              
275             =head2 C<error>
276              
277             my $error = $obj->error;
278              
279             Get error.
280              
281             Returns error string or undef.
282              
283             =head2 C<is_valid>
284              
285             my $is_valid = $obj->is_valid;
286              
287             Get flag, that means validity of rc identification.
288              
289             Returns 0/1.
290              
291             =head2 C<month>
292              
293             my $month = $obj->month;
294              
295             Get month of birth.
296              
297             Returns string with month.
298              
299             =head2 C<rc>
300              
301             my $rc = $obj->rc;
302              
303             Get rc identification.
304              
305             Returns string with rc identification.
306              
307             =head2 C<serial>
308              
309             my $serial = $obj->serial;
310              
311             Get serial part of rc identification.
312              
313             Returns string with three numbers.
314              
315             =head2 C<sex>
316              
317             my $sex = $obj->sex;
318              
319             Get flag, that means sex of person.
320              
321             Returns male/female string.
322              
323             =head2 C<year>
324              
325             my $year = $obj->year;
326              
327             Get year of birth.
328              
329             Returns string with year.
330              
331             =head1 ERRORS
332              
333             new():
334             Parameter 'rc' is required.
335             From Class::Utils::set_params():
336             Unknown parameter '%s'.
337              
338             =head1 EXAMPLE1
339              
340             =for comment filename=parse_and_print_rc_ok.pl
341              
342             use strict;
343             use warnings;
344              
345             use Person::ID::CZ::RC;
346              
347             # Object.
348             my $obj = Person::ID::CZ::RC->new(
349             'rc' => '840501/1330',
350             );
351              
352             # Get error.
353             my $error = $obj->error || '-';
354              
355             # Print out.
356             print "Personal number: ".$obj->rc."\n";
357             print "Year: ".$obj->year."\n";
358             print "Month: ".$obj->month."\n";
359             print "Day: ".$obj->day."\n";
360             print "Sex: ".$obj->sex."\n";
361             print "Serial: ".$obj->serial."\n";
362             print "Checksum: ".$obj->checksum."\n";
363             print "Alternate: ".$obj->alternate."\n";
364             print "Valid: ".$obj->is_valid."\n";
365             print "Error: ".$error."\n";
366              
367             # Output:
368             # Personal number: 840501/1330
369             # Year: 1984
370             # Month: 05
371             # Day: 01
372             # Sex: male
373             # Serial: 133
374             # Checksum: 0
375             # Alternate: 0
376             # Valid: 1
377             # Error: -
378              
379             =head1 EXAMPLE2
380              
381             =for comment filename=parse_and_print_rc_fail_date.pl
382              
383             use strict;
384             use warnings;
385              
386             use Person::ID::CZ::RC;
387              
388             # Object.
389             my $obj = Person::ID::CZ::RC->new(
390             'rc' => '840230/1337',
391             );
392              
393             # Get error.
394             my $error = $obj->error || '-';
395              
396             # Print out.
397             print "Personal number: ".$obj->rc."\n";
398             print "Year: ".$obj->year."\n";
399             print "Month: ".$obj->month."\n";
400             print "Day: ".$obj->day."\n";
401             print "Sex: ".$obj->sex."\n";
402             print "Serial: ".$obj->serial."\n";
403             print "Checksum: ".$obj->checksum."\n";
404             print "Alternate: ".$obj->alternate."\n";
405             print "Valid: ".$obj->is_valid."\n";
406             print "Error: ".$error."\n";
407              
408             # Output:
409             # Personal number: 840230/1337
410             # Year: 1984
411             # Month: 02
412             # Day: 30
413             # Sex: male
414             # Serial: 133
415             # Checksum: 7
416             # Alternate: 0
417             # Valid: 0
418             # Error: Date isn't valid.
419              
420             =head1 EXAMPLE3
421              
422             =for comment filename=parse_and_print_rc_fail_bad_checksum.pl
423              
424             use strict;
425             use warnings;
426              
427             use Person::ID::CZ::RC;
428              
429             # Object.
430             my $obj = Person::ID::CZ::RC->new(
431             'rc' => '840229/1330',
432             );
433              
434             # Get error.
435             my $error = $obj->error || '-';
436              
437             # Print out.
438             print "Personal number: ".$obj->rc."\n";
439             print "Year: ".$obj->year."\n";
440             print "Month: ".$obj->month."\n";
441             print "Day: ".$obj->day."\n";
442             print "Sex: ".$obj->sex."\n";
443             print "Serial: ".$obj->serial."\n";
444             print "Checksum: ".$obj->checksum."\n";
445             print "Alternate: ".$obj->alternate."\n";
446             print "Valid: ".$obj->is_valid."\n";
447             print "Error: ".$error."\n";
448              
449             # Output:
450             # Personal number: 840229/1330
451             # Year: 1984
452             # Month: 02
453             # Day: 29
454             # Sex: male
455             # Serial: 133
456             # Checksum: 0
457             # Alternate: 0
458             # Valid: 0
459             # Error: Checksum isn't valid.
460              
461             =head1 EXAMPLE4
462              
463             =for comment filename=parse_and_print_rc_fail_no_checksum.pl
464              
465             use strict;
466             use warnings;
467              
468             use Person::ID::CZ::RC;
469              
470             # Object.
471             my $obj = Person::ID::CZ::RC->new(
472             'rc' => '840229/133',
473             );
474              
475             # Get error.
476             my $error = $obj->error || '-';
477              
478             # Print out.
479             print "Personal number: ".$obj->rc."\n";
480             print "Year: ".$obj->year."\n";
481             print "Month: ".$obj->month."\n";
482             print "Day: ".$obj->day."\n";
483             print "Sex: ".$obj->sex."\n";
484             print "Serial: ".$obj->serial."\n";
485             print "Checksum: ".$obj->checksum."\n";
486             print "Alternate: ".$obj->alternate."\n";
487             print "Valid: ".$obj->is_valid."\n";
488             print "Error: ".$error."\n";
489              
490             # Output:
491             # Personal number: 840229/133
492             # Year: 1984
493             # Month: 02
494             # Day: 29
495             # Sex: male
496             # Serial: 133
497             # Checksum: -
498             # Alternate: 0
499             # Valid: 0
500             # Error: Format of rc identification hasn't checksum.
501              
502             =head1 EXAMPLE5
503              
504             =for comment filename=parse_and_print_rc_fail_format.pl
505              
506             use strict;
507             use warnings;
508              
509             use Person::ID::CZ::RC;
510              
511             # Object.
512             my $obj = Person::ID::CZ::RC->new(
513             'rc' => '840229|1330',
514             );
515              
516             # Get error.
517             my $error = $obj->error || '-';
518              
519             # Print out.
520             print "Personal number: ".$obj->rc."\n";
521             print "Year: ".$obj->year."\n";
522             print "Month: ".$obj->month."\n";
523             print "Day: ".$obj->day."\n";
524             print "Sex: ".$obj->sex."\n";
525             print "Serial: ".$obj->serial."\n";
526             print "Checksum: ".$obj->checksum."\n";
527             print "Alternate: ".$obj->alternate."\n";
528             print "Valid: ".$obj->is_valid."\n";
529             print "Error: ".$error."\n";
530              
531             # Output:
532             # Personal number: 840229|1330
533             # Year: -
534             # Month: -
535             # Day: -
536             # Sex: -
537             # Serial: -
538             # Checksum: -
539             # Alternate: -
540             # Valid: 0
541             # Error: Format of rc identification isn't valid.
542              
543             =head1 DEPENDENCIES
544              
545             L<Class::Utils>,
546             L<DateTime>,
547             L<English>,
548             L<Error::Pure>.
549              
550             =head1 SEE ALSO
551              
552             =over
553              
554             =item L<Business::DK::CPR>
555              
556             Danish CPR (SSN) number generator/validator
557              
558             =item L<No::PersonNr>
559              
560             Check Norwegian Social security numbers
561              
562             =item L<Person::ID::CZ::RC::Generator>
563              
564             Perl class for Czech RC identification generation.
565              
566             =item L<Se::PersonNr>
567              
568             Module for validating and generating a Swedish personnummer.
569              
570             =back
571              
572             =head1 REPOSITORY
573              
574             L<https://github.com/michal-josef-spacek/Person::ID::CZ::RC>
575              
576             =head1 AUTHOR
577              
578             Michal Josef Špaček L<mailto:skim@cpan.org>
579              
580             L<http://skim.cz>
581              
582             =head1 LICENSE AND COPYRIGHT
583              
584             © Michal Josef Špaček 2013-2023
585              
586             BSD 2-Clause License
587              
588             =head1 VERSION
589              
590             0.05
591              
592             =cut