Validate-CodiceFiscale

 view release on metacpan or  search on metacpan

lib/Validate/CodiceFiscale.pm  view on Meta::CPAN

      G => 15,
      H => 17,
      I => 19,
      J => 21,
      K => 2,
      L => 4,
      M => 18,
      N => 20,
      O => 11,
      P => 3,
      Q => 6,
      R => 8,
      S => 12,
      T => 14,
      U => 16,
      V => 10,
      W => 22,
      X => 25,
      Y => 24,
      Z => 23,
     },
     my $even_checksums = {
      0 => 0,
      1 => 1,
      2 => 2,
      3 => 3,
      4 => 4,
      5 => 5,
      6 => 6,
      7 => 7,
      8 => 8,
      9 => 9,
      A => 0,
      B => 1,
      C => 2,
      D => 3,
      E => 4,
      F => 5,
      G => 6,
      H => 7,
      I => 8,
      J => 9,
      K => 10,
      L => 11,
      M => 12,
      N => 13,
      O => 14,
      P => 15,
      Q => 16,
      R => 17,
      S => 18,
      T => 19,
      U => 20,
      V => 21,
      W => 22,
      X => 23,
      Y => 24,
      Z => 25,
     };
   state $checksums_for = [$odd_checksums, $even_checksums];
   my @chars = split m{}mxs, substr($cf, 0, 15);    # no checksum
   my $sum = sum map { $checksums_for->[$_ % 2]{$chars[$_]} } 0 .. $#chars;
   chr(ord('A') + ($sum % 26));
} ## end sub _cf_checksum

sub _normalized_string ($string, @positions) {
   state $letters   = [qw< L M N P Q R S T U V >];
   state $digit_for = {map { $letters->[$_] => $_ } 0 .. $letters->$#*};
   for my $i (@positions) {
      my $current = substr($string, $i, 1);
      substr($string, $i, 1, $digit_for->{$current})
        if exists $digit_for->{$current};
   }
   return $string;
} ## end sub _normalized_string

sub _normalized_birthplace ($place) { _normalized_string($place, 1 .. 3) }
sub _normalized_birthdate ($date) { _normalized_string($date, 0, 1, 3, 4) }

sub _expand_date ($date, $opts) {
   state $mlf       = [split m{}mxs, 'ABCDEHLMPRST'];
   state $month_for = {map { $mlf->[$_] => $_ } 0 .. $mlf->$#*};

   $date = _normalized_birthdate($date);
   my ($y, $mc, $d) = $date =~ m{\A(\d\d)([ABCDEHLMPRST])(\d\d)\z}mxs
     or return;
   my $m = 1 + $month_for->{$mc};
   $_ += 0 for ($d, $y);
   my $sex = $d > 40 ? 'F' : 'M';
   $d -= 40 if $d > 40;

   # century: the initial digits of a year
   if (defined(my $years_baseline = ($opts // {})->{years_baseline})) {
      $y += $years_baseline;
   }
   else {    # whatever in the last 100 years
      my $this_year = 1900 + (localtime)[5];
      $y += 100 * int($this_year / 100);
      $y -= 100 if $y > $this_year;
   }

   return ($y, $m, $d, $sex);
} ## end sub _expand_date

sub _is_valid_cf_date ($y, $m, $d) {
   return !!(eval { timegm(30, 30, 12, $d, $m - 1, $y); 1 });
}

sub _compact_birthdates ($birthdate) {
   state $month_letter_for = ['', split m{}mxs, 'ABCDEHLMPRST'];
   my ($y, $m, $d) = split m{\D}mxs, $birthdate;
   ($y, $d) = ($d, $y) if $d > 31;
   $y %= 100;
   $m = $month_letter_for->[$m + 0];
   map { sprintf '%02d%s%02d', $y, $m, $_ } ($d, $d + 40);
} ## end sub _compact_birthdates

sub _compact_surname ($surname) {
   my ($cs, $vs) = _consonants_and_vowels($surname);
   my @retval = ($cs->@*, $vs->@*, ('X') x 3);
   return join '', @retval[0 .. 2];
}

sub _compact_name ($name) {
   my ($cs, $vs) = _consonants_and_vowels($name);
   splice $cs->@*, 1, 1 if $cs->@* > 3;
   my @retval = ($cs->@*, $vs->@*, ('X') x 3);
   return join '', @retval[0 .. 2];
} ## end sub _compact_name

sub _consonants_and_vowels ($string) {
   my (@consonants, @vowels);
   for my $char (grep { m{[A-Z]}mxs } split m{}mxs, uc($string)) {
      if   ($char =~ m{[AEIOU]}mxs) { push @vowels,     $char }
      else                          { push @consonants, $char }
   }
   return (\@consonants, \@vowels);
} ## end sub _consonants_and_vowels

sub _places {
   state $retval = do {
      local $/;
      binmode DATA, ':raw';
      (my $json = readline(DATA)) =~ s{\n+}{}gmxs;
      decode_json($json);
   };
}

sub _place_name_for ($place, $birthdate) {
   state $place_for = _places();
   my $record = $place_for->{_normalized_birthplace($place)} or return;
   return "[$record->[-1]{name}]" unless defined($birthdate);
   for my $candidate ($record->@*) {
      next if $birthdate gt $candidate->{end};
      last if $birthdate lt $candidate->{start};
      return $candidate->{name};
   }
   return;
} ## end sub _place_name_for

1;

__DATA__
{"G935":[{"start":"1957-04-07","end":"9999-12-31","name":"POSTA FIBRENO"}],"
G846":[{"start":"1866-11-19","end":"1902-02-06","name":"PONTE DI PIAVE"},{"n
ame":"PONTE DI PIAVE","start":"1902-02-07","end":"1907-05-09"},{"name":"PONT
E DI PIAVE","start":"1907-05-10","end":"9999-12-31"}],"M082":[{"name":"VITER
BO","end":"1927-01-11","start":"1871-01-15"},{"name":"VITERBO","start":"1927
-01-12","end":"1928-05-18"},{"name":"VITERBO","start":"1928-05-19","end":"19
46-11-11"},{"end":"9999-12-31","start":"1946-11-12","name":"VITERBO"}],"B508
":[{"end":"1927-01-11","start":"1863-04-23","name":"CAMPIGLIA CERVO"},{"star
t":"1927-01-12","end":"1992-04-15","name":"CAMPIGLIA CERVO"},{"start":"1992-
04-16","end":"2015-12-31","name":"CAMPIGLIA CERVO"}],"A402":[{"start":"1861-
03-17","end":"9999-12-31","name":"ARIELLI"}],"I608":[{"end":"9999-12-31","st
art":"1861-03-17","name":"SENIGALLIA"}],"B255":[{"start":"1863-07-13","end":
"9999-12-31","name":"BUGLIO IN MONTE"}],"E780":[{"name":"MACCHIA VALFORTORE"
,"end":"1928-06-30","start":"1861-03-17"},{"name":"MACCHIA VALFORTORE","star
t":"1946-04-09","end":"1970-03-02"},{"name":"MACCHIA VALFORTORE","start":"19
70-03-03","end":"9999-12-31"}],"G831":[{"name":"PONTEBBA","start":"1866-11-1
9","end":"1924-08-29"},{"end":"1928-05-18","start":"1924-08-30","name":"PONT
EBBA"},{"end":"1968-04-05","start":"1928-05-19","name":"PONTEBBA"},{"name":"
PONTEBBA","start":"1968-04-06","end":"9999-12-31"}],"F153":[{"start":"1861-0
3-17","end":"9999-12-31","name":"MESE"}],"L292":[{"start":"1863-03-30","end"
:"1928-10-15","name":"TORRICELLA VERZATE"},{"name":"TORRICELLA VERZATE","sta
rt":"1946-12-19","end":"9999-12-31"}],"L321":[{"name":"TRAMATZA","start":"18
61-03-17","end":"1928-05-12"},{"end":"1974-08-19","start":"1951-01-12","name
":"TRAMATZA"},{"start":"1974-08-20","end":"9999-12-31","name":"TRAMATZA"}],"
E495":[{"end":"1927-01-11","start":"1861-03-17","name":"LAVENO"},{"start":"1
927-01-12","end":"1928-01-27","name":"LAVENO"}],"F942":[{"end":"9999-12-31",
"start":"1861-03-17","name":"NOTARESCO"}],"C274":[{"start":"1861-03-17","end
":"1928-12-31","name":"CASTELSPINA"},{"end":"9999-12-31","start":"1954-01-24
","name":"CASTELSPINA"}],"L899":[{"name":"VIGONOVO","end":"9999-12-31","star
t":"1866-11-19"}],"A024":[{"name":"ACERRA","start":"1861-03-17","end":"1927-



( run in 0.573 second using v1.01-cache-2.11-cpan-71847e10f99 )