Last active
August 15, 2019 07:58
-
-
Save mvniekerk/83038273623d4c601ffc8caa44e46400 to your computer and use it in GitHub Desktop.
Validate a South African ID number
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.time.YearMonth; | |
import java.util.Optional; | |
import java.util.regex.Pattern; | |
import java.util.stream.Collectors; | |
import java.util.stream.IntStream; | |
import java.util.stream.Stream; | |
public class RsaIdValidator { | |
public static boolean validateRsaId(String id, boolean validateGender, boolean isMale) { | |
return Optional.ofNullable(id) | |
.filter(i -> Pattern.matches("^\\d{13}", i)) | |
.map(i -> Stream.of(i.split("")).map(Integer::parseInt).collect(Collectors.toList())) | |
// Validate date of birth | |
.filter(digits -> { | |
int month = digits.get(2) * 10 + digits.get(3); | |
if (month > 12 || month < 1) { | |
return false; | |
} | |
YearMonth now = YearMonth.now(); | |
int yearNow = now.getYear(); | |
int monthNow = now.getMonthValue(); | |
int century = yearNow / 100; | |
yearNow = (yearNow % 10) + ((yearNow / 10) % 10) * 10; // If 2019, get the 19 | |
int year = digits.get(0) * 10 + digits.get(1); | |
// 18 -> 2018, 20 -> 1920, 19 -> 2019 if month < nowMonth given calculated in 2019 | |
year += yearNow > year || (yearNow == year && month <= monthNow) | |
? century*100 | |
: (century - 1)* 100; | |
int day = digits.get(4)*10 + digits.get(5); | |
return YearMonth.of(year, month).lengthOfMonth() >= day; | |
}) | |
.filter(digits -> !validateGender || (isMale && digits.get(6) < 5)) | |
.filter(digits -> digits.get(10) == 0 || digits.get(10) == 1) // 11th digit must be either 0 (RSA citizen) or 1 (Permanent resident) | |
// Start calculating the Luhn check number | |
.map(digits -> | |
IntStream.range(0, digits.size()/2) | |
.map(i -> i * 2 + 1 ) // Get even indexes if arrays starts at 1 | |
.map(digits::get) | |
.map(i -> i * 2) // Even indexes x 2 | |
.map(i -> i % 10 + ((i / 10) % 10)) // Sum the characters | |
.reduce(0, Integer::sum) + | |
IntStream.range(0, digits.size()/2) | |
.map(i -> i * 2) // Get odd indexes | |
.map(digits::get) | |
.map(i -> i % 10 + ((i / 10) % 10)) | |
.reduce(0, Integer::sum) | |
) | |
.map(i -> (10 - (i % 10)) % 10) // Luhn number here | |
.map(i -> Integer.parseInt(id.substring(id.length() - 2)) % 10 == i) // Compare the calculated Luhn check with the last char of the number | |
.orElse(false); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment