5

I am trying to get a user's age based on their date of birth. The date of birth is given as a string in XML and converted to a Calendar like so:

final Calendar dob = javax.xml.bind.DatatypeConverter.parseDate(value);

Then I am calculating the user's age like so:

final Calendar now = Calendar.getInstance();
int age = now.get(Calendar.YEAR) - dob.get(Calendar.YEAR);
if (now.get(Calendar.DAY_OF_YEAR) < dob.get(Calendar.DAY_OF_YEAR)) {
  --age;
}

I discovered today that if today is the user's birthday (put the party hats away, it isn't mine), the age comes out a year too young. That is, if the user was born in 2000 and today is her birthday, she should be 14 years old, not 13. But when it comes down to it, Java seems to have the DAY_OF_YEAR wrong:

System.out.println(String.format("Today: %d-%d; Birthday: %d-%d", now.get(Calendar.MONTH), now.get(Calendar.DAY_OF_MONTH), dob.get(Calendar.MONTH), dob.get(Calendar.DAY_OF_MONTH)));
// prints: Today: 9-22; Birthday: 9-22
System.out.println(String.format("Today: %d; Birthday: %d", now.get(Calendar.DAY_OF_YEAR), dob.get(Calendar.DAY_OF_YEAR)));
// prints: Today: 295; Birthday: 296

What gives?

meustrus
  • 6,637
  • 5
  • 42
  • 53

2 Answers2

8

An edge condition is causing the issue.

What is special about 2000?

It's a leap year.

    Calendar cal = new GregorianCalendar();
    cal.set(2000, 11, 31);
    System.out.println(cal.getTime());
    System.out.println(cal.get(Calendar.DAY_OF_YEAR));

Output:

Sun Dec 31 13:43:28 EST 2000
366

Everything after February 29th is offset by 1 specifically for leap years. Ergo, it's not wrong. In fact, it's working as intended.

You should instead be comparing month and day of month to get around this issue.

Compass
  • 5,867
  • 4
  • 30
  • 42
  • Thank you for your answer. You win the award for most edits I've ever seen within just a couple of minutes. Just give me the chance to test the solution! – meustrus Oct 22 '14 at 17:47
  • Also, this isn't a race condition. I suppose you'd call it an edge case. But yeah, thanks for catching the leap year problem. – meustrus Oct 22 '14 at 17:52
  • @meustrus I have made an edit for you changing to 'edge' – Compass Oct 22 '14 at 17:54
0

Using joda:

DateTime jodaDob = new DateTime(dob);
DateTime now = new DateTime();
if (jodaDob.isAfter(now)) {
    age = age - 1;
}

java.util.Date has many bugs in the corner cases that joda takes care of.

hd1
  • 33,938
  • 5
  • 80
  • 91