Created
October 30, 2012 08:36
-
-
Save banterCZ/3979014 to your computer and use it in GitHub Desktop.
Validator of national identification number used in the Czech Republic.
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 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; | |
} | |
} | |
} |
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 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
commented
Oct 30, 2012
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment