Idade em anos meses e dias com JODA-TIME

Olá!

Estou com o seguinte problema e gostaria de saber se podem me ajudar. Há dias estou no desenvolvimento de um método para mostrar a idade de uma pessoa no formato de anos, dias e meses. Ao realizar várias pesquisas sobre o assunto, decidi usar os métodos da API JODA TIME. Os anos e meses mostram corretamente, porém a contagem de dias está incorreta. Não consegui entender o que o método está fazendo. Segue abaixo o código:

import org.joda.time.format.*;
import org.joda.time.*;

public class TesteIdade{

  public String diferenca_entre_datas(String data_inicio, String data_fim){

  int ano_inicio = devolverAno(data_inicio);
  int mes_inicio = devolverMes(data_inicio);
  int dia_inicio = devolverDia(data_inicio);

  int ano_fim = devolverAno(data_fim);
  int mes_fim = devolverMes(data_fim);
  int dia_fim = devolverDia(data_fim);

  DateTime data_inicial_partes = new DateTime(ano_inicio, mes_inicio, dia_inicio, 0, 0, 0, 0);
  DateTime data_final_partes   = new DateTime(ano_fim, mes_fim, dia_fim, 0, 0, 0, 0);

	 Period per = new Period(data_inicial_partes, data_final_partes);

		PeriodFormatter pf = new PeriodFormatterBuilder()
		.printZeroAlways()
		.appendYears()
		.appendSuffix (" ano "," anos ")
		//.appendSeparator("")
		//.printZeroAlways()
		.appendMonths()
		.appendSuffix (" mes "," meses ")
		//.printZeroAlways()
		.appendWeeks()
		//.printZeroAlways()
		.appendDays()
		.appendSuffix (" dia "," dias ")
		.toFormatter();

	    String data_em_dia_mes_ano = pf.print(per);

		return data_em_dia_mes_ano;

 }

 public int devolverAno(String data){

  int ano = Integer.parseInt(data.substring(6,10));

  return ano;

 }

 public int devolverMes(String data){

  int mes = Integer.parseInt(data.substring(3,5));

  return mes;

 }

 public int devolverDia(String data){

  int dia = Integer.parseInt(data.substring(0,2));

  return dia;

 }

 public static void main(String [] args){

 String data_inicio = "10/10/2000";
 String data_fim = "31/07/2012";

 String data_inicio1 = "13/03/1983";
 String data_fim1 = "30/07/2012";

 TesteIdade idade = new TesteIdade();

 System.out.println("Idade:" +idade.diferenca_entre_datas(data_inicio, data_fim));
 System.out.println("Idade:" +idade.diferenca_entre_datas(data_inicio1, data_fim1));

}

}

O teste realizado com as datas inicio 10/10/2000 e fim 31/07/2012 apresentam o seguinte resultado: Idade:11 anos 9 meses 30 dias, mas 30 dias está incorreto, deveria mostrar 21.
O teste realizado com as datas inicio 13/03/1983 e fim 30/07/2012 apresentam o seguinte resultado: Idade:29 anos 4 meses 23 dias, 23 dias também está incorreto.

Ao postar códigos no forum, favor postar entre as tags [code]

para que ele seja mais facilmente lido

Ok. Editado.

Tão importante quanto testar um programa é corrigir o seu teste. Além disso, contas com dias, meses e anos são surpreendentemente complexas. (Uma vez alguém tinha postado aqui no GUJ como é que se poderia calcular quanto resta para cumprir de uma determinada pena, e isso acabou ficando um pouco sem solução porque não batia com uma tabela que existe publicada em algum lugar).

Por exemplo, 31/07/2012 - 10/10/2000 dá 4312 dias.

Usando a convenção comercial (1 ano = 360 dias, 1 mês = 30 dias) isso dá 11 anos + 352 dias = 11 anos + 11 meses + 22 dias, obviamente não é o que você quer.
Vamos agora ver como é que se poderia fazer essa conta “no braço”.

10/10/2000 -> 10/10/2011 dá 11 anos…
10/10/2011 -> 10/07/2012 dá 9 meses…
10/07/2012 -> 31/07/2012 dá 21 dias.

Essa é a forma de se calcular o resultado do jeito que você quer. Mas veja que é um pouco complicado chegar a esse ponto (você precisa considerar um monte de casos, na verdade).

O outro caso: 13/03/1983 -> 30/07/2012
13/03/1983 -> 13/03/2012 -> 29 anos
13/03/2012 -> 13/07/2012 -> 4 meses
13/07/2012 -> 30/07/2012 -> 17 dias.

Hum… mas como é que o Joda-Time faz as contas então? Ele deve estar usando uma outra definição para a diferença entre datas (o tal do “Period”). Eu teria de analisar o código do Joda-Time para ver que tipo de mágica ele está fazendo. (Comecei a chutar um monte de coisas aqui numa planilha , mas não consegui chegar a esse resultado :frowning: )

OK entanglement acredito que essa diferença possa ser um problema no calendário ou definição das datas conforme você citou. Continuo pesquisando sobre isso.

Você está considerando meses com 31 dias e anos bissextos?
Ao meu entender, como exemplo, entre 1 de julho e 1 de agosto, existem 31 dias e não 30.

No mais, estou começando a usar o joda-time também, então não sei realmente a fundo como ele faz esse cálculo, mas pesquise se não é por esse motivo a diferença que você está encontrando.

Calculos com datas é realmente dificil, mas neste caso em particular é simples

Entre 10/10/2000 e 31/07/2012 existe a diferença de :

31 - 10 = 21 dias
(12-10) + 7 = 2 + 7 = 9 meses ( 2 meses até ao final de 2000 e 7 meses a partir do principio de 2012)
2012 - 2000 - 1 = 11 anos (é menos um porque só em 10/10/2012 completaria 12 anos)

Portanto são 11 anos, 9 meses e 21 dias

Só é que preciso entender que estes calculos não correspondem ao tempo real que passou entre as datas. Estes calculos só se aplicam para calcular idade ou coisas que têm aniversário ( como juros e penas). Por exemplo, não entram aqui considerações de anos bissestos, por exemplo.

Se queremos calcular o tempo real então temos que usar um algoritmo mais sofisticado (que é o que o Calendar do java ou o joda time , implementam). Não adianta usar a API de data sem saber como fazer os calculos. A calculadora faz qualquer conta, mesmo as que não fazem sentido.

O mais simples é o seguinte código :


	SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy");

		Date inicio = format.parse("10/10/2000");
		Date fim= format.parse("31/07/2012");
		
		Calendar calendar = Calendar.getInstance(new Locale("pt", "BR"));
		
		calendar.setTime(inicio);
		
		int count =0;
		while (calendar.getTime().compareTo(fim) < 0){
			count++;
			calendar.add(Calendar.DATE, 1);
		}
		
		System.out.print(count);

Isto dá 4312 dias.
Este algoritmo considera o locale (pt_BR) e portanto os anos bissestos corretos.

Posso agora inferir quantos anos e quandos meses ? não. Para isso precisava de um algoritmo mais sofisticado que teste que o calendario mudou de mes e de ano.

	SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy");

		Date inicio = format.parse("10/10/2000");
		Date fim= format.parse("31/07/2012");
		
		Calendar calendar = Calendar.getInstance(new Locale("pt", "BR"));
		
		calendar.setTime(inicio);
		
		int dias =0;
		int mesCorrente = calendar.get(Calendar.MONTH);
		int anoCorrente = calendar.get(Calendar.YEAR);
		
		int meses = 0;
		int anos = 0;
		while (calendar.getTime().compareTo(fim) < 0){
			dias++;
			
			calendar.add(Calendar.DATE, 1);
			
			if (mesCorrente != calendar.get(Calendar.MONTH)){
				meses++;
				mesCorrente = calendar.get(Calendar.MONTH);
			}
			if (anoCorrente != calendar.get(Calendar.YEAR)){
				anos++;
				anoCorrente = calendar.get(Calendar.YEAR);
			}
		}
		
		System.out.println(dias);
		System.out.println(meses);
		System.out.println(anos);

que dá 4312 dias, 141 meses e 12 anos
Este algoritmo só faz o calculo bruto, teriamos que subtrair 1 no ano se a data de inicio ainda não passou e usar multiplos de 12 para os meses.

O ponto é que antes de programar o algortimo, é preciso saber como criar o algoritmo e para criar o algortimo é preciso entender bem os conceitos de trabalhar com datas. Datas não são numeros sequencias.