Resposta moderna e visão geral
a) Java-8 (java.time-package)
LocalDate start = LocalDate.of(1996, 2, 29);
LocalDate end = LocalDate.of(2014, 2, 28); // use for age-calculation: LocalDate.now()
long years = ChronoUnit.YEARS.between(start, end);
System.out.println(years); // 17
Observe que a expressão LocalDate.now()
está implicitamente relacionada ao fuso horário do sistema (que geralmente é ignorado pelos usuários). Para maior clareza, geralmente é melhor usar o método sobrecarregado, now(ZoneId.of("Europe/Paris"))
especificando um fuso horário explícito (aqui "Europa / Paris", por exemplo). Se o fuso horário do sistema for solicitado, minha preferência pessoal é escrever LocalDate.now(ZoneId.systemDefault())
para tornar a relação com o fuso horário do sistema mais clara. Isso é mais trabalhoso, mas facilita a leitura.
b) Tempo de Joda
Observe que a solução Joda-Time proposta e aceita produz um resultado computacional diferente para as datas mostradas acima (um caso raro), a saber:
LocalDate birthdate = new LocalDate(1996, 2, 29);
LocalDate now = new LocalDate(2014, 2, 28); // test, in real world without args
Years age = Years.yearsBetween(birthdate, now);
System.out.println(age.getYears()); // 18
Considero isso um bug pequeno, mas a equipe da Joda tem uma visão diferente desse comportamento estranho e não deseja corrigi-lo (estranho porque o dia do mês da data de término é menor que o da data de início, portanto o ano deve ser um a menos). Veja também esta edição encerrada .
c) java.util.Calendar etc.
Para comparação, veja as várias outras respostas. Eu não recomendaria o uso dessas classes desatualizadas, porque o código resultante ainda é suscetível a erros em alguns casos exóticos e / ou muito complexo, considerando o fato de que a pergunta original parece tão simples. No ano de 2015, temos bibliotecas realmente melhores.
d) Sobre o Date4J:
A solução proposta é simples, mas às vezes falha em caso de anos bissextos. Apenas avaliar o dia do ano não é confiável.
e) Minha própria biblioteca Time4J :
Isso funciona de maneira semelhante à solução Java-8. Basta substituir LocalDate
por PlainDate
e ChronoUnit.YEARS
por CalendarUnit.YEARS
. No entanto, obter "hoje" requer uma referência explícita ao fuso horário.
PlainDate start = PlainDate.of(1996, 2, 29);
PlainDate end = PlainDate.of(2014, 2, 28);
// use for age-calculation (today):
// => end = SystemClock.inZonalView(EUROPE.PARIS).today();
// or in system timezone: end = SystemClock.inLocalView().today();
long years = CalendarUnit.YEARS.between(start, end);
System.out.println(years); // 17