Como já vimos neste mesmo bat-blog trabalhar com datas no Java nunca foi uma coisa legal, muitos desenvolvedores utilizavam o projeto Joda Time para facilitar isso e ele foi tão bem aceito que se tornou a base para a API Date and Time API do JAVA 8.

Uma das características novas das datas é que agora elas são imutáveis, ou seja, elas não podem ser modificadas após sua criação, sempre que adicionarmos/subtraímos dias, meses, anos, minutos, estamos criando um novo objeto(como quando estamos trabalhando com uma String). Outra grande mudança é que agora os números dos meses não começam por 0, finalmente Janeiro sendo o mês 1 e não 0.

Temos um novo pacote(java.time) com várias classe para trabalhar com objetos que armazenam somente datas, apenas horas ou ambas simultaneamente.

Data – LocalDate

Para criar um objeto que representa somente a data existe a classe LocalDate, que possui um método estático now que retorna a data atual do sistema operacional.

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

Note que a a data já foi mostrada no formato aaaa-mm-dd.

Se for necessário criar uma data a partir de parâmetros, existe o método estático of( int ano , int mes , int dia) que retorna um objeto com os valores informados.

LocalDate dataNatal = LocalDate.of( 2014 , 12, 25 );
System.out.println("Natal: "+dataNatal);

Outra opção pode ser criar um objeto LocalDate com a data do sistema e alterá-la através dos métodos withYear(int) , withMonth(int) e withDayOf(int). Estes métodos retornam um novo objeto com a data alterada.

LocalDate data = LocalDate.now();
System.out.println("Data Inicial: "+data);
 
data = data.withYear(2015);
System.out.println("Data com ano modificado: "+data);
 
data = data.withMonth(1);
System.out.println("Data com mês modificado: "+data);
 
data = data.withDayOfMonth(15);
System.out.println("Data com dia modificado: "+data);

Lembrando que os objetos de data são imutáveis, então sempre que chamamos um método que modifica(na verdade cria um novo objeto) uma data devemos atribuir seu retorno novamente para a mesma variável, ou a “alteração será perdida”.

Uma tarefa que ficou muito simples(sem o uso de constantes) com a nova API foi adicionar dias, meses, anos a uma data, sendo feito através dos métodos plusDays(int), plusMonths(int), plusYears(int) para adicionar e minusDays(int), minusMonths(int), minusYears(int) para subtrair.

Lembrando que como cada método retorna um objeto podemos encadear as chamadas.

LocalDate dataEnc = LocalDate.of(2014,10,10).plusMonths(4).plusDays(7);
System.out.println("Data com 7 dias e 4 meses adicionada: "+dataEnc);

Se for necessário pegar o ano, mês ou dia separadamente do objeto de data podemos utilizar os seguintes métodos:

LocalDate d = LocalDate.now();
System.out.println("DIA: "+d.getDayOfMonth() );
System.out.println("MÊS: "+d.getMonthValue() );
System.out.println("ANO: "+d.getYear() );

Também é possível pegar o mês de forma textual através de getMonth() que retorna o Enum Month, mas ele retornará por padrão em inglês, o qual pode ser formatado através do método getDisplayName() que recebe um Enum TextStyle como primeiro parâmetro, que representa o mês no formato completo(TextStyle.FULL), abreviado(TextStyle.SHORT) ou a primeira letra(TextStyle.NARROW), e o segundo parâmetro é o Locale do idioma que pode ser utilizado o padrão do sistema através do método getDefalut(). A classe Locale também possui várias constantes representando outras línguas como Locale.ENGLISH, Locale.CHINA, Locale.FRANCH, Locale.ITALY.

//mostra o texto completo
System.out.println(d.getMonth().getDisplayName(TextStyle.FULL, Locale.getDefault() ) );
         
//mostra a abreviação
System.out.println(d.getMonth().getDisplayName(TextStyle.SHORT, Locale.getDefault() ) );
 
//mostra somente a primeira letra
System.out.println(d.getMonth().getDisplayName(TextStyle.NARROW, Locale.getDefault() ) );

Hora – LocalTime

Para manipularmos somente a hora existe a classe LocalTime que tem um funcionamento muito parecido com a LocalDate, podendo ser criado através do método estático now() que retorna 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 timeOutro = LocalTime.of(12 , 11 ,10);
System.out.println("Hora com parâmetros: "+timeOutro); 

Para alterar algum dos valores da hora pode-se utilizar os métodos withHour(int), withMinute(int), withSecond(int) e withNano(int).

LocalTime time = LocalTime.now().withHour(3).withMinute(4).withSecond(6).withNano(500_000_000);
System.out.println( time );

Adicionar e subtrair tempo também segue a mesma ideia, bastando chamar os métodos plusHours(int), plusMinutes(int), plusSeconds(int). Para subtrair um tempo existem os métodos minusHours(int), minusMinutes(int), minusSeconds(int).

LocalTime time = LocalTime.of(12 , 11 ,10);
timeOutro = time.plusHours(1).plusMinutes(1).plusSeconds(1);
System.out.println(time);

Para pegar os valores de hora, minuto, segundo e nanosegundo utilizamos os métodos getHour(), getMinute(), getMinute(), getSecond(), getNano().

System.out.println("HORA: "+time.getHour() );
System.out.println("MINUTO: "+time.getMinute() );
System.out.println("SEGUNDOS: "+time.getSecond() );
System.out.println("NANOSEGUNDO: "+time.getNano() );

Data e Hora, agora tudo junto – LocalDateTime

Com a classe LocalDateTime podemos manipular a data e a hora tudo junto, como as outras classes podemos pegar do sistema utilizando now() ou passando parâmetros através do método of(int ano, int mes, int dia , int hora , int minuto , int segundo). Também temos o método of( LocalDate, LocalTime) sobrecarregado recebendo como parâmetro objetos LocalDate e LocalTime.

LocalDateTime agora = LocalDateTime.now();
System.out.println("Agora: "+agora);
 
LocalDateTime dateTime = LocalDateTime.of( 2014 , 12 , 15 , 10 ,45 , 0 );
System.out.println("Date e Hora com parâmetro: "+dateTime);
 
LocalDate data = LocalDate.of(2014,11,12);
LocalTime time = LocalTime.of( 14 , 15 , 40);
LocalDateTime dateTime2 = LocalDateTime.of( data , time);
 
System.out.println("Date e Hora: "+dateTime2);

Lembrando que a classe LocalDateTime possui todos os métodos já vistos das classes LocalDate e LocalTime.

Se for necessário podemos retirar somente a LocalDate ou o LocalTime através dos métodos toLocalDate() e toLocalTime().

LocalDateTime agora = LocalDateTime.now();
 
LocalDate dataAgora = agora.toLocalDate();
LocalTime timeAgora = agora.toLocalTime();
 
System.out.println("Data Agora: "+dataAgora );
System.out.println("Time Agora: "+timeAgora );

Enums

Como comentado alguns parágrafos acima, os meses possuem um Enum para representá-los, o Month com os valores Month.JANUARY, Month.FEBRUARY, Month.MARCH, Month.APRIL, Month.MAY, Month.JUNE, Month.JULY, Month.AUGUST, Month.SEPTEMBER, Month.OCTOBER, Month.NOVEMBER, Month.DECEMBER que podem ser utilizados para a construção de datas ao invés dos seus valores numéricos

LocalDate comMes = LocalDate.of(2014 , Month.DECEMBER , 9);
System.out.println("Data com mes enum: "+comMes);

Um método interessante do Month é lenght que retorna no número de dias que um determinados mês tem.

Month m = Month.of(11);
System.out.println("Números de dias do mês"+m.length(true) );

Outro Enum muito útil é o de DayOfWeek que possui os valores DayOfWeek.FRIDAY, DayOfWeek.MONDAY, DayOfWeek.SATURDAY, DayOfWeek.SUNDAY, DayOfWeek.THURSDAY, DayOfWeek.TUESDAY, DayOfWeek.WEDNESDAY e pode ser obtido através do método getDayOfWeek() de um objeto de data.

LocalDate dayWeek = LocalDate.now();
System.out.println("Dia da semana: "+dayWeek.getDayOfWeek());
System.out.println("Dia da semana em PT: "+dayWeek.getDayOfWeek().getDisplayName(TextStyle.FULL, Locale.getDefault() ) );

Formatando datas

A formatação de datas ficou um pouco mais fácil, já que os objetos LocalDate, LocalTime e LocalDateTime possuem um método format(DateTimeFormatter) que recebe um objeto de DateTimeFormatter que pode ser criado com os métodos ofLocalizedDate(FormatStyle) e ofLocalizedDateTime(FormatStyle) que recebem como parâmetro o formato, que pode ser FULL, LONG, MEDIUM, SHORT.

LocalDateTime data = LocalDateTime.now();
System.out.println("Data FULL: "+ data.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL) ) );
System.out.println("Data LONG: "+ data.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG) ) );
System.out.println("Data MEDIUM: "+ data.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM) ) );
System.out.println("Data SHORT: "+ data.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT) ) );
 
System.out.println("Data Time MEDIUM: "+ data.format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM) ) );
System.out.println("Data Time SHORT: "+ data.format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT) ) );

Mas em alguns casos é necessário uma formatação mais personalizada, o que pode ser conseguido com o uso do método ofPattern(String) que recebe como parâmetro uma String com o formato.

LocalDateTime data = LocalDateTime.now();
DateTimeFormatter  fmt = DateTimeFormatter.ofPattern("dd/MM/yyyy hh:mm:ss");
System.out.println("DATA PATTERN: "+fmt.format(data) );

Na String de formato é aceito vários caracteres cada representando uma informação.

SímboloSignificadoExemplo
GeraAD; Anno Domini; A
uyear2004; 04
yyear-of-era2004; 04
Dday-of-year189
M/Lmonth-of-year7; 07; Jul; July; J
dday-of-month10
Q/qquarter-of-year3; 03; Q3; 3rd quarter
Yweek-based-year1996; 96
wweek-of-week-based-year27
Wweek-of-month4
Eday-of-weekTue; Tuesday; T
e/clocalized day-of-week2; 02; Tue; Tuesday; T
Fweek-of-month3
aam-pm-of-dayPM
hclock-hour-of-am-pm (1-12)12
Khour-of-am-pm (0-11)0
kclock-hour-of-am-pm (1-24)1
Hhour-of-day (0-23)0
mminute-of-hour30
ssecond-of-minute55
Sfraction-of-second978
Amilli-of-day1234
nnano-of-second987654321
Nnano-of-day1234000000
Vtime-zone IDAmerica/Los_Angeles; Z; -08:30
ztime-zone namePacific Standard Time; PST
Olocalized zone-offsetGMT+8; GMT+08:00; UTC-08:00;

Convertendo datas

É outro recurso que ficou melhor, agora para converter uma String em um objeto de data pode-se utilizar as próprias classes de data já que elas tem o método parse, que recebem como parâmetros a String com o texto da data e um objeto DateTimeFormatter com o formato de data em que a String está.

String stringData = "09/12/2014";
DateTimeFormatter fmt2 = DateTimeFormatter.ofPattern("dd/MM/yyyy");
LocalDate sData = LocalDate.parse( stringData , fmt2 );

Como será feita uma migração entre APIs é interessante saber como converter objetos Date para LocalDate e vice-versa.

Date ts = new Date();
Instant instant = ts.toInstant();
LocalDateTime dataEmLocalDateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
System.out.println("Date to LocalDateTime: "+dataEmLocalDateTime );

A classe Date armazena internamente o seu valor em um long que representa os milissegundos a partir de 01/01/1970 às 00:00:00. Na nova API este número é representando como a classe Instant e a classe Date agora possui o método toInstant(), ao qual podemos utilizar no método ofInstant de LocalDateTime como parâmetro para criar um objeto LocalDateTime junto com um objeto ZoneId do sistema.

LocalDateTime data = LocalDateTime.now();
Instant instant = data.atZone(ZoneId.systemDefault()).toInstant();
Date d = Date.from(instant);
System.out.println("Date: "+d);

Diferença entre datas

No java sempre foi complicado calcular a diferença entre datas, na nova API foram criadas classes para facilitar isso, no exemplo abaixo vamos ver como calcular a diferença com a classe Period utilizando seu método between.

LocalDate data1 = LocalDate.of(2013,1,1);
LocalDate data2 = LocalDate.now();
Period period = Period.between( data , data);
System.out.println("Diferença entre "+data1+" e "+data2);
System.out.println("Dias: "+period.getDays() );
System.out.println("Meses: "+period.getMonths() );
System.out.println("Anos: "+period.getYears() );

Um objeto Period possui os métodos getDays(), getMonths() e getYears() que retornam o o números de dias, meses e anos respectivamente que se passaram entre as duas datas. Esta classe não calcula horas, minutos e segundo, nem é capaz de nos fornecer a diferença de tempo somente em dias por exemplo para isso temos a classe Duration.

LocalDateTime data1 = LocalDateTime.of(2014,11,1 , 1,1,1);
LocalDateTime data2 = LocalDateTime.now();
 
Duration dur = Duration.between(data1 , data2);
 
System.out.println( dur.toDays() +" dias");
System.out.println( dur.toHours()+" horas");
System.out.println( dur.toMinutes()+" minutos");
System.out.println( dur.getSeconds() +" segundos");

Na classe Duration os métodos toDays() retorna a diferença de tempo em dias, se a diferença for de 2 meses, o método toDays() vai retornar 60, já na classe Duration o getDays() retornaria 0 e o método getMonths() retornaria 2.

Como sempre este post é somente uma visão geral, mais recursos podem ser visto nos links da documentação

  • http://docs.oracle.com/javase/8/docs/api/java/time/LocalDate.html
  • http://docs.oracle.com/javase/8/docs/api/java/time/LocalTime.html
  • http://docs.oracle.com/javase/8/docs/api/java/time/LocalDateTime.html
  • http://docs.oracle.com/javase/8/docs/api/java/util/Locale.html
  • https://docs.oracle.com/javase/8/docs/api/java/time/Month.html
  • https://docs.oracle.com/javase/8/docs/api/java/time/DayOfWeek.html
  • https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html
  • https://docs.oracle.com/javase/8/docs/api/java/time/Period.html