HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavaMinor

Age in years at a specific date

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
agespecificdateyears

Problem

For eligibility to events I need to know the age of a competitor at a specific date.

Now the thing is the date calculations are checked in two places, an online PHP app where the entries are registered, and an offline Java app that runs the events.

So I have 2 versions of the same code, the PHP one was written originally and the Java is a conversion of that. (So that I am using the same calculations in both places).

My Java skills are very rusty.

I am happy to take any improvements onboard, but it is essential that both calculations return the same result.

PHP

public function ageAt($age_reference_date) {
    $ts_ref = strtotime($age_reference_date);       
    list($dob_year, $dob_month, $dob_day) = explode('-', $this->date_of_birth);

    $age = date('Y', $ts_ref) - $dob_year;      

    if (date('md', $ts_ref) < $dob_month.$dob_day) {
        $age--;
    } 

    return $age;
}


Java

public int ageAt(Date ageReferenceDate) {
    Calendar calAgeReferenceDate = Calendar.getInstance();
    Calendar calDateOfBirth = Calendar.getInstance();
    int refDateMonth;
    int dobDateMonth;
    int age;

    calAgeReferenceDate.setTime(ageReferenceDate);

    calDateOfBirth.setTime(this.dateOfBirth);
    age = calAgeReferenceDate.get(Calendar.YEAR) - calDateOfBirth.get(Calendar.YEAR);

    refDateMonth = Integer.parseInt(String.format("%02d", calAgeReferenceDate.get(Calendar.MONTH)) + String.format("%02d", calAgeReferenceDate.get(Calendar.DAY_OF_MONTH)));
    dobDateMonth = Integer.parseInt(String.format("%02d", calDateOfBirth.get(Calendar.MONTH)) + String.format("%02d", calDateOfBirth.get(Calendar.DAY_OF_MONTH)));

    if (refDateMonth < dobDateMonth) {
        age--;
    }

    return age;
}

Solution

In Java 8 you can more accurately represent the dateOfBirth with a LocalDate.
If the ageAt() method also takes a LocalDate as argument, then the age is easily computed using Period.

public int ageAt(LocalDate ageReferenceDate) {
    return Period.between(dateOfBirth, ageReferenceDate).getYears();
}


It is fairly easy, albeit a bit verbose, to convert a Date instance to a LocalDate :

LocalDate localDate = ZonedDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()).toLocalDate();


The catch is that you need to interpret it vs. a ZoneId. A Date can be mapped to different LocalDates depending on what ZoneId (TimeZone's successor) it is evaluated against. You should use the ZoneId that was used when the Date was recorded. That is probably ZoneId.systemDefault(), but if your code can run on systems accross time zones, you can have a problem.

This is also the reason why it is a better idea to represent date of birth as a LocalDate, since it does away with that ambiguity.

Code Snippets

public int ageAt(LocalDate ageReferenceDate) {
    return Period.between(dateOfBirth, ageReferenceDate).getYears();
}
LocalDate localDate = ZonedDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()).toLocalDate();

Context

StackExchange Code Review Q#85002, answer score: 2

Revisions (0)

No revisions yet.