| HetuTarkistus.java |
1 package kanta;
2
3 import static kanta.SisaltaaTarkistaja.*;
4
5 /**
6 * Luokka henkilötunnuksen tarkistamiseksi
7 * @author vesal
8 * @version 31.3.2008
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 * @example
123 * <pre name="test">
124 * HetuTarkistus hetut = new HetuTarkistus();
125 * hetut.tarkista(arvoHetu()) === null;
126 * hetut.tarkista(arvoHetu()) === null;
127 * hetut.tarkista(arvoHetu()) === null;
128 * hetut.tarkista(arvoHetu()) === null;
129 * hetut.tarkista(arvoHetu()) === null;
130 * </pre>
131 */
132 public static String arvoHetu() {
133 String apuhetu = String.format("%02d",rand(1,28)) +
134 String.format("%02d",rand(1,12)) +
135 String.format("%02d",rand(1,99)) + "-" +
136 String.format("%03d",rand(1,1000));
137 return apuhetu + hetunTarkistusMerkki(apuhetu);
138 }
139
140 }
141