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>
Listagem 1. Inclusão da dependência do Joda Time no projeto com o Maven

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());
Listagem 2. Classe DateTime do JodaTime
execução dos métodos do joda time
Figura 1. Execução dos métodos do Joda Time

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());
Listagem 3. Diversas comparações entre duas datas

A Figura 2 mostra a execução das classes e métodos para a comparação de datas do Joda Time.

execução dos métodos de comparação datas do joda time
Figura 2. Execução dos métodos de 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());
Listagem 4. Intervalo e duração entre duas datas

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.

intervalo e duração entre as variáveis datainicio datafinal
Figura 3. Intervalo e duração entre as variáveis dataInicio e DataFinal

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());
Listagem 5. Período entre duas datas

A Figura 4 mostra a execução do código que cria o período entre as variáveis dataFinal e DataInicio.

período entre as variáveis datainicio e datafinal
Figura 4. Período entre as variáveis dataInicio e DataFinal

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));
Listagem 6. Formatação de datas com o Joda Time

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”.

formatação das datas com o joda time
Figura 5. Formatação das datas com o Joda Time

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);
Listagem 7. Operações em datas

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.

operações em datas no joda time
Figura 6. Operações em datas no Joda Time

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;
}
Listagem 8. Exemplos de uso do Joda Time

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.

execução dos métodos de exemplo
Figura 7. Execução dos métodos de exemplo

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;
}
Listagem 9. Código completo do exemplo desenvolvido no artigo

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.

saída da execução aplicação inteira
Figura 8. Saída da execução da aplicação inteira

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.