Skip to content

Instantly share code, notes, and snippets.

@banterCZ
Created October 30, 2012 08:36
Show Gist options
  • Save banterCZ/3979014 to your computer and use it in GitHub Desktop.
Save banterCZ/3979014 to your computer and use it in GitHub Desktop.
Validator of national identification number used in the Czech Republic.
import static java.util.Calendar.DAY_OF_MONTH;
import static java.util.Calendar.MONTH;
import static java.util.Calendar.YEAR;
import java.util.Calendar;
/**
*
* Validator of national identification number used in the Czech Republic.
*
* See wiki <a href="http://cs.wikipedia.org/wiki/Rodn%C3%A9_%C4%8D%C3%ADslo">http://cs.wikipedia.org/wiki/Rodn%C3%A9_%C4%8D%C3%ADslo</a>
*
* @author banterCZ
*/
public class RCValidator {
/**
* Validates the given national identification number.
*
* @param value to validate (without slash, e.g. 7310285169), may be <code>null</code> or an empty string but returns <code>false</code> in that case
* @return <code>true</code> when number is valid, <code>false</code> otherwise
*/
public boolean isValid(String value) {
if (value == null) {
return false;
}
if (value.length() < 9 || value.length() > 10) {
return false;
}
int year;
int month;
int day;
try {
year = Integer.parseInt(value.substring(0, 2));
month = Integer.parseInt(value.substring(2, 4));
day = Integer.parseInt(value.substring(4, 6));
} catch (NumberFormatException e) {
return false;
}
if (value.length() == 9 && (year >= 54)) {
return false;
}
if (value.length() == 10) {
long mod = Long.parseLong(value.substring(0, 9)) % 11;
int lastNumber = Integer.parseInt(value.substring(9));
if (mod == 10) {
if (lastNumber != 0) {
return false;
}
} else if (lastNumber != mod) {
return false;
}
}
return isDayAndMonthValid(value, year, month, day);
}
private boolean isDayAndMonthValid(String value, int year, int month, int day) {
year = getYearWithHunders(year, value);
month = getRealMonth(year, month);
if (month == 0 || month > 12) {
return false;
}
if (day == 0) {
return false;
}
Calendar calendar = Calendar.getInstance();
calendar.clear();
calendar.set(YEAR, year);
calendar.set(MONTH, month - 1);
int daysInMonth = calendar.getActualMaximum(DAY_OF_MONTH);
if (daysInMonth < day) {
return false;
}
return true;
}
/**
* Removes possible offset (20, 50, or 70).
*
* @param year
* @param month may be with offset
* @return month without offset
*/
private int getRealMonth(int year, int month) {
if ((month > 70) && (year > 2003)) {
month -= 70;
} else if (month > 50) {
month -= 50;
} else if ((month > 20) && (year > 2003)) {
month -= 20;
}
return month;
}
/**
* Possible problem. People born before 1954 usually have the national identification number of length 9 but might be also 10.
* Little bit risky for somebody who is hundred-year old.
*
* @param year
* @param value
* @return
*/
private int getYearWithHunders(int year, String value) {
if (value.length() == 9) {
return year + 1900;
}
if (year > Calendar.getInstance().get(YEAR) % 100) {
return year + 1900;
} else {
return year + 2000;
}
}
}
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.Before;
import org.junit.Test;
/**
* Test for {@link RCValidator}.
*
* @author banterCZ
*/
public class RCValidatorTest {
private RCValidator validator;
@Before
public void setUp() {
validator = new RCValidator();
}
@Test
public void nullValue() {
boolean result = validator.isValid(null);
assertFalse(result);
}
@Test
public void empty() {
boolean result = validator.isValid("");
assertFalse(result);
}
@Test
public void numberFormatException() {
boolean result = validator.isValid("x123456789");
assertFalse(result);
}
/**
* Only 8 instead of 9 or 10 numbers.
*/
@Test
public void invalidLengthShort() {
boolean result = validator.isValid("73602851");
assertFalse(result);
}
/**
* Too many characters, 11 instead of 9 or 10 numbers.
*/
@Test
public void invalidLengthLong() {
boolean result = validator.isValid("73102851691");
assertFalse(result);
}
/**
* 731028/5169
*/
@Test
public void validNormalMan() {
boolean result = validator.isValid("7310285169");
assertTrue(result);
}
/**
* 736000/5158
*/
@Test
public void invalidDayZero() {
boolean result = validator.isValid("7360005158");
assertFalse(result);
}
/**
* 736033/5158
*/
@Test
public void invalidDay() {
boolean result = validator.isValid("7360335158");
assertFalse(result);
}
/**
* 120230/0011
*/
@Test
public void invalidDayFebruary() {
boolean result = validator.isValid("1202300011");
assertFalse(result);
}
/**
* 2011-02-29
*/
@Test
public void invalidLeapYear() {
boolean result = validator.isValid("1102290002");
assertFalse(result);
}
/**
* 2012-02-29
*/
@Test
public void validLeapYear() {
boolean result = validator.isValid("1202290001");
assertTrue(result);
}
/**
* Only about 1000 existing birthNumbers
*
* 840501/1330
*/
@Test
public void validExceptionMod11() {
boolean result = validator.isValid("8405011330");
assertTrue(result);
}
/**
* 840501/133
* @see #validExceptionMod11()
*/
@Test
public void invalidExceptionMod11() {
boolean result = validator.isValid("8405011331");
assertFalse(result);
}
/**
* 835607/8656
*/
@Test
public void invalid() {
boolean result = validator.isValid("8356078656");
assertFalse(result);
}
/**
* 530506/1234
* May happen, but default length for years 1953 and earlier is 9.
*/
@Test
public void oldValid10() {
boolean result = validator.isValid("5305061234");
assertTrue(result);
}
/**
* 530506/123
*/
@Test
public void oldValid9() {
boolean result = validator.isValid("530506123");
assertTrue(result);
}
/**
* 530506/12
*/
@Test
public void oldInvalid8() {
boolean result = validator.isValid("53050612");
assertFalse(result);
}
/**
* 540506/123
*/
@Test
public void newInvalid8() {
boolean result = validator.isValid("540506123");
assertFalse(result);
}
/**
* 120203/1237
*/
@Test
public void validAfter2K() {
boolean result = validator.isValid("1202031237");
assertTrue(result);
}
/**
* 128231/0007
*/
@Test
public void monthOffset70() {
boolean result = validator.isValid("1282310007");
assertTrue(result);
}
/**
* 038231/0005
*/
@Test
public void invalidMonthOffset70() {
boolean result = validator.isValid("0382310005");
assertFalse(result);
}
/**
* 126231/0005
*/
@Test
public void monthOffset50() {
boolean result = validator.isValid("1262310005");
assertTrue(result);
}
/**
* 123231/0002
*/
@Test
public void monthOffset20() {
boolean result = validator.isValid("1232310002");
assertTrue(result);
}
/**
* 033231/0000
*/
@Test
public void invalidMonthOffset20() {
boolean result = validator.isValid("0332310000");
assertFalse(result);
}
/**
* 120001/0009
*/
@Test
public void invalidMonth() {
boolean result = validator.isValid("1200010009");
assertFalse(result);
}
}
@mormegil-cz
Copy link

    /**
     * Invalid (non-numeric) characters: 123456/foo!
     */
    @Test
    public void invalidCharacters() {
        boolean result = validator.isValid("123456foo!");
        assertFalse(result);
    }

    /**
     * Minus sign: -20101/000
     */
    @Test
    public void negativeYear() {
        boolean result = validator.isValid("-20101000");
        assertFalse(result);
    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment