Back-End

27 set, 2016

Java 8: um novo tempo

Publicidade

Manipular datas nunca foi a coisa mais gostosa de fazer quando fala-se de Java antes do (super) java.time, pra não dizer o quanto é traumático na vida de um aspirante a programador. Era necessário o uso da classe Date, criada no Java 1.0, que não era tão boa para trabalhar e também não ajudava a lidar com internacionalização – os métodos não são claros e atualmente a maior parte deles estão depreciados, por conta de sua ineficiência e complexidade.

Para tornar a vida menos traumática, foi criada a classe abstrata no Java 1.1 chamada Calendar, com mais recursos que a classe Date, porém ainda questionável  mesmo com mais recursos, era difícil utilizá-la por ter métodos confusos e complicados de utilizar.

Visando a melhorar essas ineficiências, foi criada a biblioteca Joda-Time, base para o Java.Time, com mais performance do que o Calendar, com métodos mais simples e com característica de métodos imutáveis (o que será explicado mais abaixo). Desde então, seus métodos tornaram-se mais claros.

Inicialmente, para obter uma data, bastava instanciar um DateTime, que é uma classe imutável que substitui o uso do Calendar. Essa classe possui inúmeros construtores, um deles recebe um Object, o que dá diversas possibilidades de argumentos, inclusive entender que deseja-se converter o Date (JDK) em DateTime, veja:

java.util.Date juDate = new Date();
DateTime dt = new DateTime(juDate);

Quando consegue-se capturar o Object, seus métodos já podem ser utilizados.

java.util.Date juDate = new Date();
DateTime dt = new DateTime(juDate);

int month = dt.getMonthOfYear();
int year = dt.getYear();

Desde 2007, o Date and Time API com o pacote java.time, nova API de datas disponível para ser utilizada com o Java 8, baseada no Joda-Time, Java.time foi um alívio para muitos, ainda mais porque agora, pode-se começar o mês de janeiro com 1 e não 0! Outra novidade é que no novo pacote há várias classes com objetos que armazenam somente horas, somente datas, ou ambos! Isso é esplêndido!

Mas e aí? Como pegar isso tudo?

O java.time teve melhorias em vários detalhes. Uma das principais, e que dá mais identidade à API, é o conceito de separação, de como dados temporais são interpretados e divididos em duas categorias – dados para: computadores e humanos.

Dados temporais para computadores

Para computadores, o tempo é uma variável em constante crescimento. É um Timestamp, uma sequência de datas que determina quando “tal” evento aconteceu, com absoluta precisão. No Date, era utilizado um long que demonstrava os milissegundos a partir de 01/01/1970 às 00:00:00. Com a nova API, utiliza-se a classe Instant para sua representação, atualmente também com nanossegundos.

Instant agora = Instant.now();
System.out.println(agora);

Datas temporais para humanos

Quando se fala de datas para humanos, há uma divisão no tempo – trimestres, semanas, segundos, e muito mais… Ainda existe a mudança de fusos horários, calendários diferentes com a internacionalização e também horário de verão. As classes do pacote java.time permitem que a manipulação com diferentes interpretações de tempo seja possível e precisa, totalmente diferente de quando só se podia usar Date ou Calendar.

Data – LocalDate

O LocalDate é uma das classes que conseguem interpretar algumas das diferentes concepções de tempo – a de dia, mês e ano. Para um objeto que armazena apenas a data seja criado, pode-se utilizar a classe LocalDate, que possui o método estático now, que responde a data do sistema operacional, veja:

LocalDate dataAtual = LocalDate.now();
System.out.println("Data Atual:" + dataAtual);

A data será apresentada no formato aaaa-mm-dd.

Além disso, agora as datas são imutáveis, ou seja, não há possibilidade de modificação após a criação delas; ao adicionar dias (com o método plusDays(int)), por exemplo, cria-se um novo objeto (isso quando está sendo trabalhado com uma String).

LocalDate data = LocalDate.of(2016,08,20);
data = data.plusDays(9);
System.out.println("Nove dias adicionados a data atual: "+data);

Hora – LocalTime

Para a hora ser manipulada, é utilizada a classe LocalTime, bem parecida com a LocalDate, podendo também ser criada através do método estático now(), retornando a hora atual do sistema ou por parâmetros com o método of(int hora, int minuto, int segundo).

LocalTime time = LocalTime.now();
System.out.println("Hora atual: "+time);

LocalTime timeDiferente = LocalTime.of(13 , 25 ,18);
System.out.println("Hora com parâmetros: "+timeDiferente);

Da mesma forma que o LocalDate, o “tempo” pode ser adicionado ou subtraído; por exemplo, caso queira adicionar tempo, pode ser feito o seguinte:

LocalTime time = LocalTime.of(13 , 25 ,18);
timeOutro = time.plusHours(1).plusMinutes(1).plusSeconds(1);
System.out.println(time);

Caso queira subtrair:

LocalTime time = LocalTime.of(13 , 25 ,18);
timeOutro = time.minusHours(-1).minusMinutes(-1).minusSeconds(-1);
System.out.println(time);

Hora e Data – LocalDateTime

Para tudo ser manipulado junto e misturado com hora e data, existe a classe LocalDateTime. Como as outras classes, pode-se utilizar data/hora atual do sistema com o now(), ou também passar parâmetros através do método of(int ano, int mes, int dia, int hora, int minuto, int segundo), além do método of(LocalDate, LocalTime), que recebe como parâmetros objetos LocalDate, LocalTime.

LocalDateTime agora = LocalDateTime.now();
System.out.println("Agora: "+agora);

LocalDateTime dateTime = LocalDateTime.of( 2016, 08, 20, 21, 45, 0 );
System.out.println("Date e Hora com parâmetro: "+dateTime);

LocalDate data = LocalDate.of(2016, 08, 20);
LocalTime time = LocalTime.of( 21, 15, 40);
LocalDateTime dateTime2 = LocalDateTime.of( data , time);

System.out.println("Date e Hora: "+dateTime2);

Lidando com Fuso/Internacionalização

Para que uma data e hora possam ser manipuladas com um fuso horário específico, deve ser utilizada a classe ZonedDateTime.

ZoneId fusoHorarioDeSaoPaulo = ZoneId.of("America/Sao_Paulo");
ZonedDateTime agoraEmSaoPaulo = ZonedDateTime.now(fusoHorarioDeSaoPaulo);
System.out.println(agoraEmSaoPaulo);

Outra questão é quando trata-se de horário de verão. Por exemplo, ao final dessa época, tem-se a mesma hora duas vezes na mesma noite; nesses casos, o que pode ser feito é o seguinte:

ZoneId fusoHorarioDeSaoPaulo = ZoneId.of("America/Sao_Paulo");
 
LocalDateTime fimDoHorarioDeVerao2013SemFusoHorario = 
LocalDateTime.of(2014, Month.FEBRUARY, 15, 23, 00);
 
ZonedDateTime fimDoHorarioVerao2013ComFusoHorario = 
fimDoHorarioDeVerao2013SemFusoHorario.atZone(fusoHorarioDeSaoPaulo);
System.out.println(fimDoHorarioVerao2013ComFusoHorario); 
 
ZonedDateTime maisUmaHora = 
fimDoHorarioVerao2013ComFusoHorario.plusHours(1);
System.out.println(maisUmaHora);

Formatação de datas

Esse tipo de formatação, para o padrão das classes da API que utiliza o formato ISO-8601, toString, ficou muito mais fácil, já que os objetos de LocalCate, LocalTime e LocalDateTime são o método format que recebe um outro objeto de DateTimeFormatter; ele pode ser criado com os métodos ofLocalizedDate(FormatStyle) e ofLocalizedDateTime(FormatStyle). O enum FormatStyle possui alguns formatos pré-definidos (FULL,LONG, MEDIUM, SHORT), que podem ser combinados com um Locale.

LocalDate hoje = LocalDate.now();
DateTimeFormatter formatador = 
DateTimeFormatter.ofPattern("dd/MM/yyyy");
hoje.format(formatador); 

LocalDateTime agora = LocalDateTime.now();
DateTimeFormatter formatador = DateTimeFormatter
  .ofLocalizedDateTime(FormatStyle.SHORT)
  .withLocale(new Locale("pt", "br"));
agora.format(formatador);

Referências