diff --git a/src/main/java/net/datafaker/idnumbers/AlbanianIdNumber.java b/src/main/java/net/datafaker/idnumbers/AlbanianIdNumber.java index 60b9d1227..c40a33689 100644 --- a/src/main/java/net/datafaker/idnumbers/AlbanianIdNumber.java +++ b/src/main/java/net/datafaker/idnumbers/AlbanianIdNumber.java @@ -1,9 +1,16 @@ package net.datafaker.idnumbers; import net.datafaker.providers.base.BaseProviders; +import net.datafaker.providers.base.IdNumber.IdNumberRequest; +import net.datafaker.providers.base.PersonIdNumber; +import net.datafaker.providers.base.PersonIdNumber.Gender; import java.time.LocalDate; +import static net.datafaker.idnumbers.Utils.gender; +import static net.datafaker.idnumbers.Utils.birthday; +import static net.datafaker.providers.base.PersonIdNumber.Gender.FEMALE; + /** * The Albanian Identity Number is a unique personal identification number of 10 characters in the format YYMMDDSSSC */ @@ -24,19 +31,20 @@ public String generateInvalid(BaseProviders faker) { } @Override - public String generateValid(BaseProviders faker) { - LocalDate birthDate = faker.timeAndDate().birthday(0, 200); - boolean female = faker.bool().bool(); - String basePart = yy(birthDate.getYear()) + mm(birthDate.getMonthValue(), female) + dd(birthDate.getDayOfMonth()) + sss(faker); - return basePart + checksum(basePart); + public PersonIdNumber generateValid(BaseProviders faker, IdNumberRequest request) { + LocalDate birthDate = birthday(faker, request); + Gender gender = gender(faker, request); + String basePart = yy(birthDate.getYear()) + mm(birthDate.getMonthValue(), gender) + dd(birthDate.getDayOfMonth()) + sss(faker); + String idNumber = basePart + checksum(basePart); + return new PersonIdNumber(idNumber, birthDate, gender); } String yy(int year) { return FIRST_CHAR.charAt((year - 1800) / 10) + String.valueOf(year % 10); } - String mm(int month, boolean female) { - return String.format("%02d", (female ? 50 : 0) + month); + String mm(int month, Gender gender) { + return String.format("%02d", (gender == FEMALE ? 50 : 0) + month); } String dd(int dayOfMonth) { diff --git a/src/main/java/net/datafaker/idnumbers/AmericanIdNumber.java b/src/main/java/net/datafaker/idnumbers/AmericanIdNumber.java index b6c807392..f65b00086 100644 --- a/src/main/java/net/datafaker/idnumbers/AmericanIdNumber.java +++ b/src/main/java/net/datafaker/idnumbers/AmericanIdNumber.java @@ -1,10 +1,15 @@ package net.datafaker.idnumbers; import net.datafaker.providers.base.BaseProviders; +import net.datafaker.providers.base.IdNumber; +import net.datafaker.providers.base.PersonIdNumber; import java.util.List; import java.util.regex.Pattern; +import static net.datafaker.idnumbers.Utils.gender; +import static net.datafaker.idnumbers.Utils.birthday; + public class AmericanIdNumber implements IdNumberGenerator { @Override public String countryCode() { @@ -36,6 +41,11 @@ public String generateValid(BaseProviders f) { return isValid ? ssn : generateValid(f); } + @Override + public PersonIdNumber generateValid(BaseProviders faker, IdNumber.IdNumberRequest request) { + return new PersonIdNumber(generateValid(faker), birthday(faker, request), gender(faker, request)); + } + @Override public String generateInvalid(BaseProviders faker) { return faker.regexify(faker.options().nextElement(INVALID_SSNS)); diff --git a/src/main/java/net/datafaker/idnumbers/BulgarianIdNumber.java b/src/main/java/net/datafaker/idnumbers/BulgarianIdNumber.java index b06e82c70..d593807a9 100644 --- a/src/main/java/net/datafaker/idnumbers/BulgarianIdNumber.java +++ b/src/main/java/net/datafaker/idnumbers/BulgarianIdNumber.java @@ -1,9 +1,16 @@ package net.datafaker.idnumbers; import net.datafaker.providers.base.BaseProviders; +import net.datafaker.providers.base.IdNumber.IdNumberRequest; +import net.datafaker.providers.base.PersonIdNumber; +import net.datafaker.providers.base.PersonIdNumber.Gender; import java.time.LocalDate; +import static net.datafaker.idnumbers.Utils.gender; +import static net.datafaker.idnumbers.Utils.birthday; +import static net.datafaker.idnumbers.Utils.randomGender; + /** * Specification */ @@ -18,21 +25,22 @@ public String countryCode() { } @Override - public String generateValid(BaseProviders faker) { - String basePart = basePart(faker); - return basePart + checksum(basePart); + public PersonIdNumber generateValid(BaseProviders faker, IdNumberRequest request) { + LocalDate birthday = birthday(faker, request); + Gender gender = gender(faker, request); + String basePart = basePart(faker, birthday, gender); + String idNumber = basePart + checksum(basePart); + return new PersonIdNumber(idNumber, birthday, gender); } @Override public String generateInvalid(BaseProviders faker) { - String basePart = basePart(faker); + String basePart = basePart(faker, faker.timeAndDate().birthday(), randomGender(faker)); return basePart + (checksum(basePart) + 1) % 10; } - private String basePart(BaseProviders faker) { - LocalDate birthDate = faker.timeAndDate().birthday(0, 200); - boolean female = faker.bool().bool(); - return yy(birthDate) + mm(birthDate) + dd(birthDate) + order(faker, female); + private String basePart(BaseProviders faker, LocalDate birthDate, Gender gender) { + return yy(birthDate) + mm(birthDate) + dd(birthDate) + order(faker, gender); } private String yy(LocalDate birthDate) { @@ -49,8 +57,11 @@ private String dd(LocalDate birthDate) { return "%02d".formatted(birthDate.getDayOfMonth()); } - private String order(BaseProviders faker, boolean female) { - int[] availableLastDigits = female ? ODD_DIGITS : EVEN_DIGITS; + private String order(BaseProviders faker, Gender gender) { + int[] availableLastDigits = switch (gender) { + case FEMALE -> ODD_DIGITS; + case MALE -> EVEN_DIGITS; + }; int lastDigit = availableLastDigits[faker.number().numberBetween(0, 5)]; return faker.number().digits(2) + lastDigit; } diff --git a/src/main/java/net/datafaker/idnumbers/ChineseIdNumber.java b/src/main/java/net/datafaker/idnumbers/ChineseIdNumber.java index 5e56ef78a..547730cb7 100644 --- a/src/main/java/net/datafaker/idnumbers/ChineseIdNumber.java +++ b/src/main/java/net/datafaker/idnumbers/ChineseIdNumber.java @@ -1,8 +1,15 @@ package net.datafaker.idnumbers; import net.datafaker.providers.base.BaseProviders; +import net.datafaker.providers.base.IdNumber.IdNumberRequest; +import net.datafaker.providers.base.PersonIdNumber; import net.datafaker.service.RandomService; +import java.time.LocalDate; + +import static net.datafaker.idnumbers.Utils.gender; +import static net.datafaker.idnumbers.Utils.birthday; + /** * This class is used for generating Chinese ID numbers @@ -32,7 +39,8 @@ public String getValidSsn(BaseProviders faker) { * @return a Chinese ID number string */ @Override - public String generateValid(BaseProviders faker) { + public PersonIdNumber generateValid(BaseProviders faker, IdNumberRequest request) { + LocalDate birthday = birthday(faker, request); RandomService rand = faker.random(); String loc = faker.options().option(LOCATIONS); final int dayLength = 8; @@ -42,7 +50,7 @@ public String generateValid(BaseProviders faker) { res[i] = loc.charAt(i); } - generateDay(rand, 1930, 1, 1, 2030, 1, 12, res, locLength); + fillBirthday(res, locLength, birthday); res[locLength + dayLength] = (char)('0' + rand.nextInt(10)); res[locLength + dayLength + 1] = (char)('0' + rand.nextInt(10)); res[locLength + dayLength + 2] = (char)('0' + rand.nextInt(10)); @@ -65,27 +73,14 @@ public String generateValid(BaseProviders faker) { count += (res[15] - '0') * 4; count += (res[16] - '0') * 2; count %= 11; - if (count == 10) { - return String.valueOf(res) + "X"; - } else { - return String.valueOf(res) + count; - } + String idNumber = count == 10 ? String.valueOf(res) + "X" : String.valueOf(res) + count; + return new PersonIdNumber(idNumber, birthday, gender(faker, request)); } - private void generateDay(RandomService rand, int yearStart, int monthStart, int dayStart, int yearEnd, int monthEnd, int dayEnd, char[] res, int offset) { - final int year = rand.nextInt(yearStart, yearEnd); - final int month = rand.nextInt(monthStart, monthEnd); - final int lastDay; - if (month == 2) { - lastDay = - (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) ? - 29 : 28; - } else if (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12) { - lastDay = 31; - } else { - lastDay = 30; - } - int day = rand.nextInt(dayStart, Math.min(lastDay, dayEnd)); + private void fillBirthday(char[] res, int offset, LocalDate birthday) { + int year = birthday.getYear(); + int month = birthday.getMonthValue(); + int day = birthday.getDayOfMonth(); res[offset] = (char)('0' + year / 1000); res[offset + 1] = (char)('0' + (year % 1000) / 100); res[offset + 2] = (char)('0' + (year % 100) / 10); diff --git a/src/main/java/net/datafaker/idnumbers/EstonianIdNumber.java b/src/main/java/net/datafaker/idnumbers/EstonianIdNumber.java index d0b7b6453..b48421d91 100644 --- a/src/main/java/net/datafaker/idnumbers/EstonianIdNumber.java +++ b/src/main/java/net/datafaker/idnumbers/EstonianIdNumber.java @@ -1,10 +1,17 @@ package net.datafaker.idnumbers; import net.datafaker.providers.base.BaseProviders; +import net.datafaker.providers.base.IdNumber.IdNumberRequest; +import net.datafaker.providers.base.PersonIdNumber; +import net.datafaker.providers.base.PersonIdNumber.Gender; import java.time.LocalDate; import java.time.format.DateTimeFormatter; +import static net.datafaker.idnumbers.Utils.gender; +import static net.datafaker.idnumbers.Utils.birthday; +import static net.datafaker.idnumbers.Utils.randomGender; + /** * Estonian personal identification number ("Isikukood" in estonian) *
@@ -25,25 +32,38 @@ public String countryCode() { @Override public String generateInvalid(final BaseProviders faker) { - String digits = basePart(faker); + LocalDate birthday = faker.timeAndDate().birthday(); + String digits = basePart(faker, birthday, randomGender(faker)); return digits + (checksum(digits) + 1) % 10; } @Override - public String generateValid(final BaseProviders faker) { - String digits = basePart(faker); - return digits + checksum(digits); + public PersonIdNumber generateValid(BaseProviders faker, IdNumberRequest request) { + LocalDate birthday = birthday(faker, request); + Gender gender = gender(faker, request); + String digits = basePart(faker, birthday, gender); + String idNumber = digits + checksum(digits); + return new PersonIdNumber(idNumber, birthday, gender); } - private String basePart(BaseProviders faker) { - LocalDate birthday = faker.timeAndDate().birthday(0, 100); - return firstDigit(faker) + + private String basePart(BaseProviders faker, LocalDate birthday, Gender gender) { + return firstDigit(birthday.getYear(), gender) + BIRTHDAY_FORMAT.format(birthday) + faker.number().digits(3); } - private int firstDigit(BaseProviders faker) { - return faker.random().nextInt(1, 6); + static int firstDigit(int birthYear, Gender gender) { + int digit = switch (birthYear / 100) { + case 18 -> 1; + case 19 -> 3; + case 20 -> 5; + case 21 -> 7; + default -> throw new IllegalStateException("Too far in future: " + birthYear); + }; + return switch (gender) { + case FEMALE -> digit + 1; + case MALE -> digit; + }; } static int checksum(String numbers) { diff --git a/src/main/java/net/datafaker/idnumbers/GeorgianIdNumber.java b/src/main/java/net/datafaker/idnumbers/GeorgianIdNumber.java index e15cb7ec1..3c9384408 100644 --- a/src/main/java/net/datafaker/idnumbers/GeorgianIdNumber.java +++ b/src/main/java/net/datafaker/idnumbers/GeorgianIdNumber.java @@ -1,6 +1,11 @@ package net.datafaker.idnumbers; import net.datafaker.providers.base.BaseProviders; +import net.datafaker.providers.base.IdNumber; +import net.datafaker.providers.base.PersonIdNumber; + +import static net.datafaker.idnumbers.Utils.birthday; +import static net.datafaker.idnumbers.Utils.gender; /** * Generates ID numbers for Georgian citizens and Residents @@ -16,6 +21,15 @@ public String generateValid(BaseProviders faker) { return faker.numerify("###########"); } + @Override + public PersonIdNumber generateValid(BaseProviders faker, IdNumber.IdNumberRequest request) { + return new PersonIdNumber( + generateValid(faker), + birthday(faker, request), + gender(faker, request) + ); + } + @Override public String generateInvalid(BaseProviders faker) { return faker.numerify("###########42"); diff --git a/src/main/java/net/datafaker/idnumbers/IdNumberGenerator.java b/src/main/java/net/datafaker/idnumbers/IdNumberGenerator.java index e1e218b3c..89a59f73d 100644 --- a/src/main/java/net/datafaker/idnumbers/IdNumberGenerator.java +++ b/src/main/java/net/datafaker/idnumbers/IdNumberGenerator.java @@ -1,12 +1,12 @@ package net.datafaker.idnumbers; import net.datafaker.providers.base.BaseProviders; +import net.datafaker.providers.base.IdNumber.IdNumberRequest; +import net.datafaker.providers.base.PersonIdNumber; -import java.time.format.DateTimeFormatter; +import static net.datafaker.providers.base.IdNumber.GenderRequest.ANY; public interface IdNumberGenerator { - DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyMMdd"); - /** * ISO-2 code of the country this generator provides ID numbers for * @@ -17,10 +17,20 @@ public interface IdNumberGenerator { /** * Generates a valid ID number for given country (a.k.a. "SSN", "Personal code" etc.) */ - String generateValid(BaseProviders faker); + default String generateValid(BaseProviders faker) { + return generateValid(faker, new IdNumberRequest(18, 65, ANY)).idNumber(); + } /** * Generates an invalid ID number for given country (a.k.a. "SSN", "Personal code" etc.) */ String generateInvalid(BaseProviders faker); + + /** + * Generates a valid ID number for given country corresponding to given criterias (age range, gender etc.) + * + * @return PersonIdNumber containing a valid combination of ID, Birthday and Gender. + * In countries where ID number doesn't contain gender and/or birthday, the latter values are just random. + */ + PersonIdNumber generateValid(BaseProviders faker, IdNumberRequest request); } diff --git a/src/main/java/net/datafaker/idnumbers/MacedonianIdNumber.java b/src/main/java/net/datafaker/idnumbers/MacedonianIdNumber.java index 0fde1d2d6..5d1603a02 100644 --- a/src/main/java/net/datafaker/idnumbers/MacedonianIdNumber.java +++ b/src/main/java/net/datafaker/idnumbers/MacedonianIdNumber.java @@ -1,10 +1,17 @@ package net.datafaker.idnumbers; import net.datafaker.providers.base.BaseProviders; +import net.datafaker.providers.base.IdNumber.IdNumberRequest; +import net.datafaker.providers.base.PersonIdNumber; +import net.datafaker.providers.base.PersonIdNumber.Gender; import java.time.LocalDate; import java.util.List; +import static net.datafaker.idnumbers.Utils.gender; +import static net.datafaker.idnumbers.Utils.birthday; +import static net.datafaker.idnumbers.Utils.randomGender; + /** * The Macedonian Identity Number is a unique personal identification number of 13 digits in a form "DD MM YYY RR BBB K" * @@ -20,21 +27,23 @@ public String countryCode() { } @Override - public String generateValid(BaseProviders faker) { - String basePart = basePart(faker); - return basePart + checksum(basePart); + public PersonIdNumber generateValid(BaseProviders faker, IdNumberRequest request) { + LocalDate birthday = birthday(faker, request); + Gender gender = gender(faker, request); + String basePart = basePart(faker, birthday, gender); + return new PersonIdNumber(basePart + checksum(basePart), birthday, gender); } @Override public String generateInvalid(BaseProviders faker) { - String basePart = basePart(faker); + LocalDate birthday = faker.timeAndDate().birthday(); + Gender gender = randomGender(faker); + String basePart = basePart(faker, birthday, gender); return basePart + (checksum(basePart) + 1) % 10; } - private String basePart(BaseProviders faker) { - LocalDate bd = faker.timeAndDate().birthday(0, 120); - boolean female = faker.bool().bool(); - return dd(bd) + mm(bd) + yyy(bd) + rr(faker) + sss(faker, female); + private String basePart(BaseProviders faker, LocalDate bd, Gender gender) { + return dd(bd) + mm(bd) + yyy(bd) + rr(faker) + sss(faker, gender); } private String dd(LocalDate bd) { @@ -64,8 +73,11 @@ private String rr(BaseProviders faker) { * - from 000 to 499 for the male, and * - from 500 to 999 for the female citizens. */ - private String sss(BaseProviders faker, boolean female) { - int ordinal = female ? faker.number().numberBetween(500, 1000) : faker.number().numberBetween(0, 500); + private String sss(BaseProviders faker, Gender gender) { + int ordinal = switch (gender) { + case FEMALE -> faker.number().numberBetween(500, 1000); + case MALE -> faker.number().numberBetween(0, 500); + }; return "%03d".formatted(ordinal); } diff --git a/src/main/java/net/datafaker/idnumbers/MexicanIdNumber.java b/src/main/java/net/datafaker/idnumbers/MexicanIdNumber.java index f0f0c1960..a07a55426 100644 --- a/src/main/java/net/datafaker/idnumbers/MexicanIdNumber.java +++ b/src/main/java/net/datafaker/idnumbers/MexicanIdNumber.java @@ -1,10 +1,16 @@ package net.datafaker.idnumbers; import net.datafaker.providers.base.BaseProviders; +import net.datafaker.providers.base.IdNumber.IdNumberRequest; import net.datafaker.providers.base.Options; +import net.datafaker.providers.base.PersonIdNumber; +import net.datafaker.providers.base.PersonIdNumber.Gender; import java.time.LocalDate; +import static net.datafaker.idnumbers.Utils.gender; +import static net.datafaker.idnumbers.Utils.birthday; + /** * Implementation based on the definition at * https://en.wikipedia.org/wiki/Unique_Population_Registry_Code @@ -43,7 +49,6 @@ public String countryCode() { "OA", "PU", "QT", "QR", "SL", "SI", "SO", "TB", "TM", "TL", "VE", "YU", "ZA", "NE", }; - private static final char[] SEX = {'H', 'M'}; @Deprecated public String get(BaseProviders faker) { @@ -57,24 +62,33 @@ public String get(BaseProviders faker) { * @return A valid MEX CURP. */ @Override - public String generateValid(BaseProviders faker) { - final Options options = faker.options(); - char[] birthDay = getBirthday(faker).toCharArray(); + public PersonIdNumber generateValid(BaseProviders faker, IdNumberRequest request) { + Gender gender = gender(faker, request); + LocalDate birthday = birthday(faker, request); + char[] birthDay = formatBirthday(birthday).toCharArray(); final char[] ssn = new char[18]; + final Options options = faker.options(); ssn[0] = options.option(CONSONANT); ssn[1] = options.option(VOWEL); ssn[2] = options.option(CONSONANT); ssn[3] = options.option(CONSONANT); System.arraycopy(birthDay, 0, ssn, 4, 6); - ssn[10] = options.option(SEX); + ssn[10] = genderCharacter(gender); System.arraycopy(options.option(STATES).toCharArray(), 0, ssn, 11, 2); ssn[13] = options.option(VOWEL); ssn[14] = options.option(VOWEL); ssn[15] = options.option(VOWEL); ssn[16] = (birthDay[0] == '1' ? '0' : options.option(CONSONANT)); ssn[17] = String.valueOf(getChecksum(ssn)).charAt(0); - return String.valueOf(ssn); + return new PersonIdNumber(String.valueOf(ssn), birthday, gender); + } + + private char genderCharacter(Gender gender) { + return switch (gender) { + case FEMALE -> 'M'; + case MALE -> 'H'; + }; } @Deprecated @@ -94,13 +108,9 @@ public String generateInvalid(BaseProviders faker) { } /** - * Randomly gets a birthday. - * - * @param f A specific instance of Faker. - * @return A valid date. + * Formats given birthday to fit into ID Number */ - private String getBirthday(BaseProviders f) { - LocalDate birthday = f.timeAndDate().birthday(0, 120); + private String formatBirthday(LocalDate birthday) { return String.valueOf(birthday.getYear() * 10000 + birthday.getMonthValue() * 100 + birthday.getDayOfMonth()); } diff --git a/src/main/java/net/datafaker/idnumbers/MoldovanIdNumber.java b/src/main/java/net/datafaker/idnumbers/MoldovanIdNumber.java index be471c20c..92ca06d36 100644 --- a/src/main/java/net/datafaker/idnumbers/MoldovanIdNumber.java +++ b/src/main/java/net/datafaker/idnumbers/MoldovanIdNumber.java @@ -1,9 +1,14 @@ package net.datafaker.idnumbers; import net.datafaker.providers.base.BaseProviders; +import net.datafaker.providers.base.IdNumber.IdNumberRequest; +import net.datafaker.providers.base.PersonIdNumber; import java.time.LocalDate; +import static net.datafaker.idnumbers.Utils.birthday; +import static net.datafaker.idnumbers.Utils.randomGender; + /** * The Moldovan Individual Tax ID Number is 13 digits. *
@@ -14,7 +19,7 @@
*/
public class MoldovanIdNumber implements IdNumberGenerator {
- private static final int[] CHECKSUM_MASK = new int[]{7, 3, 1, 7, 3, 1, 7, 3, 1, 7, 3, 1};
+ private static final int[] CHECKSUM_MASK = {7, 3, 1, 7, 3, 1, 7, 3, 1, 7, 3, 1};
@Override
public String countryCode() {
@@ -22,19 +27,20 @@ public String countryCode() {
}
@Override
- public String generateValid(BaseProviders faker) {
- String basePart = basePart(faker);
- return basePart + checksum(basePart);
+ public PersonIdNumber generateValid(BaseProviders faker, IdNumberRequest request) {
+ LocalDate birthday = birthday(faker, request);
+ String basePart = basePart(faker, birthday);
+ String idNumber = basePart + checksum(basePart);
+ return new PersonIdNumber(idNumber, birthday, randomGender(faker));
}
@Override
public String generateInvalid(BaseProviders faker) {
- String basePart = basePart(faker);
+ String basePart = basePart(faker, faker.timeAndDate().birthday());
return basePart + (checksum(basePart) + 1) % 10;
}
- private String basePart(BaseProviders faker) {
- var birthday = faker.timeAndDate().birthday(0, 120);
+ private String basePart(BaseProviders faker, LocalDate birthday) {
// IDNP: 2ГГГXXXYYYYYK
return firstDigit() + ГГГ(birthday) + XXX(faker) + YYYYY(faker);
}
diff --git a/src/main/java/net/datafaker/idnumbers/PolishIdNumber.java b/src/main/java/net/datafaker/idnumbers/PolishIdNumber.java
index f5e212bbd..5657f366c 100644
--- a/src/main/java/net/datafaker/idnumbers/PolishIdNumber.java
+++ b/src/main/java/net/datafaker/idnumbers/PolishIdNumber.java
@@ -1,9 +1,17 @@
package net.datafaker.idnumbers;
import net.datafaker.providers.base.BaseProviders;
+import net.datafaker.providers.base.IdNumber.GenderRequest;
+import net.datafaker.providers.base.IdNumber.IdNumberRequest;
+import net.datafaker.providers.base.PersonIdNumber;
import java.time.LocalDate;
-import java.util.Optional;
+
+import static net.datafaker.idnumbers.Utils.birthday;
+import static net.datafaker.idnumbers.Utils.gender;
+import static net.datafaker.idnumbers.Utils.randomGender;
+import static net.datafaker.providers.base.PersonIdNumber.Gender.FEMALE;
+import static net.datafaker.providers.base.PersonIdNumber.Gender.MALE;
/**
* Implementation based on the definition at
@@ -19,30 +27,56 @@ public String countryCode() {
return "PL";
}
+ /**
+ * @deprecated Use {@link GenderRequest} instead
+ */
+ @Deprecated
public enum Gender {
MALE, FEMALE, ANY
}
@Override
- public String generateValid(BaseProviders faker) {
- return get(faker, faker.timeAndDate().birthday(0, 100), Gender.ANY);
+ public PersonIdNumber generateValid(BaseProviders faker, IdNumberRequest request) {
+ LocalDate birthday = birthday(faker, request);
+ PersonIdNumber.Gender gender = gender(faker, request);
+ return new PersonIdNumber(get(faker, birthday, gender), birthday, gender);
+ }
+
+ /**
+ * @param requestedGender nullable
+ * @deprecated Use {@link #generateValid(BaseProviders, IdNumberRequest)} instead
+ */
+ @Deprecated
+ public String get(BaseProviders faker, LocalDate birthDate, Gender requestedGender) {
+ PersonIdNumber.Gender gender = pickGender(faker, requestedGender);
+ return get(faker, birthDate, gender);
+ }
+
+ private static PersonIdNumber.Gender pickGender(BaseProviders faker, Gender requestedGender) {
+ return requestedGender == null ? randomGender(faker) :
+ switch (requestedGender) {
+ case ANY -> randomGender(faker);
+ case MALE -> MALE;
+ case FEMALE -> FEMALE;
+ };
}
- public String get(BaseProviders faker, LocalDate birthDate, Gender gender) {
- int[] digits = generateDigits(faker, birthDate, Optional.ofNullable(gender).orElse(Gender.ANY));
+ private String get(BaseProviders faker, LocalDate birthDate, PersonIdNumber.Gender gender) {
+ int[] digits = generateDigits(faker, birthDate, gender);
int controlDigit = getControlDigit(digits);
return toString(digits, controlDigit);
}
@Override
public String generateInvalid(BaseProviders faker) {
- int[] digits = generateDigits(faker, faker.timeAndDate().birthday(0, 100), Gender.ANY);
+ PersonIdNumber.Gender gender = randomGender(faker);
+ int[] digits = generateDigits(faker, faker.timeAndDate().birthday(), gender);
int controlDigit = getControlDigit(digits);
int invalidControlDigit = (controlDigit + 1) % 10;
return toString(digits, invalidControlDigit);
}
- private int[] generateDigits(BaseProviders faker, LocalDate birthDate, Gender gender) {
+ private int[] generateDigits(BaseProviders faker, LocalDate birthDate, PersonIdNumber.Gender gender) {
int monthEncoded = getMonthEncoded(birthDate.getYear(), birthDate.getMonthValue());
return new int[]{
birthDate.getYear() / 10 % 10,
@@ -72,7 +106,7 @@ private static String toString(int[] digits, int controlDigit) {
}
private int randomDigit(BaseProviders faker) {
- return faker.random().nextInt(10);
+ return faker.number().randomDigit();
}
private int getControlDigit(int[] digits) {
@@ -81,11 +115,10 @@ private int getControlDigit(int[] digits) {
return (10 - sum % 10) % 10;
}
- private int getGenderDigit(BaseProviders faker, Gender gender) {
+ private int getGenderDigit(BaseProviders faker, PersonIdNumber.Gender gender) {
return switch (gender) {
case FEMALE -> faker.random().nextInt(5) * 2;
case MALE -> faker.random().nextInt(5) * 2 + 1;
- default -> randomDigit(faker);
};
}
diff --git a/src/main/java/net/datafaker/idnumbers/PortugueseIdNumber.java b/src/main/java/net/datafaker/idnumbers/PortugueseIdNumber.java
index b89ae0f62..6e96333a2 100644
--- a/src/main/java/net/datafaker/idnumbers/PortugueseIdNumber.java
+++ b/src/main/java/net/datafaker/idnumbers/PortugueseIdNumber.java
@@ -1,6 +1,11 @@
package net.datafaker.idnumbers;
import net.datafaker.providers.base.BaseProviders;
+import net.datafaker.providers.base.IdNumber.IdNumberRequest;
+import net.datafaker.providers.base.PersonIdNumber;
+
+import static net.datafaker.idnumbers.Utils.gender;
+import static net.datafaker.idnumbers.Utils.birthday;
/**
* Portuguese VAT identification number (NIF)
@@ -44,6 +49,11 @@ public String generateValid(final BaseProviders faker) {
return digits + digitSum;
}
+ @Override
+ public PersonIdNumber generateValid(BaseProviders faker, IdNumberRequest request) {
+ return new PersonIdNumber(generateValid(faker), birthday(faker, request), gender(faker, request));
+ }
+
private int calculateDigitSum(String numbers) {
int checkSum = 0;
for (int i = 1; i <= numbers.length(); i++) {
diff --git a/src/main/java/net/datafaker/idnumbers/SingaporeIdNumber.java b/src/main/java/net/datafaker/idnumbers/SingaporeIdNumber.java
index ba7b948ea..cf3671456 100644
--- a/src/main/java/net/datafaker/idnumbers/SingaporeIdNumber.java
+++ b/src/main/java/net/datafaker/idnumbers/SingaporeIdNumber.java
@@ -1,14 +1,21 @@
package net.datafaker.idnumbers;
import net.datafaker.providers.base.BaseProviders;
+import net.datafaker.providers.base.IdNumber.IdNumberRequest;
+import net.datafaker.providers.base.PersonIdNumber;
+import net.datafaker.providers.base.PersonIdNumber.Gender;
import net.datafaker.service.RandomService;
-import java.util.EnumMap;
-import java.util.Map;
+import java.time.LocalDate;
+
+import static net.datafaker.idnumbers.Utils.gender;
+import static net.datafaker.idnumbers.Utils.randomGender;
+import static net.datafaker.providers.base.IdNumber.GenderRequest.ANY;
/**
* Generate number of UIN/FIN for Singapore.
* Algorithm is given from http://www.ngiam.net/NRIC/
+ * See ...
*/
public class SingaporeIdNumber implements IdNumberGenerator {
@Override
@@ -18,40 +25,44 @@ public String countryCode() {
public enum Type {SINGAPOREAN_TWENTIETH_CENTURY, FOREIGNER_TWENTIETH_CENTURY, SINGAPOREAN_TWENTY_FIRST_CENTURY, FOREIGNER_TWENTY_FIRST_CENTURY}
- private record NricType(char firstLetter, String matchLetters, int[] code, int initialValue) {
- private String format(int[] digits) {
- int value = initialValue;
- StringBuilder id = new StringBuilder(String.valueOf(firstLetter));
- for (int i = 0; i < digits.length; i++) {
- value += digits[i] * code[i];
- id.append(digits[i]);
- }
- value %= 11;
- id.append(matchLetters.charAt(value));
- return id.toString();
- }
+ private static String format(LocalDate issueDate, boolean citizen, int[] randomDigits) {
+ int checkDigitInitialValue = issueDate.getYear() < 2000 ? 0 : 4;
+ char firstLetter = citizen ? centuryPrefixCitizen(issueDate) : centuryPrefixForeigner(issueDate);
+ String matchLetters = citizen ? UIN_LETTERS : FIN_LETTERS;
+ int checkDigit = checkDigitInitialValue;
+
+ StringBuilder id = new StringBuilder(11);
+ id.append(firstLetter);
+ for (int i = 0; i < randomDigits.length; i++) {
+ checkDigit += randomDigits[i] * CODE[i];
+ id.append(randomDigits[i]);
}
+ checkDigit %= 11;
+ id.append(matchLetters.charAt(checkDigit));
+ return id.toString();
+ }
private static final int[] CODE = {0, 2, 7, 6, 5, 4, 3, 2};
private static final String FIN_LETTERS = "XWUTRQPNMLK";
private static final String UIN_LETTERS = "JZIHGFEDCBA";
- private static final Map
@@ -23,50 +29,49 @@ public String getValidRrn(BaseProviders f) {
}
@Override
- public String generateValid(BaseProviders f) {
+ public PersonIdNumber generateValid(BaseProviders f, IdNumberRequest request) {
StringBuilder patternBuilder = new StringBuilder();
- LocalDate now = LocalDate.now();
+ LocalDate birthday = birthday(f, request);
String iso = f.nation().isoCountry();
- String gender = f.gender().shortBinaryTypes();
+ Gender gender = gender(f, request);
// 1st to 6th digits indicate date of birth
- patternBuilder.append(generateDay(f.random(), now.getYear() - 65,
- now.getMonthValue(), now.getDayOfMonth(), now.getYear() - 18,
- now.getMonthValue(), now.getDayOfMonth()));
+ patternBuilder.append(generateDay(birthday));
// Matches RRN Pattern ( ######-####### )
patternBuilder.append('-');
// 7th digit indicates birth century, gender, nationality
- patternBuilder.append(get7thDigit(now.getYear(), gender, iso));
+ patternBuilder.append(get7thDigit(birthday.getYear(), gender, iso));
// From Oct 2020, 8 to 13 digits are randomized
// 8th to 13th digits are random digits
patternBuilder.append("######");
- return f.numerify(patternBuilder.toString());
+ String idNumber = f.numerify(patternBuilder.toString());
+ return new PersonIdNumber(idNumber, birthday, gender);
}
- private int get7thDigit(int year, String shortBinaryGender, String isoCountry) {
+ private int get7thDigit(int year, Gender gender, String isoCountry) {
// Local starts with 1, foreigner starts with 5
int locality = isoCountry.equalsIgnoreCase("kr") ? 1 : 5;
if (year < 1900) {
// Male: 9 | Female: 0
- return shortBinaryGender.equalsIgnoreCase("m") ? 9 : 0;
+ return gender == MALE ? 9 : 0;
} else if (year < 2000) {
// Male: 1, 5 | Female: 2, 6
- return locality + (shortBinaryGender.equalsIgnoreCase("m") ? 0 : 1);
+ return locality + (gender == MALE ? 0 : 1);
} else {
// Male: 3, 7 | Female: 4, 8
- return locality + (shortBinaryGender.equalsIgnoreCase("m") ? 2 : 3);
+ return locality + (gender == MALE ? 2 : 3);
}
}
- private String generateDay(RandomService rand, int yearStart, int monthStart, int dayStart, int yearEnd, int monthEnd, int dayEnd) {
- final int year = rand.nextInt(yearStart, yearEnd) % 100;
- final int month = rand.nextInt(monthStart, monthEnd);
- final int day = rand.nextInt(dayStart, dayEnd);
+ private String generateDay(LocalDate birthday) {
+ final int year = birthday.getYear() % 100;
+ final int month = birthday.getMonthValue();
+ final int day = birthday.getDayOfMonth();
final char[] res = new char[6];
res[0] = (char) ('0' + (year / 10));
res[1] = (char) ('0' + (year % 10));
diff --git a/src/main/java/net/datafaker/idnumbers/SwedenIdNumber.java b/src/main/java/net/datafaker/idnumbers/SwedenIdNumber.java
index 79eb996ea..2d28a0e08 100644
--- a/src/main/java/net/datafaker/idnumbers/SwedenIdNumber.java
+++ b/src/main/java/net/datafaker/idnumbers/SwedenIdNumber.java
@@ -1,11 +1,17 @@
package net.datafaker.idnumbers;
import net.datafaker.providers.base.BaseProviders;
+import net.datafaker.providers.base.IdNumber;
+import net.datafaker.providers.base.PersonIdNumber;
import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.temporal.ChronoField;
+import static net.datafaker.idnumbers.Utils.gender;
+import static net.datafaker.idnumbers.Utils.birthday;
+
/**
* Implementation based on the definition at
* https://www.skatteverket.se/privat/folkbokforing/personnummer.4.3810a01c150939e893f18c29.html
@@ -13,6 +19,8 @@
* https://en.wikipedia.org/wiki/Personal_identity_number_(Sweden)
*/
public class SwedenIdNumber implements IdNumberGenerator {
+ private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyMMdd");
+
@Override
public String countryCode() {
return "SE";
@@ -27,12 +35,14 @@ public String getValidSsn(BaseProviders f) {
}
@Override
- public String generateValid(BaseProviders f) {
+ public PersonIdNumber generateValid(BaseProviders f, IdNumber.IdNumberRequest request) {
+ LocalDate birthday = birthday(f, request);
String end = f.numerify("###");
- String candidate = DATE_TIME_FORMATTER.format(f.timeAndDate().birthday(0, 100))
+ String basePart = DATE_TIME_FORMATTER.format(birthday)
+ f.options().option(PLUS_MINUS)
+ end;
- return candidate + calculateChecksum(candidate);
+ String idNumber = basePart + calculateChecksum(basePart);
+ return new PersonIdNumber(idNumber, birthday, gender(f, request));
}
@Deprecated
diff --git a/src/main/java/net/datafaker/idnumbers/Utils.java b/src/main/java/net/datafaker/idnumbers/Utils.java
new file mode 100644
index 000000000..32a9b17f1
--- /dev/null
+++ b/src/main/java/net/datafaker/idnumbers/Utils.java
@@ -0,0 +1,32 @@
+package net.datafaker.idnumbers;
+
+import net.datafaker.providers.base.BaseProviders;
+import net.datafaker.providers.base.IdNumber;
+import net.datafaker.providers.base.IdNumber.IdNumberRequest;
+import net.datafaker.providers.base.PersonIdNumber.Gender;
+
+import java.time.LocalDate;
+
+import static net.datafaker.providers.base.PersonIdNumber.Gender.FEMALE;
+import static net.datafaker.providers.base.PersonIdNumber.Gender.MALE;
+
+public class Utils {
+
+ static LocalDate birthday(BaseProviders faker, IdNumberRequest request) {
+ return faker.timeAndDate().birthday(request.minAge(), request.maxAge());
+ }
+
+ static Gender gender(BaseProviders faker, IdNumberRequest request) {
+ IdNumber.GenderRequest gender = request.gender();
+ return switch (gender) {
+ case FEMALE -> FEMALE;
+ case MALE -> MALE;
+ case ANY -> randomGender(faker);
+ };
+ }
+
+ static Gender randomGender(BaseProviders faker) {
+ return faker.bool().bool() ? FEMALE : MALE;
+ }
+
+}
diff --git a/src/main/java/net/datafaker/providers/base/AbstractProvider.java b/src/main/java/net/datafaker/providers/base/AbstractProvider.java
index 5663180b8..314c2fa52 100644
--- a/src/main/java/net/datafaker/providers/base/AbstractProvider.java
+++ b/src/main/java/net/datafaker/providers/base/AbstractProvider.java
@@ -37,7 +37,7 @@ public int hashCode() {
return getClass().hashCode();
}
- protected