Trabalhando com Joda-Time

Veja nesse artigo como trabalhar com a biblioteca Joda-Time para resolver problemas de data/hora bastante comuns na API nativa da linguagem Java.

A API padrão de manipulação de data/hora do Java sempre foi alvo de críticas por parte da comunidade. Muitos a consideram complicada e até mesmo deselegante. Tarefas simples, como calcular dias entre datas não estão disponíveis na API e devem ser calculadas "no braço". Isso contraria o movimento atual do desenvolvimento de software, que a cada dia privilegia linguagens e frameworks menos "verbosos", que economizam digitação e favorecem a produtividade e expressividade.

Visando atacar as deficiências da API nativa, foi criado a biblioteca Joda-Time, que oferece uma solução de alto nível para manipular data/hora. Alguns pontos a destacar do Joda-Time:

Veremos como utilizar alguns dos recursos do Joda-Time para resolveremos problemas comuns do dia-a-dia. Nos exemplos abaixo, utilizou-se a versão 2.1 do Joda Time, que pode ser obtida nesse link.

Classe DateTime

A classe DateTime é a alternativa à classe Calendar. As classes Calendar e Date representam instantes no tempo. Um instante pode ser considerado a quantidade de milisegundos a partir da data de referência: 1970-01-01T00:00Z (GMT).

import java.util.Calendar; import java.util.GregorianCalendar; import java.util.TimeZone; public class InstanteTest { public static void main(String... args) { // Configurando a data referência TimeZone timeZone = TimeZone.getTimeZone("GMT"); Calendar calendar = new GregorianCalendar(1970, 00, 01, 00, 00, 00); calendar.setTimeZone(timeZone); // Deve ser 0 System.out.println("Diferenca em milisegundos = " + calendar.getTimeInMillis()); // Configurando uma hora de diferença calendar.set(Calendar.HOUR_OF_DAY, 1); // Deve ser de 1 hora = 3600000 milisegundos System.out.println("Diferenca em milisegundos = " + calendar.getTimeInMillis()); System.out.println("Diferenca em horas = " + calendar.getTimeInMillis()/1000/60/60); } }
Listagem 1. Compreendendo a data de referência
Diferenca em Milisegundos = 0 Diferenca em Milisegundos = 3600000 Diferenca em Horas = 1
Listagem 2. Resultado do código da listagem 1

Isso demonstra que o método getTimeInMillis() retorna sempre a diferença em milissegundos da data configurada no Calendar e a data referência.

Como DateTime utiliza a mesma data de referência, ela é facilmente intercambiável com a API Padrão do Java. Além disso, ela é imutável e provê várias facilidades que suas co-irmãs não possuem.

import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.joda.time.Hours; public class InstanteJodaTimeTest { public static void main(String... args) { // Configurando a data referência DateTimeZone timeZone = DateTimeZone.forID("GMT"); DateTime dateTime1 = new DateTime(1970, 01, 01, 00, 00, 00, timeZone); // Deve ser 0 System.out.println("Diferenca em milisegundos = " + dateTime1.getMillis()); // Configurando uma hora de diferença DateTime dateTime2 = dateTime1.plusHours(1); // Deve ser de 1 hora = 3600000 milisegundos System.out.println("Diferenca em milisegundos = " + dateTime2.getMillis()); System.out.println("Diferenca em horas = " + Hours.hoursBetween(dateTime1, dateTime2).getHours()); } }
Listagem 3. Versão utilizando Joda-Time
Diferenca em milisegundos = 0 Diferenca em milisegundos = 3600000 Diferenca em horas = 1
Listagem 4. Saída da versão utilizando Joda-Time

Existem algumas diferenças importantes entre os 2 códigos:

Formatação de data/hora com DateTime

A classe DateTime oferece diversos métodos para formatar data/hora e campos individuais, tornando o trabalho bem mais simples.

import org.joda.time.DateTime; public class FormatacaoTest { public static void main(String... args) { DateTime dateTime = new DateTime(1970, 01, 01, 00, 00, 00); // Imprimindo a data no formato YYYY-MM-dd System.out.println("dateTime.toString() = " + dateTime.toString("YYYY-MM-dd")); // Imprimindo a data no formato YYYY-MM-dd HH:mm:ss System.out.println("dateTime.toString() = " + dateTime.toString("YYYY-MM-dd HH:mm:ss")); // Imprimindo o mês: Janeiro System.out.println("dateTime.toString() = " + dateTime.monthOfYear().getAsText()); // Imprimindo o mês: Jan System.out.println("dateTime.toString() = " + dateTime.monthOfYear().getAsShortText()); // Imprimindo o mês em Inglês System.out.println("dateTime.toString() = " + dateTime.monthOfYear().getAsText(Locale.ENGLISH)); } }
Listagem 5. Explorando as opções de formatação do DateTime
dateTime.toString() = 1970-01-01 dateTime.toString() = 1970-01-01 00:00:00 dateTime.toString() = Janeiro dateTime.toString() = Jan dateTime.toString() = January
Listagem 6. Resultado das opções de formatação

Classes DateTimeFormatter e DateTimeFormat

Para realizar o parse e formatação de Strings para data/hora e vice-versa, temos as classes DateTimeFormatter (que efetua as operações de formatação e parse) e a DateTimeFormat (fábrica que cria objetos DateTimeFormatter a partir de padrões e estilos).

import org.joda.time.DateTime; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; import org.joda.time.format.ISODateTimeFormat; public class DateTimeFormatterTest { public static void main(String ...args) { DateTime dateTime = new DateTime(1970, 01, 01, 00, 00, 00); DateTimeFormatter fmt = DateTimeFormat.forPattern("dd-MM-YYYYY"); // Alternativa 1 System.out.println(fmt.print(dateTime)); // Alternativa 2 System.out.println(dateTime.toString(fmt)); // Efetuando parse da string no formato "dd-MM-YYYYY" dateTime = fmt.parseDateTime("21-12-2012"); System.out.println(dateTime.toString(fmt)); // Imprimindo no formato ISO8601 fmt = ISODateTimeFormat.dateTime(); System.out.println(fmt.print(dateTime)); } }
Listagem 7. Usando DateTimeFormatter
01-01-1970 01-01-1970 21-12-2012 2012-12-21T00:00:00.000-02:00
Listagem 8. Resultado do código usando DateTimeFormatter

A formatação/parse com essas classes é tão simples quando usar SimpleDateFormat, com a vantagem de ambas serem classes imutáveis e o SimpleDateFormat não.

Alias, a imutabilidade é usada intensamente na API JodaTime, o que permite o uso do method chaining, que é uma das formas de se implementar interface fluente.

import org.joda.time.DateTime; import org.joda.time.format.DateTimeFormatter; import org.joda.time.format.DateTimeFormatterBuilder; public class MethodChaining { public static void main(String... args) { DateTime dateTime = new DateTime(); // Configurando o ano para 2010 System.out.println(dateTime.withYear(2010)); // Somando 20 dias System.out.println(dateTime.withYear(2010).plusDays(20)); // Verificando se o ano é bissexto System.out.println(dateTime.withYear(2010).plusDays(20).year().isLeap()); DateTimeFormatter fmt = new DateTimeFormatterBuilder(). appendDayOfMonth(2). // 2 Digito (Valor mínimo) - Preenche com 0 se for menor que 10 appendLiteral('-'). // Separador appendMonthOfYearText(). // Mes como Texto appendLiteral('-'). // Separador appendYear(2, 4). // Numero minimo para impressao (2) | Numero maximo para parse (4) toFormatter(); // Imprime DD-Mes Por Extenso-Ano System.out.println(fmt.print(dateTime)); } }
Listagem 9. Demonstrando encadeamento de métodos
2010-12-02T00:19:46.984-02:00 2010-12-22T00:19:46.984-02:00 false 02-Dezembro-2012
Listagem 10. Resultado do encadeamento de métodos

A biblioteca JodaTime oferece diversos outros recursos para se trabalhar com data/hora no Java. Ela também pode ser utilizada em conjunto com Date/Calendar, visto que a classe DateTime aceita em seu construtor objetos dessas 2 classes, e também possui métodos que retornam tanto Date (toDate()), como Calendar (toCalendar(), toGregorianCalendar()).

Apenas arranhamos o universo de classes da API Joda Time. Espero que esse artigo contribua para a compreensão dessa importante API (O Java 8 poderá ter uma nova API de data/hora inspirada no JodaTime. ref: InfoQ).

Referências:

Ebook exclusivo
Dê um upgrade no início da sua jornada. Crie sua conta grátis e baixe o e-book

Artigos relacionados