0

I wrote what I thought would be simple time API call to get the amount of months and years between two LocalDates. However, when I am doing my tests the numbers do not seem to be computing correctly.

https://repl.it/@trajano/LocalDate-Period-Ages?language=java

import java.time.*;
import java.time.temporal.*;
import static org.junit.Assert.assertEquals;
class Main {
  public static void main(String[] args) {

    {
        User user = new User();
        user.setDateOfBirth(LocalDate.of(2014, 11, 11));
        assertEquals(0, user.getAgeInMonths(LocalDate.of(2014, 11, 11)));
        assertEquals(0, user.getAgeInMonths(LocalDate.of(2014, 11, 12)));
        assertEquals(0, user.getAgeInMonths(LocalDate.of(2014, 12, 10)));
        assertEquals(1, user.getAgeInMonths(LocalDate.of(2014, 12, 11)));
        assertEquals(1, user.getAgeInMonths(LocalDate.of(2014, 12, 25)));
        assertEquals(1, user.getAgeInMonths(LocalDate.of(2015, 1, 6)));
        assertEquals(1 + 12, user.getAgeInMonths(LocalDate.of(2016, 1, 6)));
        assertEquals(1 + 12 * 2, user.getAgeInMonths(LocalDate.of(2017, 1, 6)));
        assertEquals(1 + 12 * 3, user.getAgeInMonths(LocalDate.of(2018, 1, 6)));    }
  }
}

class User {
  LocalDate dateOfBirth;

  public void setDateOfBirth(LocalDate dateOfBirth) {
    this.dateOfBirth = dateOfBirth;
  }

  public int getAge(final LocalDate processingDate) {

    return (int) Period.between(dateOfBirth, processingDate.plusDays(1)).get(ChronoUnit.YEARS);
  }

  public int getAgeInMonths(final LocalDate processingDate) {
    return (int) Period.between(dateOfBirth, processingDate.plusDays(1)).get(ChronoUnit.MONTHS);
  }

  public int getAge() {
    return getAge(LocalDate.now());
  }

  public int getAgeInMonths() {
    return getAgeInMonths(LocalDate.now());
  }
}

I tried both Period. and ChronoUnit.MONTHS but the assertions fail. Using the code from Java Time period in decimal number of years Java 8 Date time for calculating age in decimals

return (int) Period.between(dateOfBirth, processingDate.plusDays(1)).get(ChronoUnit.MONTHS);

and

return (int) ChronoUnit.MONTHS.between(dateOfBirth, processingDate.plusDays(1));

UPDATE: I made a mistake on the assertions. So I fixed them.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
Archimedes Trajano
  • 35,625
  • 19
  • 175
  • 265

2 Answers2

2

My version would probably be:

public int getAgeInMonths(LocalDate processingDate) {
    return Math.toIntExact(ChronoUnit.MONTHS.between(dateOfBirth, processingDate));
}

As a matter of taste I find it completely superfluous to declare the parameter final. Whether the method modifies the parameter or not is an implementation detail not relevant in the method signature.

I prefer Math.toIntExact as a safer way of converting from long to int. It will throw an exception in case of int overflow.

Also as a matter of taste, when all we are interested in is months, we don’t need to go through the Period class.

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

Here's what finally worked

public int getAge(final LocalDate processingDate) {

    return Period.between(dateOfBirth, processingDate).getYears();
}

public int getAgeInMonths(final LocalDate processingDate) {

    return (int) Period.between(dateOfBirth, processingDate).toTotalMonths();

}
Archimedes Trajano
  • 35,625
  • 19
  • 175
  • 265