terça-feira, 16 de março de 2010

Novos Tempos em Java SE 7

Em se tratando de Java, trabalhar com datas - e tempo de uma forma geral - sempre foi bastante problemático. Uma série de problemas de design faz com que o uso da API atual seja complexo e sujeito a bugs. JSR-310 to the rescue! A nova API de data e hora, ainda em desenvolvimento e a ser integrada na versão 7 da plataforma Java SE, promete finalmente por fim a esta dor de cabeça que 11 em cada 10 desenvolvedores Java certamente já tiveram de suportar.

Problemas com a API Atual

Todos concordam que a API atual de data e hora da plataforma Java, exposta principalmente através das classes java.util.Date e java.util.Calendar, apresenta uma série de inconveniências. Entre elas:
  • Ausência de classes que representem conceitos bastante comuns como data sem hora, hora sem data, ou mesmo um período ou intervalo. A ausência destas classes faz com que o desenvolvedor acabe usando a API de uma forma diferente daquela para a qual ela foi desenhada.
  • Os objetos não são imutáveis, requerendo sincronização externa em ambientes multi-thread.
  • As classes limitam-se a representar a data como um número incremental a partir de um momento inicial (epoch). Além de ser pouco intuitiva, esta abordagem dificulta a manipulação dos objetos de forma adequada.
Estes são alguns problemas que a JSR-310 se propõe a resolver.

Características da JSR-310

As principais características da JSR-310 são:
  • Baseada na ISO-8601 - padrão internacional para representação de datas e horas.
  • Thread-safe - Seus principais objetos são imutáveis.
  • Mais coesão e legibilidade- As classes e seus métodos têm responsabilidades melhor definidas e os nomes representam estas responsabilidades de maneira mais clara.
  • Mais Extensível - A API fornece uma série de "pontos de extensão" através dos quais - fazendo uso principalmente do desing patern Strategy - É possível customizar e estender o comportamento da API, facilitando certas tarefas do cotidiano.
  • Duas escalas distintas: Máquina (representa a passagem do tempo usando um único número incremental) e Humana (representa a passagem do tempo através do valor de um certo número de campos, tais como ano, mês, hora, etc.)

 Conceitos e principais classes

A JSR-310 foi construída em torno de alguns conceitos básicos, que refletem as principais classes da API. São eles:
Instantes
Instantes representam um momento específico na linha do tempo, com precisão de nanosegundos. Um exemplo de um instante seria "3 de Junho de 1983 as 12:03:00.0 UTC". Alternativamente, um instante pode ser definido como um deslocamento, em nanosegundos, de um momento inicial padrão - o epoch (que continua sendo meia noite de 1 de janeiro de 1970).

Existem diversas classes que representam um instante na API JSR-310: Instant, OffSetDateTime e ZonedDateTime.

Instant é uma das classes mais básicas da API. Usa a escala de máquina. Deve ser vista como a substituta da java.util.Date. Possui métodos de comparação com outros Instant e adição e subtração de certa duração (lembrando que a classe é imutável)

Instant agora = Clock.systemDefaultZone().instant();

Instant umMinutoAMais = agora.plusSeconds(60); //retorna um novo Instant!

Boolean test = agora.isAfter(umMinutoAMais); //false 

OffSetDateTime representa um dia, momento do dia e um deslocamento do UTC (coordinated universal time). Usa, portanto, escala humana.

OffsetDateTime hoje= Clock.systemDefaultZone().offsetDateTime();

OffsetDateTime aniversario= OffsetDateTime.of(2009, MonthOfYear.NOVEMBER, 24,19,20,15,ZoneOffset.hours(-3));

System.out.println(aniversario); //2009-11-24T19:20:15-03:00


ZonedDateTime é similar a OffSetDateTime, incorporando o ID de uma zona, como America/New_York. Esta informação é importante na medida em que o deslocamento do UTC pode variar ao longo do ano de acordo com a região (no horário de verão, por exemplo). Quando capturar estas variações é importante para o negócio, uma ZonedDateTime deve ser utilizada.

TimeZone timeZone = TimeZone.of("Europe/Paris");
ZonedDateTime agora= Clock.system(timeZone).zonedDateTime();
System.out.println(agora); //hora atual no time zone de paris

Parciais
Parciais são representações de data/hora insuficientes para especificar determinado momento na linha do tempo. 25 de Dezembro ou 12:15 são exemplos de parciais. Como não determinam um instante específico, não podem ser representados através de um deslocamento em nanosegundos do epoch. Usam, portanto, exclusivamente a escala humana.

Alguns exemplos de parciais na API: MonthDay (que representa um dia em um mês), LocalTime (hora sem data!)  e LocalDate (Data sem hora!)

MonthDay pascoa = MonthDay.of(MonthOfYear.APRIL, 4);

  LocalDate hoje = Clock.system(TimeZone.UTC).today();
  Year ano = hoje.toYear();
  boolean bissexto = ano.isLeap(); //Determina se o ano atual é bissexto.
  
  LocalDateTime aniversario = LocalDateTime.of(1983, MonthOfYear.JUNE, 3, 12, 00);
  
  System.out.println(aniversario);
  
  LocalDateTime umMesDepois = aniversario.plusMonths(1);
  
  System.out.println(umMesDepois);
  
  System.out.println(aniversario.isBefore(umMesDepois)); //true 

Durações
Uma duração representa certa quantidade de tempo, com precisão em nanosegundos. Bastante similar ao conceito de período, difere deste por representar uma quantidade determinada de instantes. Na API, a principal classe que representa uma duração é a Duration.

long segundos = 60L;
  
 Duration duration = Duration.seconds(segundos); //cria uma duração de 1 minuto
 
 Instant instante1 = Clock.system(TimeZone.UTC).instant(); //instante atual
 
 Instant instante2 = instante1.plus(duration); //Novo instante criado a parir do instante 1 + duração.
 
 Duration duration2 = Duration.durationBetween(instante1, instante2); // duração criada a partir de 2 instantes.
 
 boolean iguais = duration.equals(duration2);//true
 
 System.out.println(iguais);



Períodos
Assim como as durações, períodos representam certa quantidade de tempo. São representados, no entanto, através de um determinado número de campos (ano, mês, hora). Na API, períodos são representados principalmente pela classe Period.

Period thePeriod = Period.years(8); //cria um período de oito anos
  
  LocalDate data = Clock.system(TimeZone.UTC).today();  
  
  LocalDate daquiAOitoAnos =  data.plus(thePeriod); //adiciona o período a data atual.
  
  System.out.println(daquiAOitoAnos);


Customizando comportamento

Como foi mencionado, a JSR-310 oferece alguns pontos de extensão, de forma que é possível customizar seu comportamento e implementar algumas funcionalidade de forma mais fácil e direta. Alguns destes pontos de extensão:

  • Adjusters: Usados para "ajustar" datas, retornando outra data com certa característica desejada (um dia ou hora específicos, por exemplo). A API fornece alguns Adjusters built-in, para ajustar para o último dia do mês, por exemplo.
  • Resolvers: Indicam como tratar uma data inválida (se alguma operação resolver para o dia 29 de fevereiro de um ano que não seja bissexto, por exemplo). Neste caso a API também possui implementações padrão, sendo a mais comum a que resolve para a próxima data válida.
  • Matchers: Realizam consultas booleanas e datas e horas, para saber se a data possui determinada característica (um dia ou hora específicos, por exemplo). Como nos outros casos, implementações padrões são fornecidas, para determinar, por exemplo, se a data pertence a um determinado ano.

Conclusão

Este post discorreu sobre as principais características da JSR-310, a nova API Java para data e hora que deverá ser entregue juntamente com a versão 7 da plataforma SE. Esta API apresenta significantes melhorias em relação a API atual, como foi possível observar ao longo do post.

Referências


http://wiki.java.net/bin/view/Projects/DateTimeEDR1 - Casa da JSR-310 na java.net. Aqui é possível encontrar a referência, um user guide, java docs e uma implementação de referência. Vale salientar que a especificação ainda está em desenvolvimento!


Revista Java Magazine ano VII edição 69 - Excelente artigo sobre a JSR-310

Nenhum comentário:

Postar um comentário