2

i'm trying to compare a personalnumber with the current date to get the persons age.

The current problem is that if my personalnumber has a zero in it, it gets removed so i can't parse it.

        for (int i = 0; i <= personList.size(); i++) {

            SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
            String date = formatter.format(new Date());

            String temp = personList.get(i).getCPR();
            int year = Integer.parseInt((temp.substring(4, 6)));
            if (year < 20) {
                year += 2000;
            } else {
                year += 1900;
            }

            int month = Integer.parseInt(temp.substring(2, 4));
            int day = Integer.parseInt(temp.substring(0, 2));

            /*if (month if month don't start with zero){
                    add 0 to month on the left
                }
                    same goes for day*/


            String birthday = year + "-" + month + "-" + day;

            LocalDate date1 = LocalDate.parse(birthday);
            LocalDate date2 = LocalDate.parse(date);

            long age = date1.until(date2, ChronoUnit.YEARS);

            System.out.println(age);
        }

this is the error i'm getting

Exception in thread "main" java.time.format.DateTimeParseException: Text '1995-1-13' could not be parsed at index 5

I want 1995-1-13 to read 1995-01-13

FakeRune
  • 63
  • 1
  • 3
  • 13
  • I would recommend to go through the simpledateformat java documentation and see which format suits your requirement. https://docs.oracle.com/javase/8/docs/api/java/text/SimpleDateFormat.html A quick fix for your code would be to change the birthday expression as shown below String birthday = String.format("%d-%02d-%02d",year,month,day); – Vinoth A Sep 07 '19 at 12:24
  • I recommend you don’t use `SimpleDateFormat` and `Date`. Those classes are poorly designed and long outdated, the former in particular notoriously troublesome. Instead Stick to the `LocalDate` from [java.time, the modern Java date and time API](https://docs.oracle.com/javase/tutorial/datetime/) that you are already using, and if you need, also `DateTimeFormatter` from this API. – Ole V.V. Sep 07 '19 at 12:51

4 Answers4

4

The formatter you want is "yyyy-M-d" that will take care of both single digit and double digit month and day values:

 DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-M-d");
 String temp ="1995-1-13";

 LocalDate date1 = LocalDate.parse(temp, formatter);
 long age = date1.until(LocalDateTime.now(), ChronoUnit.YEARS);
 System.out.println(age);

Output:

 24

This is as per the JDK docs:

Number: If the count of letters is one, then the value is output using the minimum number of digits and without padding. Otherwise, the count of digits is used as the width of the output field, with the value zero-padded as necessary.

Do not use the SimpleDateFormat classes if you are in Java 8+. I assumed you are on Java 8 as you used LocalDate classes. Also instead of using String#substring with hardcoded indexes to separate the date components use the DateTimeFormatter#ofPattern to parse the String date into a LocalDate.

Fullstack Guy
  • 16,368
  • 3
  • 29
  • 44
1

You probably don't need these two lines:

int month = Integer.parseInt(temp.substring(2, 4));
int day = Integer.parseInt(temp.substring(0, 2));

because you add those Integers again to string.

StringBuilder birthday = new StringBuilder();
birthday.append(year);
birthday.append("-");
birthday.append(temp.substring(2,4));
birthday.append("-");
birthday.append(temp.substring(0,2));
LocalDate date1 = LocalDate.parse(birthday.toString());
madnan
  • 161
  • 1
  • 7
0

Try this:

// Pass the date in YYYY-MM-dd format, i.e., 1995-1-15
@SuppressLint("SimpleDateFormat")
public void parseDate(String date) {
    SimpleDateFormat dateFormat = new SimpleDateFormat("YYYY-MM-dd");
    try {
        // Parsing and then formatting date using SimpleDateFormat object.
        String requiredDate = dateFormat.format(dateFormat.parse(date));

        // Printing in the log
        Log.d("Parsed Date", requiredDate);
    } catch (ParseException e) {
        e.printStackTrace();
    }
}
MojoJojo
  • 783
  • 5
  • 15
  • One, `Date` and `SimpleDateFormat` are poorly designed, long outdated and not recommended at all. Two, when I pass a date string of `1995-1-15` to your code, I get a `requiredDate` of `1995-01-01`. – Ole V.V. Sep 07 '19 at 17:56
0

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).

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161