I am assuming a Danish personnumer (person number, “social security number”) of the format
ddmmyycxxg
For example for a woman born on August 24, 2001, the number might be
2408018314
The digits mean:
dd
: day of month of birth
mm
month of birth
yy
year of birth
c
code for century of birth; for historic reasons it has come to mean:
- 0–3: 1900–1999
- 4 or 9: 1937–2036
- 5–8 Either 1858–1899 or 2000–2057
xx
any digits
g
code for gender: even for women, odd for men
Or in code:
DateTimeFormatter formatter1858 = formatterWithBaseYear(1858);
DateTimeFormatter formatter1900 = formatterWithBaseYear(1900);
DateTimeFormatter formatter1937 = formatterWithBaseYear(1937);
DateTimeFormatter formatter2000 = formatterWithBaseYear(2000);
String personnummer = "2408018314";
char centuryCode = personnummer.charAt(6);
String birthDateString = personnummer.substring(0, 6);
LocalDate dateOfBirth;
switch (centuryCode) {
case '0':
case '1':
case '2':
case '3':
// 1900 - 1999
dateOfBirth = LocalDate.parse(birthDateString, formatter1900);
break;
case '4':
case '9':
// 1937 - 2036
dateOfBirth = LocalDate.parse(birthDateString, formatter1937);
break;
case '5':
case '6':
case '7':
case '8':
// 1858 - 1899 or 2000 - 2057
dateOfBirth = LocalDate.parse(birthDateString, formatter1858);
if (dateOfBirth.getYear() > 1899) {
dateOfBirth = LocalDate.parse(birthDateString, formatter2000);
assert dateOfBirth.getYear() >= 2000 && dateOfBirth.getYear() <= 2057 : dateOfBirth;
}
break;
default:
throw new IllegalStateException("Not a valid digit: " + centuryCode);
}
LocalDate today = LocalDate.now(ZoneId.of("Europe/Copenhagen"));
if (dateOfBirth.isAfter(today)) {
System.out.println("This person was born in the future??");
}
long ageInYears = ChronoUnit.YEARS.between(dateOfBirth, today);
System.out.println(ageInYears);
I am using the following auxiliary method for building the four formatters:
private static DateTimeFormatter formatterWithBaseYear(int year) {
return new DateTimeFormatterBuilder()
.appendPattern("ddMM")
.appendValueReduced(ChronoField.YEAR, 2, 2, year)
.toFormatter();
}
The snippet prints:
18
I saw the following issues with your code:
- It doesn’t seem to take into account that a person may be more than 100 years old. It seems to assume a birth year from 1920 through 2019. This will also cause trouble if you try it with a newborn baby in just 4 months from now (birth year will be 2020).
- You were trying to use the notoriously troublesome and long outdated
SimpleDateFormat
and the likewise outdated Date
class.
- You are parsing day, month and year separately. It’s better to leave the entire parsing to a
DateTimeFormatter
. It will parse everything at once and also provide validation that we are getting a valid date.
- Converting your parsed number back into a new string and parsing that string once more is overcomplicating things (and was also the reason for your trouble with parsing one-digit numbers back). Just avoid all of that.
- For getting today’s date just use
LocalDate.now(yourTimeZone)
. No reason for any formatting or parsing.
Link: Personnummeret i CPR-systemet (The personnummer in the Central Person Register System) documenting the meaning of the digits (in Danish).