Skip to content

Instantly share code, notes, and snippets.

@mvniekerk
Last active August 15, 2019 07:58
Show Gist options
  • Save mvniekerk/83038273623d4c601ffc8caa44e46400 to your computer and use it in GitHub Desktop.
Save mvniekerk/83038273623d4c601ffc8caa44e46400 to your computer and use it in GitHub Desktop.
Validate a South African ID number
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