Skip to content

Commit 71fd588

Browse files
cbxpasolntsev
andauthored
add Hungarian id number generator (#1555)
Co-authored-by: Andrei Solntsev, Oleg Pasichnyk <andrei.solntsev@gmail.com>
1 parent 0b23383 commit 71fd588

5 files changed

Lines changed: 133 additions & 0 deletions

File tree

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package net.datafaker.idnumbers;
2+
3+
import net.datafaker.providers.base.BaseProviders;
4+
import net.datafaker.providers.base.IdNumber;
5+
import net.datafaker.providers.base.PersonIdNumber;
6+
7+
import java.time.LocalDate;
8+
import java.time.format.DateTimeFormatter;
9+
10+
import static java.lang.Character.getNumericValue;
11+
import static net.datafaker.idnumbers.Utils.randomGender;
12+
13+
/**
14+
* Hungarian personal identification number
15+
* <p>
16+
* The structure of such number is GYYMMDDXXC, whereas
17+
* <ul>
18+
* <li>G indicates century of birth and gender,</li>
19+
* <li>YYMMDD indicates birth year, month and day,</li>
20+
* <li>XX is the serial number, and</li>
21+
* <li>C is a checksum digit.</li>
22+
* </ul>
23+
* <p>
24+
* See <a href="https://en.wikipedia.org/wiki/National_identification_number#Hungary">Hungarian identification number</a>
25+
*/
26+
public class HungarianIdNumber implements IdNumberGenerator {
27+
28+
private static final DateTimeFormatter BIRTHDAY_FORMAT = DateTimeFormatter.ofPattern("yyMMdd");
29+
30+
@Override
31+
public String countryCode() {
32+
return "HU";
33+
}
34+
35+
@Override
36+
public String generateInvalid(BaseProviders faker) {
37+
String validIdNumber = generateValid(faker);
38+
String basePart = validIdNumber.substring(0, 9);
39+
int validCheckDigit = getNumericValue(validIdNumber.charAt(9));
40+
int invalidCheckDigit = (validCheckDigit + 1) % 10;
41+
return basePart + invalidCheckDigit;
42+
}
43+
44+
@Override
45+
public PersonIdNumber generateValid(BaseProviders faker, IdNumber.IdNumberRequest request) {
46+
LocalDate birthday = faker.timeAndDate().birthday();
47+
PersonIdNumber.Gender gender = randomGender(faker);
48+
int checkDigit;
49+
String basePart;
50+
do {
51+
basePart = basePart(faker, birthday, gender);
52+
checkDigit = getCheckDigit(basePart);
53+
} while (checkDigit == 10);
54+
55+
String idNumber = basePart + checkDigit;
56+
return new PersonIdNumber(idNumber, birthday, gender);
57+
}
58+
59+
private String basePart(BaseProviders faker, LocalDate birthday, PersonIdNumber.Gender gender) {
60+
return firstDigit(birthday.getYear(), gender) +
61+
BIRTHDAY_FORMAT.format(birthday) +
62+
faker.number().digits(2);
63+
}
64+
65+
static int getCheckDigit(String basePart) {
66+
char[] numbers = basePart.toCharArray();
67+
int summ = 0;
68+
for (int i = 0; i < numbers.length; i++) {
69+
summ += getNumericValue(numbers[i]) * (i + 1);
70+
}
71+
72+
return summ % 11;
73+
}
74+
75+
static int firstDigit(int birthYear, PersonIdNumber.Gender gender) {
76+
return switch (gender) {
77+
case MALE -> isInRange(birthYear) ? 1 : 3;
78+
case FEMALE -> isInRange(birthYear) ? 2 : 4;
79+
};
80+
}
81+
82+
private static boolean isInRange(int birthYear) {
83+
return birthYear >= 1900 && birthYear <= 1999;
84+
}
85+
86+
}

src/main/resources/META-INF/services/net.datafaker.idnumbers.IdNumberGenerator

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ net.datafaker.idnumbers.ChineseIdNumber
55
net.datafaker.idnumbers.EstonianIdNumber
66
net.datafaker.idnumbers.FrenchIdNumber
77
net.datafaker.idnumbers.GeorgianIdNumber
8+
net.datafaker.idnumbers.HungarianIdNumber
89
net.datafaker.idnumbers.ItalianIdNumber
910
net.datafaker.idnumbers.LatvianIdNumber
1011
net.datafaker.idnumbers.MacedonianIdNumber

src/test/java/net/datafaker/helpers/IdNumberPatterns.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,6 @@ public class IdNumberPatterns {
1010
public static final Pattern FRENCH = Pattern.compile("[12]\\d{2}(1[0-2]|0[1-9])\\d[0-9a-zA-Z]\\d{8}");
1111
public static final Pattern ITALIAN = Pattern.compile("[A-Z]{6}\\d{2}[ABCDEHLMPRST]\\d{2}[\\dA-Z]{5}");
1212
public static final Pattern POLISH = Pattern.compile("\\d{11}");
13+
public static final Pattern HUNGARIAN = Pattern.compile("[1-4]\\d{9}");
1314

1415
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package net.datafaker.idnumbers;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import static net.datafaker.providers.base.PersonIdNumber.Gender.FEMALE;
6+
import static net.datafaker.providers.base.PersonIdNumber.Gender.MALE;
7+
import static org.assertj.core.api.Assertions.assertThat;
8+
9+
class HungarianIdNumberTest {
10+
11+
@Test
12+
void firstDigit() {
13+
assertThat(HungarianIdNumber.firstDigit(1899, MALE)).isEqualTo(3);
14+
assertThat(HungarianIdNumber.firstDigit(1900, MALE)).isEqualTo(1);
15+
assertThat(HungarianIdNumber.firstDigit(1901, MALE)).isEqualTo(1);
16+
assertThat(HungarianIdNumber.firstDigit(1899, FEMALE)).isEqualTo(4);
17+
assertThat(HungarianIdNumber.firstDigit(1900, FEMALE)).isEqualTo(2);
18+
assertThat(HungarianIdNumber.firstDigit(1901, FEMALE)).isEqualTo(2);
19+
assertThat(HungarianIdNumber.firstDigit(1999, MALE)).isEqualTo(1);
20+
assertThat(HungarianIdNumber.firstDigit(2000, MALE)).isEqualTo(3);
21+
assertThat(HungarianIdNumber.firstDigit(1999, FEMALE)).isEqualTo(2);
22+
assertThat(HungarianIdNumber.firstDigit(2000, FEMALE)).isEqualTo(4);
23+
}
24+
25+
@Test
26+
void checkDigit() {
27+
assertThat(HungarianIdNumber.getCheckDigit("807159215")).isEqualTo(3);
28+
assertThat(HungarianIdNumber.getCheckDigit("100000000")).isEqualTo(1);
29+
assertThat(HungarianIdNumber.getCheckDigit("010000000")).isEqualTo(2);
30+
assertThat(HungarianIdNumber.getCheckDigit("001000000")).isEqualTo(3);
31+
assertThat(HungarianIdNumber.getCheckDigit("000000001")).isEqualTo(9);
32+
assertThat(HungarianIdNumber.getCheckDigit("170010103")).isEqualTo(10);
33+
}
34+
}

src/test/java/net/datafaker/providers/base/IdNumberTest.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class IdNumberTest extends BaseFakerTest<BaseFaker> {
3434
private static final Faker UKRAINIAN = new Faker(new Locale("uk", "UA"));
3535
private static final Faker FRENCH = new Faker(new Locale("fr", "FR"));
3636
private static final Faker ITALIAN = new Faker(new Locale("it", "IT"));
37+
private static final Faker HUNGARIAN = new Faker(new Locale("hu", "HU"));
3738

3839
@Test
3940
void testValid() {
@@ -235,6 +236,16 @@ void italianIdNumber() {
235236
assertThatPin(ITALIAN.idNumber().valid()).matches(IdNumberPatterns.ITALIAN);
236237
}
237238

239+
@RepeatedTest(100)
240+
void hungarianIdNumber_valid() {
241+
assertThatPin(HUNGARIAN.idNumber().valid()).matches(IdNumberPatterns.HUNGARIAN);
242+
}
243+
244+
@RepeatedTest(100)
245+
void hungarianIdNumber_invalid() {
246+
assertThatPin(HUNGARIAN.idNumber().invalid()).matches(IdNumberPatterns.HUNGARIAN);
247+
}
248+
238249
private static AbstractStringAssert<?> assertThatPin(String pin) {
239250
return assertThat(pin)
240251
.as(() -> "PIN: %s".formatted(pin));

0 commit comments

Comments
 (0)