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 de data e hora existentes 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 no pacote java.util quanto no pacote java.sql. Novamente, as classes de formatação e análise são definidas no pacote java.text.
  • O 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 algum. Além disso, ambas as classes têm o mesmo nome, o que é um design muito ruim por si só.
  • Não existem classes claramente definidas para hora, timestamp, 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, então elas não são seguras para thread. Este é um dos maiores problemas com as classes de Data e Calendário do Java.
  • A classe Date não fornece internacionalização, não há suporte para fuso horário. Então, as classes java.util.Calendar e java.util.TimeZone foram introduzidas, mas também têm todos os problemas listados acima.

Existem algumas outras questões com os métodos definidos nas classes Date e Calendar, mas os problemas acima deixam claro que uma API robusta de Data e Hora era necessária em Java. É por isso que Joda Time desempenhou um papel fundamental como substituto de qualidade para os requisitos de Data e Hora em Java.

Princípios de Design de Data e Hora do Java 8

A API de Data e Hora do Java 8 é uma implementação do JSR-310. Foi projetada para superar todas as falhas nas implementações legadas de data e hora. 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 multithreaded.

  2. Separação de Preocupações: A nova API separa claramente entre data e hora legíveis para humanos e tempo de máquina (timestamp Unix). Ela define classes separadas para Data, Hora, DataHora, Timestamp, Fuso Horário, etc.

  3. Clareza: Os métodos são claramente definidos e executam 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 usam o Padrão de Fábrica e o Padrão de Estratégia para melhor manipulação. 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 Date-Time vêm com métodos para realizar tarefas comuns, como adição, subtração, formatação, análise, obtenção da 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 seguras para threads. 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 formatar e analisar objetos de data e hora. Na maioria das vezes, não os 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 esses métodos facilmente 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. É hora agora 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 data 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 feb29_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: Identificação de fuso horário desconhecida: IST
		//LocalDate hojeIST = LocalDate.now(ZoneId.of("IST"));
		
		//Obtendo a data a partir 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 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 a fuso horário e criação de instância ao passar 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) {
		
		//Hora 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 um 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: ID de fuso horário desconhecido: IST
		//LocalTime hojeIST = LocalTime.now(ZoneId.of("IST"));
		
		//Obtendo a data a partir da data base, ou seja, 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 feb29_2014 = LocalDateTime.of(2014, Month.FEBRUARY, 28, 25,1,1);
		//Exceção na 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
		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, ele lançará java.time.DateTimeException, que é uma RuntimeException, então não precisamos capturá-la explicitamente.

Também vimos que podemos obter dados de Data/Hora passando ZoneId, você pode obter a lista de valores de ZoneId suportados em seu 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. Instantâneo

A classe Instantâneo é usada para trabalhar com formato de hora legível por máquina. Instantâneo 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);
		
		//Instantâneo 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);
	}

}

Resultado:

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ípio de Data e Hora fornece vários métodos de utilidade, como mais/menos dias, semanas, meses etc. Existem 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 dois 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, em seguida, analisar uma String para obter os objetos 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

API de Data Java Suporte a Data e Hora Legado

As classes de Data/Hora legadas são usadas em quase todas as aplicações, então ter compatibilidade com versões anteriores é imprescindível. É por isso que existem vários métodos de utilidade através 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 Instante
		Instant timestamp = new Date().toInstant();
		//Agora podemos converter Instante para 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 Instante
		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);
		
	}

}

Resultado:

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