| 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("1212121") === "Väärä erotinmerkki";
44 * hetut.tarkista("12121212") === "Väärä erotinmerkki";
45 * hetut.tarkista("121212-") === "Yksilöosa liian lyhyt";
46 * hetut.tarkista("121212-12345")=== "Hetu liian pitkä";
47 * hetut.tarkista("121212-222S") === "Tarkistusmerkin kuuluisi olla N";
48 * hetut.tarkista("121212-222N") === null;
49 * hetut.tarkista("121212-231Y") === null;
50 * hetut.tarkista("311212-2317") === null;
51 * </pre>
52 */
53 @Override
54 public String tarkista(String hetu) {
55 int pituus = hetu.length();
56 if ( pituus < 6 ) return "Hetu liian lyhyt";
57 String pvm = hetu.substring(0,6);
58 if ( !onkoVain(pvm,NUMEROT)) return "Alkuosassa saa olla vain numeroita";
59 int pv = Integer.parseInt(pvm.substring(0,2));
60 int kk = Integer.parseInt(pvm.substring(2,4));
61 // int vv = Integer.parseInt(pvm.substring(4,6)); TODO vielä tarkempi pvm tarkistus
62 if ( kk < 1 ) return "Liian pieni kuukausi";
63 if ( 12 < kk ) return "Liian suuri kuukausi";
64 int pvmkk = KUUKAUDET[kk-1];
65 if ( pv < 1 ) return "Liian pieni päivämäärä";
66 if ( pvmkk < pv ) return "Liian suuri päivämäärä";
67 if ( pituus == 6 ) return null; // pelkkä syntymäaika kelpaa
68 String erotin = hetu.substring(6,7);
69 if ( !onkoVain(erotin,"+-A")) return "Väärä erotinmerkki";
70 if ( pituus < 11 ) return "Yksilöosa liian lyhyt";
71 if ( pituus > 11 ) return "Hetu liian pitkä";
72 String yksilo = hetu.substring(7,10);
73 if ( !onkoVain(yksilo,NUMEROT)) return "Yksilöosassa kirjaimia";
74 char merkki = hetunTarkistusMerkki(hetu);
75 if ( hetu.charAt(10) != merkki ) return "Tarkistusmerkin kuuluisi olla " + merkki;
76 return null;
77 }
78
79
80 /**
81 * Palauttaa mikä olisi hetun tarkistumerkki. Tuotava parametrinä
82 * laillista muotoa oleva hetu, josta mahdollisesti tarkistumerkki
83 * puuttuu.
84 * @param hetu tutkittava hetu
85 * @return hetun tarkistusmerkki
86 * @example
87 * <pre name="test">
88 * hetunTarkistusMerkki("121212-222") === 'N';
89 * hetunTarkistusMerkki("121212-222S") === 'N';
90 * hetunTarkistusMerkki("121212-222N") === 'N';
91 * hetunTarkistusMerkki("121212-231Y") === 'Y';
92 * hetunTarkistusMerkki("311212-2317") === '7';
93 * hetunTarkistusMerkki("311212-2317XY") === '7'; // vaikka on liikaa merkkejä
94 * hetunTarkistusMerkki("999999-9999XY") === 'F'; // vaikka on pvm väärin
95 * hetunTarkistusMerkki("12121A-222S") === 'N'; #THROWS NumberFormatException
96 * hetunTarkistusMerkki("12121A-22") === 'N'; #THROWS StringIndexOutOfBoundsException
97 * hetunTarkistusMerkki("121") === 'N'; #THROWS StringIndexOutOfBoundsException
98 * </pre>
99 */
100 public static char hetunTarkistusMerkki(String hetu) {
101 String pvm = hetu.substring(0,6);
102 String yksilo = hetu.substring(7,10);
103 long luku = Long.parseLong(pvm+yksilo);
104 int jakojaannos = (int)(luku % 31L);
105 return TARKISTUSMERKIT.charAt(jakojaannos);
106 }
107
108
109 /**
110 * Arvotaan satunnainen kokonaisluku välille [ala,yla]
111 * @param ala arvonnan alaraja
112 * @param yla arvonnan yläraja
113 * @return satunnainen luku väliltä [ala,yla]
114 */
115 public static int rand(int ala, int yla) {
116 double n = (yla-ala)*Math.random() + ala;
117 return (int)Math.round(n);
118 }
119
120
121 /**
122 * Arvotaan satunnainen henkilötunnus, joka täyttää hetun ehdot
123 * @return satunnainen laillinen henkilötunnus
124 */
125 public static String arvoHetu() {
126 String apuhetu = String.format("%02d",rand(1,28)) +
127 String.format("%02d",rand(1,12)) +
128 String.format("%02d",rand(1,99)) + "-" +
129 String.format("%03d",rand(1,1000));
130 return apuhetu + hetunTarkistusMerkki(apuhetu);
131 }
132
133 }
134