|
| 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 | +} |
0 commit comments