HetuTarkistus.java |
1 package kanta; 2 3 import static kanta.SisaltaaTarkistaja.*; 4 5 /** 6 * Luokka henkilötunnuksen tarkistamiseksi 7 * @author vesal 8 * @version 9.1.2011 9 * 10 */ 11 public class HetuTarkistus implements Tarkistaja { 12 /** Hetuun kelpaavat tarkistusmerkit järjestyksessä */ 13 // 0123456789012345678901234567890 14 public static final String TARKISTUSMERKIT = "0123456789ABCDEFHJKLMNPRSTUVWXY"; 15 16 /** Kuukausien maksimipituudet */ 17 // 1 2 3 4 5 6 7 8 9 10 11 12 18 public static int[] KUUKAUDET = {31,29,31,30,31,30,31,31,30,31,30,31}; 19 20 21 /** 22 * Tarkistetaan hetu. Sallitaan myös muoto jossa vain syntymäaika. 23 * @param hetu joka tutkitaan. 24 * @return null jos oikein, muuten virhettä kuvaava teksti 25 * TODO tarkistukset kuntoon myös karkausvuodesta. 26 * @example 27 * <pre name="test"> 28 * #PACKAGEIMPORT 29 * HetuTarkistus hetut = new HetuTarkistus(); 30 * hetut.tarkista("12121") === "Hetu liian lyhyt"; 31 * hetut.tarkista("k") === "Hetu liian lyhyt"; 32 * hetut.tarkista("12121k") === "Alkuosassa saa olla vain numeroita"; 33 * hetut.tarkista("121212") === null; // sallitaan pelkkä syntymäaika 34 * hetut.tarkista("001212") === "Liian pieni päivämäärä"; 35 * hetut.tarkista("321212") === "Liian suuri päivämäärä"; 36 * hetut.tarkista("300212") === "Liian suuri päivämäärä"; 37 * hetut.tarkista("310412") === "Liian suuri päivämäärä"; 38 * hetut.tarkista("121312") === "Liian suuri kuukausi"; 39 * hetut.tarkista("120012") === "Liian pieni kuukausi"; 40 * hetut.tarkista("121212B222Q") === "Väärä erotinmerkki"; 41 * hetut.tarkista("121212-2k2Q") === "Yksilöosassa kirjaimia"; 42 * hetut.tarkista("121212-2") === "Yksilöosa liian lyhyt"; 43 * hetut.tarkista("121212-") === "Yksilöosa liian lyhyt"; 44 * hetut.tarkista("121212-12345")=== "Hetu liian pitkä"; 45 * hetut.tarkista("121212-222S") === "Tarkistusmerkin kuuluisi olla N"; 46 * hetut.tarkista("121212-222N") === null; 47 * hetut.tarkista("121212-231Y") === null; 48 * hetut.tarkista("311212-2317") === null; 49 * </pre> 50 */ 51 @Override 52 public String tarkista(String hetu) { 53 int pituus = hetu.length(); 54 if ( pituus < 6 ) return "Hetu liian lyhyt"; 55 String pvm = hetu.substring(0,6); 56 if ( !onkoVain(pvm,NUMEROT)) return "Alkuosassa saa olla vain numeroita"; 57 int pv = Integer.parseInt(pvm.substring(0,2)); 58 int kk = Integer.parseInt(pvm.substring(2,4)); 59 // int vv = Integer.parseInt(pvm.substring(4,6)); TODO vielä tarkempi pvm tarkistus 60 if ( kk < 1 ) return "Liian pieni kuukausi"; 61 if ( 12 < kk ) return "Liian suuri kuukausi"; 62 int pvmkk = KUUKAUDET[kk-1]; 63 if ( pv < 1 ) return "Liian pieni päivämäärä"; 64 if ( pvmkk < pv ) return "Liian suuri päivämäärä"; 65 if ( pituus == 6 ) return null; // pelkkä syntymäaika kelpaa 66 if ( pituus < 11 ) return "Yksilöosa liian lyhyt"; 67 if ( pituus > 11 ) return "Hetu liian pitkä"; 68 String erotin = hetu.substring(6,7); 69 if ( !onkoVain(erotin,"+-A")) return "Väärä erotinmerkki"; 70 String yksilo = hetu.substring(7,10); 71 if ( !onkoVain(yksilo,NUMEROT)) return "Yksilöosassa kirjaimia"; 72 char merkki = hetunTarkistusMerkki(hetu); 73 if ( hetu.charAt(10) != merkki ) return "Tarkistusmerkin kuuluisi olla " + merkki; 74 return null; 75 } 76 77 78 /** 79 * Palauttaa mikä olisi hetun tarkistumerkki. Tuotava parametrinä 80 * laillista muotoa oleva hetu, josta mahdollisesti tarkistumerkki 81 * puuttuu. 82 * @param hetu tutkittava hetu 83 * @return hetun tarkistusmerkki 84 * @example 85 * <pre name="test"> 86 * hetunTarkistusMerkki("121212-222") === 'N'; 87 * hetunTarkistusMerkki("121212-222S") === 'N'; 88 * hetunTarkistusMerkki("121212-222N") === 'N'; 89 * hetunTarkistusMerkki("121212-231Y") === 'Y'; 90 * hetunTarkistusMerkki("311212-2317") === '7'; 91 * hetunTarkistusMerkki("311212-2317XY") === '7'; // vaikka on liikaa merkkejä 92 * hetunTarkistusMerkki("999999-9999XY") === 'F'; // vaikka on pvm väärin 93 * hetunTarkistusMerkki("12121A-222S") === 'N'; #THROWS NumberFormatException 94 * hetunTarkistusMerkki("12121A-22") === 'N'; #THROWS StringIndexOutOfBoundsException 95 * hetunTarkistusMerkki("121") === 'N'; #THROWS StringIndexOutOfBoundsException 96 * </pre> 97 */ 98 public static char hetunTarkistusMerkki(String hetu) { 99 String pvm = hetu.substring(0,6); 100 String yksilo = hetu.substring(7,10); 101 long luku = Long.parseLong(pvm+yksilo); 102 int jakojaannos = (int)(luku % 31L); 103 return TARKISTUSMERKIT.charAt(jakojaannos); 104 } 105 106 107 /** 108 * Arvotaan satunnainen kokonaisluku välille [ala,yla] 109 * @param ala arvonnan alaraja 110 * @param yla arvonnan yläraja 111 * @return satunnainen luku väliltä [ala,yla] 112 */ 113 public static int rand(int ala, int yla) { 114 double n = (yla-ala)*Math.random() + ala; 115 return (int)Math.round(n); 116 } 117 118 119 /** 120 * Arvotaan satunnainen henkilötunnus, joka täyttää hetun ehdot 121 * @return satunnainen laillinen henkilötunnus 122 */ 123 public static String arvoHetu() { 124 String apuhetu = String.format("%02d",rand(1,28)) + 125 String.format("%02d",rand(1,12)) + 126 String.format("%02d",rand(1,99)) + "-" + 127 String.format("%03d",rand(1,1000)); 128 return apuhetu + hetunTarkistusMerkki(apuhetu); 129 } 130 131 } 132