Calcular a diferença de dias entre duas datas é um problema bastante recorrente em aplicações. Por exemplo, se for criado um filtro com data de início e data de fim, mas a diferença entre as duas datas não pode ser maior que 30 dias, ou as duas datas tem que estar no mesmo mês. Outro exemplo é para quando for necessário saber quantos dias ou horas faltam para um determinado evento.
Até o Java 7, as APIs padrão da plataforma para manipulação de datas não possuíam métodos para a comparação direta de datas. Para fazer isso era necessário converter as datas para long, e com as datas neste formato fazer a comparação. Isso é um problema porque além de ser difícil de implementar, também é difícil de entender o que está acontecendo no código. E essa comparação só vai funcionar para intervalos fixos, como dia e horas, mas para meses e anos que podem ter diferentes intervalos serão ainda mais difíceis de implementar. No Java 8 foi criada uma nova API para facilitar estas tarefas.
Mas em projetos que não podem ser atualizados para a versão mais recente da plataforma, é possível utilizar o framework Joda Time, que é um projeto que tem como objetivo facilitar a manipulação de datas em Java. O Joda Time possui métodos para a comparação, formatação e criação de datas, além de ter diversos métodos para a representação e recuperação de informações sobre datas.
Atualmente o Joda time está na versão 2.5 que foi lançada em outubro de 2013. O framework foi a base para a nova API de datas criadas na versão 8 do Java.
Configuração do Projeto
Para configurar o projeto que utilizará o Joda Time basta incluir o .jar do framework no build-path do projeto. O jar do framework pode ser encontrado no site do projeto Joda Time. Também é possível configurar o projeto com o Maven, e para isso a Listagem 1 mostra como pode ser feito.
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.5</version>
</dependency>
Exemplos de utilização do Joda Time
Para a utilização de datas no JodaTime é utilizada a classe org.joda.time.DateTime, que complementa a classe java.util.Date da plataforma Java. A Listagem 2 mostra como utilizar essa classe e mostra também algumas funcionalidades interessantes da classe. Primeiro mostra como recuperar o nome do mês como String e depois mostra como recuperar qual o dia da semana, também em String da data escolhida. Por fim, mostra como pegar o ano da data criada. Existem diversas outras opções de como pegar os nomes em diferentes línguas e recuperar diversas outras informações sobre a data. A Figura 1 mostra a saída com a execução desses métodos.
DateTime dataFinal = new DateTime();
DateTime dataInicio = new DateTime(2011, 1, 1, 0, 0);
System.out.println("Mês ShortString: "
+ dataFinal.monthOfYear().getAsShortText());
System.out.println("Mês Italiano: "
+ dataFinal.monthOfYear().getAsShortText(Locale.ITALIAN));
System.out.println("Mês String: "
+ dataFinal.monthOfYear().getAsText());
System.out.println("Semana do Ano: "
+ dataFinal.getWeekOfWeekyear());
System.out.println("Dia da semana String: "
+ dataFinal.dayOfWeek().getAsText());
System.out.println("Dia da semana Francês: "
+ dataFinal.dayOfWeek().getAsText(Locale.FRENCH));
System.out.println("Dia da semana Italiano: "
+ dataFinal.dayOfWeek().getAsText(Locale.ITALIAN));
System.out.println("Ano: " + dataFinal.year().get());
System.out.println("Ano no seculo atual:" + dataFinal.yearOfCentury().get());

Outra funcionalidade muito útil do Joda Time é a comparação entre datas. É possível comparar duas datas de diversas maneiras, descobrindo quantos meses, anos, dias, horas, minutos existem entre duas datas. A Listagem 3 mostra um exemplo de como fazer essa comparação. Primeiro é feita a comparação com a classe Days: o método daysBetween compara a quantidade de dias entre uma data inicial e uma data final. O método yearsBetween da classe Years compara os anos de diferença entre duas datas. O método HoursBetween da classe Hours compara as horas de diferença entre as duas datas e o método monthsBetween da classe Month compara os meses de diferença entre as duas datas.
DateTime dataFinal = new DateTime();
DateTime dataInicio = new DateTime(2011, 1, 1, 0, 0);
Days d = Days.daysBetween(dataInicio, dataFinal);
System.out.println("Diferença dias:" + d.getDays());
Years y = Years.yearsBetween(dataInicio, dataFinal);
System.out.println("Diferença anos:" + y.getYears());
Hours h = Hours.hoursBetween(dataInicio, dataFinal);
System.out.println("Diferença horas:" +h.getHours());
Months m = Months.monthsBetween(dataInicio, dataFinal);
System.out.println("Diferença meses:" + m.getMonths());
A Figura 2 mostra a execução das classes e métodos para a comparação de datas do Joda Time.

O Joda time também tem uma funcionalidade para descobrir um intervalo e a duração entre duas datas. Para isso são utilizadas as classes org.joda.time.Interval e org.joda.time.Duration. Inicialmente descobrimos o intervalo entre uma data início e uma data fim, que é sempre representado em milissegundos. Com o intervalo criado, podemos criar um objeto do tipo Duration, que tem a duração entre as duas datas e essa duração pode ser recuperada em milissegundos, dias, horas e segundos. A Listagem 4 mostra como isso pode ser feito.
DateTime dataFinal = new DateTime();
DateTime dataInicio = new DateTime(2011, 1, 1, 0, 0);
Interval intervalo = new Interval(dataInicio, dataFinal);
System.out.println(intervalo);
Duration duracao = intervalo.toDuration();
System.out.println("Duração milisegundos:" + duracao.getMillis());
System.out.println("Duração dias:" + duracao.getStandardDays());
System.out.println("Duração horas:" + duracao.getStandardHours());
System.out.println("Duração segundos:" + duracao.getStandardSeconds());
A Figura 3 mostra a execução do código que cria o intervalo e a duração entre as variáveis dataFinal e DataInicio.

A Listagem 5 mostrar como recuperar o período entre duas datas. A diferença do período e a duração, é que a duração é a comparação total entre as datas e o resultado é retornado em milissegundos. Já o período é a comparação feita campo a campo, onde é comparado dia com dia, mês com mês, e assim por diante. Por exemplo, o período entre as datas 10/10 /2012 e 15/12/2014 é de 5 dias, 2 meses e 2 anos.
DateTime dataFinal = new DateTime();
DateTime dataInicio = new DateTime(2011, 1, 1, 0, 0);
Interval intervalo = new Interval(dataInicio, dataFinal);
System.out.println(intervalo);
Period period = intervalo.toPeriod();
System.out.println("Anos do periodo:" + period.getYears());
System.out.println("Meses do periodo:" + period.getMonths());
System.out.println("Dias do periodo:" + period.getDays());
System.out.println("Horas do periodo:" + period.getHours());
A Figura 4 mostra a execução do código que cria o período entre as variáveis dataFinal e DataInicio.

Outra funcionalidade do Joda Time é a formatação das datas, isso é bastante parecido com a formatação de datas com a classe SimpleDateFormat da plataforma Java. Para a formatação de datas com o Joda Time são utilizadas as classes org.joda.time.format.DateTimeFormatter e org.joda.time.format.DateTimeFormatter. Como a Listagem 6 mostra, é criado o formatador com padrão que a data deve ser exibida e para utilizar esse formato, o objeto da classe DateTimeFormatter deve ser passado como parâmetro para o método toString das datas.
DateTimeFormatter dtfPadrao = DateTimeFormat.forPattern("dd/MM/yyyy");
System.out.println("Data formatada Padrao: " + dataInicio.toString(dtfPadrao));
System.out.println("Data formatada Padrao: " + dataFinal.toString(dtfPadrao));
DateTimeFormatter dtfExtenso = DateTimeFormat.forPattern("dd 'de' MMMM 'de' yyyy");
System.out.println("Data formatada Extenso: " + dataInicio.toString(dtfExtenso));
System.out.println("Data formatada Extenso: " + dataFinal.toString(dtfExtenso));
A Figura 5 mostra a execução do código que formata as datas, primeiro com padrão “dd/MM/yyyy” e depois com o padrão “dd ‘de’ MMMM ‘de’ yyyy”.

Como mostra a Listagem 7, é possível fazer operações com as datas no Joda Time como, por exemplo, acrescentar ou remover dias, meses, anos, segundos, entre outros. Uma coisa importante do Joda Time é que assim como a classe String, os objetos do tipo DateTime são imutáveis. Isso quer dizer que, mesmo chamando algum método como, por exemplo, data.plusDays(1), o objeto que executou esse método não será alterado. O método criará outro objeto que terá o objeto inicial como base, mas os dados alterados pelo método executado.
DateTime data = new DateTime();
DateTime dataMaisDias = data.plusDays(100);
DateTime dataMaisAno = data.plusWeeks(10);
DateTime dataMaisSemanas = data.plusYears(5);
DateTime dataMaisHoras = data.plusHours(100);
DateTime dataMenosDias = data.minusDays(100);
System.out.println(dataMaisDias);
System.out.println(dataMaisAno);
System.out.println(dataMaisSemanas);
System.out.println(dataMaisHoras);
System.out.println(dataMenosDias);
A Figura 6 mostra a execução do código que realiza as operações nas datas, entre eles a de adicionar dias, meses ou a de remover dias.

Para mostrar como utilizar o Joda Time, a Listagem 8 mostra alguns exemplos práticos do uso. O primeiro método, chamado horasAnoNovo, recebe uma data e um ano qualquer como parâmetro. Com isso, é calculado quantas horas faltam para se chegar a virada de ano do ano passado como parâmetro para o método. O segundo método, chamado diasAnoNovo, faz a mesma coisa, mas retorna a quantidades de dias que faltam para o ano novo.
O terceiro método, chamado diaSemanaAniversario, recebe como parâmetro uma data qualquer, que representa o aniversário de alguém e retorna uma String com o dia da semana dessa data. O quarto método, chamado intervaloMaiorTrintaDias, compara a diferença de dias entre duas datas: se essa diferença for maior que 30 dias retorna true, caso seja menor retorna false.
public static int horasAnoNovo(DateTime data, int ano) {
DateTime dataFinal = new DateTime(ano, 1, 1, 0, 0);
return Hours.hoursBetween(data, dataFinal).getHours();
}
public static int diasAnoNovo(DateTime data, int ano) {
DateTime dataFinal = new DateTime(ano, 1, 1, 0, 0);
return Days.daysBetween(data, dataFinal).getDays();
}
public static String diaSemanaAniversario(DateTime data) {
return data.dayOfWeek().getAsText();
}
public static boolean intervaloMaiorTrintaDias(DateTime dataInicial,
DateTime dataFinal) {
Days dias = Days.daysBetween(dataInicial, dataFinal);
if (dias.getDays() > 30) {
return true;
}
return false;
}
A Figura 7 mostra a execução da chamada aos métodos criados: primeiro do método horasAnoNovo com os parâmetros 05/11/2014 e 2030; depois do método diasAnoNovo com os parâmetros 05/11/2014 e 2030; depois do método diaSemanaAniversario com o parâmetro 07/03/2030 e, por último, do método intervaloMaiorTrintaDias om os parâmetros 05/11/2014 e 10/10/2015.

Para facilitar a compreensão, a Listagem 9 mostra o código completo do exemplo desenvolvido para esse artigo.
package teste;
import java.util.Locale;
import org.joda.time.DateTime;
import org.joda.time.Days;
import org.joda.time.Duration;
import org.joda.time.Hours;
import org.joda.time.Interval;
import org.joda.time.Months;
import org.joda.time.Period;
import org.joda.time.Years;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
public class Main {
public static void main(String[] args) {
DateTime dataFinal = new DateTime();
DateTime dataInicio = new DateTime(2011, 1, 1, 0, 0);
System.out.println("Mês ShortString: "
+ dataFinal.monthOfYear().getAsShortText());
System.out.println("Mês Italiano: "
+ dataFinal.monthOfYear().getAsShortText(Locale.ITALIAN));
System.out.println("Mês String: "
+ dataFinal.monthOfYear().getAsText());
System.out.println("Semana do Ano: "
+ dataFinal.getWeekOfWeekyear());
System.out.println("Dia da semana String: "
+ dataFinal.dayOfWeek().getAsText());
System.out.println("Dia da semana Francês: "
+ dataFinal.dayOfWeek().getAsText(Locale.FRENCH));
System.out.println("Dia da semana Italiano: "
+ dataFinal.dayOfWeek().getAsText(Locale.ITALIAN));
System.out.println("Ano: " + dataFinal.year().get());
System.out.println("Ano no seculo atual:"
+ dataFinal.yearOfCentury().get());
Days d = Days.daysBetween(dataInicio, dataFinal);
System.out.println("Diferença dias:" + d.getDays());
Years y = Years.yearsBetween(dataInicio, dataFinal);
System.out.println("Diferença anos:" + y.getYears());
Hours h = Hours.hoursBetween(dataInicio, dataFinal);
System.out.println("Diferença horas:" +h.getHours());
Months m = Months.monthsBetween(dataInicio, dataFinal);
System.out.println("Diferença meses:" + m.getMonths());
Interval intervalo = new Interval(dataInicio, dataFinal);
System.out.println(intervalo);
Duration duracao = intervalo.toDuration();
System.out.println("Duração milisegundos:" + duracao.getMillis());
System.out.println("Duração dias:" + duracao.getStandardDays());
System.out.println("Duração horas:" + duracao.getStandardHours());
System.out.println("Duração segundos:" + duracao.getStandardSeconds());
Period period = intervalo.toPeriod();
System.out.println("Anos do periodo:" + period.getYears());
System.out.println("Meses do periodo:" + period.getMonths());
System.out.println("Dias do periodo:" + period.getDays());
System.out.println("Horas do periodo:" + period.getHours());
DateTime data = new DateTime();
DateTime dataMaisDias = data.plusDays(100);
DateTime dataMaisAno = data.plusWeeks(10);
DateTime dataMaisSemanas = data.plusYears(5);
DateTime dataMaisHoras = data.plusHours(100);
DateTime dataMenosDias = data.minusDays(100);
System.out.println(dataMaisDias);
System.out.println(dataMaisAno);
System.out.println(dataMaisSemanas);
System.out.println(dataMaisHoras);
System.out.println(dataMenosDias);
DateTimeFormatter dtfPadrao = DateTimeFormat
.forPattern("dd/MM/yyyy");
System.out.println("Data formatada Padrao: "
+ dataInicio.toString(dtfPadrao));
System.out.println("Data formatada Padrao: "
+ dataFinal.toString(dtfPadrao));
DateTimeFormatter dtfExtenso = DateTimeFormat
.forPattern("dd 'de' MMMM 'de' yyyy");
System.out.println("Data formatada Extenso: "
+ dataInicio.toString(dtfExtenso));
System.out.println("Data formatada Extenso: "
+ dataFinal.toString(dtfExtenso));
System.out.println("Horas que faltam ano novo: "
+ horasAnoNovo(new DateTime(), 2030));
System.out.println("Dias que faltam ano novo: "
+ diasAnoNovo(new DateTime(), 2030));
System.out.println("O seu aniversário cai em uma: "
+ diaSemanaAniversario(new DateTime(2030, 3, 7, 0, 0)));
System.out.println("O intervalo tem mais que 30 dias?"
+ intervaloMaiorTrintaDias(dataInicio, dataFinal));
}
public static int horasAnoNovo(DateTime data, int ano) {
DateTime dataFinal = new DateTime(ano, 1, 1, 0, 0);
return Hours.hoursBetween(data, dataFinal).getHours();
}
public static int diasAnoNovo(DateTime data, int ano) {
DateTime dataFinal = new DateTime(ano, 1, 1, 0, 0);
return Days.daysBetween(data, dataFinal).getDays();
}
public static String diaSemanaAniversario(DateTime data) {
return data.dayOfWeek().getAsText();
}
public static boolean intervaloMaiorTrintaDias(DateTime
dataInicial, DateTime dataFinal) {
Days dias = Days.daysBetween(dataInicial, dataFinal);
if (dias.getDays() > 30) {
return true;
}
return false;
}
package teste;
}
A Figura 8 mostra a saída da execução completa do código criado para o exemplo. A aplicação foi desenvolvida como uma aplicação simples de console, mas o Joda Time pode ser utilizado em qualquer tipo de projeto Java, como um projeto Web ou um projeto EJB.

O Joda Time é um framework para a manipulação de datas em Java, como até a versão 7 do Java a API nativa para manipulação de datas era bastante limitada, esse framework é bastante útil.
No Java 8, foi implementada uma nova API de datas, que foi totalmente baseada no Joda Time. Em projetos que já utilizam o Java 8, talvez o Joda Time não seja necessário, mas ainda existem muitos projetos que não podem ser atualizados para a versão mais nova do Java, nesses projetos, o Joda Time ainda pode ser bastante útil.