Java 8 Data – LocalDate, LocalDateTime, Instant

O API de Data e Hora é um dos maiores recursos do lançamento do Java 8. O Java estava sem uma abordagem consistente para Data e Hora desde o início e o API de Data e Hora do Java 8 é uma adição bem-vinda às APIs principais do Java.

Por que precisamos de um novo API de Data e Hora do Java?

Antes de começarmos a olhar para o API de Data e Hora do Java 8, vamos ver por que precisamos de um novo API para isso. Houve vários problemas com as classes existentes relacionadas a data e hora em java, alguns deles são:

  • As classes de Data e Hora do Java não são definidas de forma consistente, temos a classe Date tanto nos pacotes java.util quanto java.sql. Novamente, as classes de formatação e análise são definidas no pacote java.text.
  • A classe java.util.Date contém tanto valores de data quanto de hora, enquanto java.sql.Date contém apenas valores de data. Ter isso no pacote java.sql não faz sentido. Além disso, ambas as classes têm o mesmo nome, o que é um design muito ruim em si mesmo.
  • Não existem classes claramente definidas para tempo, carimbo de data e hora, formatação e análise. Temos a classe abstrata java.text.DateFormat para necessidades de análise e formatação. Geralmente, a classe SimpleDateFormat é usada para análise e formatação.
  • Todas as classes de Data são mutáveis, portanto, elas não são seguras para threads. Este é um dos maiores problemas com as classes de Data e Calendário do Java.
  • A classe Date não oferece internacionalização, nem suporte a fuso horário. Portanto, as classes java.util.Calendar e java.util.TimeZone foram introduzidas, mas elas também têm todos os problemas listados acima.

Há algumas outras questões com os métodos definidos nas classes Date e Calendar, mas os problemas acima deixam claro que uma API de Data e Hora robusta era necessária em Java. Por isso, o Joda Time teve um papel fundamental como uma substituição de qualidade para os requisitos de Data e Hora em Java.

Princípios de Design do Java 8 Date Time

A API de Data e Hora do Java 8 é uma implementação do JSR-310. Ela é projetada para superar todas as falhas nas implementações de data e hora legadas. Alguns dos princípios de design da nova API de Data e Hora são:

  1. Imutabilidade: Todas as classes na nova API de Data e Hora são imutáveis e adequadas para ambientes multithread.

  2. Separation of Concerns: A nova API separa claramente entre data e hora legíveis por humanos e tempo de máquina (timestamp Unix). Ela define classes separadas para Data, Hora, DateTime, Timestamp, Timezone, etc.

  3. Clareza: Os métodos são claramente definidos e realizam a mesma ação em todas as classes. Por exemplo, para obter a instância atual, temos o método now(). Existem métodos format() e parse() definidos em todas essas classes, em vez de ter uma classe separada para eles.

    Todas as classes utilizam o Padrão de Fábrica e o Padrão de Estratégia para um melhor manuseio. Uma vez que você tenha usado os métodos em uma das classes, trabalhar com outras classes não será difícil.

  4. Operações de utilidade: Todas as novas classes da API de Data e Hora vêm com métodos para realizar tarefas comuns, como adicionar, subtrair, formatar, analisar, obter a parte separada na data/hora, etc.

  5. Extensível: A nova API de Data e Hora funciona no sistema de calendário ISO-8601, mas podemos usá-la com outros calendários não ISO também.

Pacotes da API de Data e Hora

A API de Data e Hora do Java 8 consiste nos seguintes pacotes.

  1. java.time: Este é o pacote base da nova API de Data e Hora do Java. Todas as principais classes base fazem parte deste pacote, como LocalDate, LocalTime, LocalDateTime, Instant, Period, Duration, etc. Todas essas classes são imutáveis e thread-safe. Na maioria das vezes, essas classes serão suficientes para lidar com requisitos comuns.
  2. java.time.chrono: Este pacote define APIs genéricas para sistemas de calendário não ISO. Podemos estender a classe AbstractChronology para criar nosso próprio sistema de calendário.
  3. java.time.formato: Este pacote contém classes usadas para formatação e análise de objetos de data e hora. Na maioria das vezes, não as usaríamos diretamente porque as classes principais no pacote java.time fornecem métodos de formatação e análise.
  4. java.time.temporal: Este pacote contém objetos temporais e podemos usá-lo para descobrir as datas ou horas específicas relacionadas aos objetos de data/hora. Por exemplo, podemos usá-los para descobrir o primeiro ou último dia do mês. Você pode identificar facilmente esses métodos porque sempre têm o formato “withXXX”.
  5. Pacote java.time.zone: Este pacote contém classes para suportar diferentes fusos horários e suas regras.

Exemplos de Classes da API de Data e Hora do Java 8

Nós examinamos a maioria das partes importantes da API de Data e Hora do Java. Agora é hora de examinar as classes mais importantes da API de Data e Hora com exemplos.

1. LocalDate

LocalDate é uma classe imutável que representa uma Data com o formato padrão de yyyy-MM-dd. Podemos usar o método now() para obter a data atual. Também podemos fornecer argumentos de entrada para ano, mês e dia para criar uma instância de LocalDate.

Esta classe fornece um método sobrecarregado para now() onde podemos passar ZoneId para obter datas em um fuso horário específico. Esta classe fornece a mesma funcionalidade que java.sql.Date.

package com.journaldev.java8.time;

import java.time.LocalDate;
import java.time.Month;
import java.time.ZoneId;

/**
 * Exemplos de LocalDate
 * @author pankaj
 *
 */
public class LocalDateExample {

	public static void main(String[] args) {
		
		// Data Atual
		LocalDate today = LocalDate.now();
		System.out.println("Current Date="+today);
		
		// Criando LocalDate fornecendo argumentos de entrada
		LocalDate firstDay_2014 = LocalDate.of(2014, Month.JANUARY, 1);
		System.out.println("Specific Date="+firstDay_2014);
		
		
		// Tente criar uma data fornecendo entradas inválidas
		// LocalDate fev29_2014 = LocalDate.of(2014, Month.FEBRUARY, 29);
		// Exceção na thread "main" java.time.DateTimeException: 
		// Data inválida '29 de fevereiro' pois '2014' não é um ano bissexto
		
		// Data atual em "Asia/Kolkata", você pode obtê-la na documentação do ZoneId
		LocalDate todayKolkata = LocalDate.now(ZoneId.of("Asia/Kolkata"));
		System.out.println("Current Date in IST="+todayKolkata);

		// java.time.zone.ZoneRulesException: ID de fuso horário desconhecido: IST
		// LocalDate hojeIST = LocalDate.now(ZoneId.of("IST"));
		
		// Obtendo data da data base, ou seja, 01/01/1970
		LocalDate dateFromBase = LocalDate.ofEpochDay(365);
		System.out.println("365th day from base date= "+dateFromBase);
		
		LocalDate hundredDay2014 = LocalDate.ofYearDay(2014, 100);
		System.out.println("100th day of 2014="+hundredDay2014);
	}

}

Explicação dos métodos de LocalDate é fornecida nos comentários. Quando executamos este programa, obtemos a seguinte saída.

Current Date=2014-04-28
Specific Date=2014-01-01
Current Date in IST=2014-04-29
365th day from base date= 1971-01-01
100th day of 2014=2014-04-10

2. LocalTime

LocalTime é uma classe imutável cuja instância representa um horário no formato legível para humanos. Seu formato padrão é hh:mm:ss.zzz. Assim como LocalDate, esta classe fornece suporte para fuso horário e cria instância passando hora, minuto e segundo como argumentos de entrada.

package com.journaldev.java8.time;

import java.time.LocalTime;
import java.time.ZoneId;

/**
 * Exemplos de LocalTime
 * @author pankaj
 *
 */
public class LocalTimeExample {

	public static void main(String[] args) {
		
		//Horário Atual
		LocalTime time = LocalTime.now();
		System.out.println("Current Time="+time);
		
		//Criando LocalTime fornecendo argumentos de entrada
		LocalTime specificTime = LocalTime.of(12,20,25,40);
		System.out.println("Specific Time of Day="+specificTime);
		
		
		//Tente criar horário fornecendo entradas inválidas
		//LocalTime invalidTime = LocalTime.of(25,20);
		//Exceção em thread "main" java.time.DateTimeException: 
		//Valor inválido para HourOfDay (valores válidos 0 - 23): 25
		
		//Data atual em "Asia/Kolkata", você pode obtê-la na documentação do ZoneId
		LocalTime timeKolkata = LocalTime.now(ZoneId.of("Asia/Kolkata"));
		System.out.println("Current Time in IST="+timeKolkata);

		//java.time.zone.ZoneRulesException: Identificação de fuso horário desconhecida: IST
		//LocalTime hojeIST = LocalTime.now(ZoneId.of("IST"));
		
		//Obtendo data a partir da data base i.e 01/01/1970
		LocalTime specificSecondTime = LocalTime.ofSecondOfDay(10000);
		System.out.println("10000th second time= "+specificSecondTime);

	}

}

Saída:

Current Time=15:51:45.240
Specific Time of Day=12:20:25.000000040
Current Time in IST=04:21:45.276
10000th second time= 02:46:40

3. LocalDateTime

LocalDateTime é um objeto de data e hora imutável que representa uma data e hora com o formato padrão yyyy-MM-dd-HH-mm-ss.zzz. Ele fornece um método de fábrica que recebe argumentos de entrada LocalDate e LocalTime para criar uma instância de LocalDateTime.

package com.journaldev.java8.time;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;
import java.time.ZoneId;
import java.time.ZoneOffset;

public class LocalDateTimeExample {

	public static void main(String[] args) {
		
		//Data Atual
		LocalDateTime today = LocalDateTime.now();
		System.out.println("Current DateTime="+today);
		
		//Data Atual usando LocalDate e LocalTime
		today = LocalDateTime.of(LocalDate.now(), LocalTime.now());
		System.out.println("Current DateTime="+today);
		
		//Criando LocalDateTime fornecendo argumentos de entrada
		LocalDateTime specificDate = LocalDateTime.of(2014, Month.JANUARY, 1, 10, 10, 30);
		System.out.println("Specific Date="+specificDate);
		
		
		//Tente criar uma data fornecendo entradas inválidas
		//LocalDateTime fev29_2014 = LocalDateTime.of(2014, Month.FEBRUARY, 28, 25,1,1);
		//Exceção na thread "main" java.time.DateTimeException: 
		//Valor inválido para HoraDoDia (valores válidos 0 - 23): 25

		
		//Data atual em "Asia/Kolkata", você pode obtê-la a partir da documentação do ZoneId
		LocalDateTime todayKolkata = LocalDateTime.now(ZoneId.of("Asia/Kolkata"));
		System.out.println("Current Date in IST="+todayKolkata);

		//java.time.zone.ZoneRulesException: ID de fuso horário desconhecido: IST
		//LocalDateTime hojeIST = LocalDateTime.now(ZoneId.of("IST"));
		
		//Obtendo a data a partir da data base, ou seja, 01/01/1970
		LocalDateTime dateFromBase = LocalDateTime.ofEpochSecond(10000, 0, ZoneOffset.UTC);
		System.out.println("10000th second time from 01/01/1970= "+dateFromBase);

	}

}

Em todos os três exemplos, vimos que se fornecermos argumentos inválidos para criar Data/Hora, então ele lança java.time.DateTimeException, que é um RuntimeException, então não precisamos capturá-lo explicitamente.

Também vimos que podemos obter dados de Data/Hora passando o ZoneId, você pode obter a lista de valores de ZoneId suportados a partir do JavaDoc. Quando executamos a classe acima, obtemos a seguinte saída.

Current DateTime=2014-04-28T16:00:49.455
Current DateTime=2014-04-28T16:00:49.493
Specific Date=2014-01-01T10:10:30
Current Date in IST=2014-04-29T04:30:49.493
10000th second time from 01/01/1970= 1970-01-01T02:46:40

4. Instante

A classe Instante é usada para trabalhar com formato de hora legível por máquina. Instante armazena data e hora em timestamp Unix.

package com.journaldev.java8.time;

import java.time.Duration;
import java.time.Instant;

public class InstantExample {

	public static void main(String[] args) {
		//Timestamp atual
		Instant timestamp = Instant.now();
		System.out.println("Current Timestamp = "+timestamp);
		
		//Instante a partir do timestamp
		Instant specificTime = Instant.ofEpochMilli(timestamp.toEpochMilli());
		System.out.println("Specific Time = "+specificTime);
		
		//Exemplo de Duração
		Duration thirtyDay = Duration.ofDays(30);
		System.out.println(thirtyDay);
	}

}

Saída:

Current Timestamp = 2014-04-28T23:20:08.489Z
Specific Time = 2014-04-28T23:20:08.489Z
PT720H

Utilitários da API de Data do Java 8

A maioria das classes de princípios de Data e Hora fornece vários métodos de utilidade, como mais/menos dias, semanas, meses, etc. Existem alguns outros métodos de utilidade para ajustar a data usando o TemporalAdjuster e para calcular o período entre duas datas.

package com.journaldev.java8.time;

import java.time.LocalDate;
import java.time.LocalTime;
import java.time.Period;
import java.time.temporal.TemporalAdjusters;

public class DateAPIUtilities {

	public static void main(String[] args) {
		
		LocalDate today = LocalDate.now();
		
		//Obter o ano, verificar se é ano bissexto
		System.out.println("Year "+today.getYear()+" is Leap Year? "+today.isLeapYear());
		
		//Comparar duas LocalDate para antes e depois
		System.out.println("Today is before 01/01/2015? "+today.isBefore(LocalDate.of(2015,1,1)));
		
		//Criar LocalDateTime a partir de LocalDate
		System.out.println("Current Time="+today.atTime(LocalTime.now()));
		
		//Operações de adição e subtração
		System.out.println("10 days after today will be "+today.plusDays(10));
		System.out.println("3 weeks after today will be "+today.plusWeeks(3));
		System.out.println("20 months after today will be "+today.plusMonths(20));

		System.out.println("10 days before today will be "+today.minusDays(10));
		System.out.println("3 weeks before today will be "+today.minusWeeks(3));
		System.out.println("20 months before today will be "+today.minusMonths(20));
		
		//Ajustadores temporais para ajustar as datas
		System.out.println("First date of this month= "+today.with(TemporalAdjusters.firstDayOfMonth()));
		LocalDate lastDayOfYear = today.with(TemporalAdjusters.lastDayOfYear());
		System.out.println("Last date of this year= "+lastDayOfYear);
		
		Period period = today.until(lastDayOfYear);
		System.out.println("Period Format= "+period);
		System.out.println("Months remaining in the year= "+period.getMonths());		
	}
}

Output:

Year 2014 is Leap Year? false
Today is before 01/01/2015? true
Current Time=2014-04-28T16:23:53.154
10 days after today will be 2014-05-08
3 weeks after today will be 2014-05-19
20 months after today will be 2015-12-28
10 days before today will be 2014-04-18
3 weeks before today will be 2014-04-07
20 months before today will be 2012-08-28
First date of this month= 2014-04-01
Last date of this year= 2014-12-31
Period Format= P8M3D
Months remaining in the year= 8

Java 8 Análise e Formatação de Datas

É muito comum formatar datas em diferentes formatos e depois analisar uma String para obter objetos de Data e Hora.

package com.journaldev.java8.time;

import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class DateParseFormatExample {

	public static void main(String[] args) {
		
		//Exemplos de formatação
		LocalDate date = LocalDate.now();
		//formato padrão
		System.out.println("Default format of LocalDate="+date);
		//formato específico
		System.out.println(date.format(DateTimeFormatter.ofPattern("d::MMM::uuuu")));
		System.out.println(date.format(DateTimeFormatter.BASIC_ISO_DATE));
		
		
		LocalDateTime dateTime = LocalDateTime.now();
		//formato padrão
		System.out.println("Default format of LocalDateTime="+dateTime);
		//formato específico
		System.out.println(dateTime.format(DateTimeFormatter.ofPattern("d::MMM::uuuu HH::mm::ss")));
		System.out.println(dateTime.format(DateTimeFormatter.BASIC_ISO_DATE));
		
		Instant timestamp = Instant.now();
		//formato padrão
		System.out.println("Default format of Instant="+timestamp);
		
		//Exemplos de análise
		LocalDateTime dt = LocalDateTime.parse("27::Apr::2014 21::39::48",
				DateTimeFormatter.ofPattern("d::MMM::uuuu HH::mm::ss"));
		System.out.println("Default format after parsing = "+dt);
	}

}

Output:

Default format of LocalDate=2014-04-28
28::Apr::2014
20140428
Default format of LocalDateTime=2014-04-28T16:25:49.341
28::Apr::2014 16::25::49
20140428
Default format of Instant=2014-04-28T23:25:49.342Z
Default format after parsing = 2014-04-27T21:39:48

Compatibilidade com a Data e Hora Legadas da API Java

As classes de Data/Hora legadas são usadas em quase todas as aplicações, então ter compatibilidade com versões anteriores é essencial. É por isso que existem vários métodos de utilidade por meio dos quais podemos converter classes legadas em novas classes e vice-versa.

package com.journaldev.java8.time;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone;

public class DateAPILegacySupport {

	public static void main(String[] args) {
		
		//Data para Instantâneo
		Instant timestamp = new Date().toInstant();
		//Agora podemos converter Instantâneo em LocalDateTime ou outras classes similares
		LocalDateTime date = LocalDateTime.ofInstant(timestamp, 
						ZoneId.of(ZoneId.SHORT_IDS.get("PST")));
		System.out.println("Date = "+date);
		
		//Calendário para Instantâneo
		Instant time = Calendar.getInstance().toInstant();
		System.out.println(time);
		//Fuso Horário para ZoneId
		ZoneId defaultZone = TimeZone.getDefault().toZoneId();
		System.out.println(defaultZone);
		
		//ZonedDateTime a partir de um Calendário específico
		ZonedDateTime gregorianCalendarDateTime = new GregorianCalendar().toZonedDateTime();
		System.out.println(gregorianCalendarDateTime);
		
		//API de Data para classes Legadas
		Date dt = Date.from(Instant.now());
		System.out.println(dt);
		
		TimeZone tz = TimeZone.getTimeZone(defaultZone);
		System.out.println(tz);
		
		GregorianCalendar gc = GregorianCalendar.from(gregorianCalendarDateTime);
		System.out.println(gc);
		
	}

}

Output:

Date = 2014-04-28T16:28:54.340
2014-04-28T23:28:54.395Z
America/Los_Angeles
2014-04-28T16:28:54.404-07:00[America/Los_Angeles]
Mon Apr 28 16:28:54 PDT 2014
sun.util.calendar.ZoneInfo[id="America/Los_Angeles",offset=-28800000,dstSavings=3600000,useDaylight=true,transitions=185,lastRule=java.util.SimpleTimeZone[id=America/Los_Angeles,offset=-28800000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]]
java.util.GregorianCalendar[time=1398727734404,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="America/Los_Angeles",offset=-28800000,dstSavings=3600000,useDaylight=true,transitions=185,lastRule=java.util.SimpleTimeZone[id=America/Los_Angeles,offset=-28800000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]],firstDayOfWeek=2,minimalDaysInFirstWeek=4,ERA=1,YEAR=2014,MONTH=3,WEEK_OF_YEAR=18,WEEK_OF_MONTH=5,DAY_OF_MONTH=28,DAY_OF_YEAR=118,DAY_OF_WEEK=2,DAY_OF_WEEK_IN_MONTH=4,AM_PM=1,HOUR=4,HOUR_OF_DAY=16,MINUTE=28,SECOND=54,MILLISECOND=404,ZONE_OFFSET=-28800000,DST_OFFSET=3600000]

Como você pode ver, os métodos toString() das classes legadas TimeZone e GregorianCalendar são muito verbosos e não amigáveis para o usuário.

Conclusão

I like this new Date Time API a lot. Some of the most used classes will be LocalDate and LocalDateTime. It’s very easy to work with the new classes. And, having similar methods that does a particular job makes it easy to find. It will take some time from moving legacy classes to new Date Time classes, but I believe it will be worth the time and effort.

Source:
https://www.digitalocean.com/community/tutorials/java-8-date-localdate-localdatetime-instant