Data Java 8 – LocalDate, LocalDateTime, Instant

L’API Data e Ora è una delle caratteristiche più importanti del rilascio di Java 8. Java mancava di un approccio coerente per la gestione di Data e Ora fin dall’inizio e l’API Data e Ora di Java 8 è un’aggiunta gradita alle API di base di Java.

Perché abbiamo bisogno della nuova API Java Data e Ora?

Prima di iniziare a esaminare l’API Data e Ora di Java 8, vediamo perché abbiamo bisogno di una nuova API per questo. Ci sono stati diversi problemi con le classi esistenti relative a data e ora in Java, alcuni di essi sono:

  • Le classi Data e Ora di Java non sono definite in modo coerente, abbiamo la classe Data sia nel package java.util che in quello java.sql. Inoltre, le classi di formattazione e analisi sono definite nel package java.text.
  • java.util.Date contiene sia valori di data che di ora, mentre java.sql.Date contiene solo il valore della data. Avere questo nel package java.sql non ha senso. Inoltre, entrambe le classi hanno lo stesso nome, il che è un design molto cattivo di per sé.
  • Non ci sono classi chiaramente definite per tempo, timestamp, formattazione e analisi. Abbiamo la classe astratta java.text.DateFormat per l’analisi e la formattazione necessarie. Di solito, la classe SimpleDateFormat viene utilizzata per l’analisi e la formattazione.
  • Tutte le classi Data sono mutabili, quindi non sono thread-safe. È uno dei problemi più grandi con le classi Data e Calendar di Java.
  • La classe Date non fornisce l’internazionalizzazione, né il supporto per i fusi orari. Quindi le classi java.util.Calendar e java.util.TimeZone sono state introdotte, ma hanno anche tutti i problemi elencati sopra.

Ci sono alcuni altri problemi con i metodi definiti nelle classi Date e Calendar, ma i problemi sopra elencati chiariscono che era necessaria una robusta API per la data e l’ora in Java. Ecco perché Joda Time ha svolto un ruolo chiave come sostituto di qualità per i requisiti di data e ora di Java.

Principi di progettazione della data e ora di Java 8

L’API di data e ora di Java 8 è un’implementazione di JSR-310. È progettata per superare tutti i difetti delle implementazioni legacy della data e dell’ora. Alcuni dei principi di progettazione della nuova API di data e ora sono:

  1. Immutabilità: Tutte le classi nella nuova API di data e ora sono immutabili e adatte per ambienti multithreading.

  2. Separazione delle preoccupazioni: La nuova API separa chiaramente tra data e ora leggibili dall’uomo e tempo macchina (timestamp Unix). Definisce classi separate per Data, Ora, DataOra, Timestamp, Fuso Orario, ecc.

  3. Chiarezza: I metodi sono chiaramente definiti e svolgono la stessa azione in tutte le classi. Ad esempio, per ottenere l’istanza corrente abbiamo il metodo now(). Sono definiti metodi format() e parse() in tutte queste classi piuttosto che avere una classe separata per essi.

    Tutte le classi utilizzano il Pattern Factory e il Pattern Strategy per una migliore gestione. Una volta utilizzati i metodi in una delle classi, lavorare con altre classi non sarà difficile.

  4. Operazioni di utilità: Tutte le nuove classi API Data-Ora sono dotate di metodi per eseguire compiti comuni, come sommare, sottrarre, formattare, analizzare, ottenere la parte separata in data/ora, ecc.

  5. Estendibile: La nuova API Data e Ora funziona sul sistema di calendario ISO-8601 ma possiamo usarla anche con altri calendari non ISO.

Pacchetti API Data e Ora

Java 8 API Data e Ora consiste nei seguenti pacchetti.

  1. java.time: Questo è il pacchetto base della nuova API Data e Ora di Java. Tutte le principali classi di base fanno parte di questo pacchetto, come LocalDate, LocalTime, LocalDateTime, Instant, Period, Duration, ecc. Tutte queste classi sono immutabili e thread-safe. Nella maggior parte dei casi, queste classi saranno sufficienti per gestire requisiti comuni.
  2. java.time.chrono: Questo pacchetto definisce API generiche per sistemi di calendario non ISO. Possiamo estendere la classe AbstractChronology per creare il nostro sistema di calendario.
  3. java.time.formato: Questo pacchetto contiene classi utilizzate per formattare e analizzare gli oggetti data-ora. La maggior parte delle volte non li utilizzeremo direttamente perché le classi principali nel pacchetto java.time forniscono metodi di formattazione e analisi.
  4. java.time.temporale: Questo pacchetto contiene oggetti temporali e possiamo usarlo per trovare le date o gli orari specifici relativi agli oggetti data/ora. Ad esempio, possiamo usare questi per trovare il primo o l’ultimo giorno del mese. È possibile identificare facilmente questi metodi perché hanno sempre il formato “withXXX”.
  5. Pacchetto java.time.zone: Questo pacchetto contiene classi per supportare diversi fusi orari e le loro regole.

Esempi di classi API di data e ora di Java 8

Abbiamo esaminato la maggior parte delle parti importanti dell’API di data e ora di Java. È ora di esaminare le classi più importanti dell’API di data e ora con degli esempi.

1. LocalDate

LocalDate è una classe immutabile che rappresenta una data con il formato predefinito di yyyy-MM-dd. Possiamo utilizzare il metodo now() per ottenere la data corrente. Possiamo anche fornire argomenti di input per anno, mese e giorno per creare un’istanza di LocalDate.

Questa classe fornisce un metodo sovraccaricato per now() in cui possiamo passare ZoneId per ottenere date in un fuso orario specifico. Questa classe fornisce la stessa funzionalità di java.sql.Date.

package com.journaldev.java8.time;

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

/**
 * Esempi di LocalDate
 * @author pankaj
 *
 */
public class LocalDateExample {

	public static void main(String[] args) {
		
		//Data corrente
		LocalDate today = LocalDate.now();
		System.out.println("Current Date="+today);
		
		//Creazione di LocalDate fornendo argomenti di input
		LocalDate firstDay_2014 = LocalDate.of(2014, Month.JANUARY, 1);
		System.out.println("Specific Date="+firstDay_2014);
		
		
		//Provare a creare una data fornendo input non validi
		//LocalDate feb29_2014 = LocalDate.of(2014, Month.FEBRUARY, 29);
		//Eccezione nel thread "main" java.time.DateTimeException: 
		//Data non valida '29 febbraio' poiché '2014' non è un anno bisestile
		
		//Data corrente in "Asia/Kolkata", è possibile ottenerla dalla documentazione di ZoneId
		LocalDate todayKolkata = LocalDate.now(ZoneId.of("Asia/Kolkata"));
		System.out.println("Current Date in IST="+todayKolkata);

		//java.time.zone.ZoneRulesException: ID fuso orario sconosciuto: IST
		//LocalDate todayIST = LocalDate.now(ZoneId.of("IST"));
		
		//Ottenimento della data dalla data di base, cioè 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);
	}

}

La spiegazione dei metodi di LocalDate è fornita nei commenti. Quando eseguiamo questo programma, otteniamo l’output seguente.

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 è una classe immutabile il cui istanza rappresenta un’ora nel formato leggibile dall’essere umano. Il suo formato predefinito è hh:mm:ss.zzz. Proprio come LocalDate, questa classe fornisce il supporto per il fuso orario e la creazione dell’istanza mediante il passaggio di ore, minuti e secondi come argomenti di input.

package com.journaldev.java8.time;

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

/**
 * Esempi di LocalTime
 * @author pankaj
 *
 */
public class LocalTimeExample {

	public static void main(String[] args) {
		
		//Ora corrente
		LocalTime time = LocalTime.now();
		System.out.println("Current Time="+time);
		
		//Creazione di LocalTime fornendo argomenti di input
		LocalTime specificTime = LocalTime.of(12,20,25,40);
		System.out.println("Specific Time of Day="+specificTime);
		
		
		//Prova a creare un'ora fornendo input non validi
		//LocalTime oraNonValida = LocalTime.of(25,20);
		//Eccezione nel thread "main" java.time.DateTimeException: 
		//Valore non valido per HourOfDay (valori validi 0 - 23): 25
		
		//Data corrente in "Asia/Kolkata", puoi ottenerla dalla documentazione di ZoneId
		LocalTime timeKolkata = LocalTime.now(ZoneId.of("Asia/Kolkata"));
		System.out.println("Current Time in IST="+timeKolkata);

		//java.time.zone.ZoneRulesException: ID fuso orario sconosciuto: IST
		//LocalTime oggiIST = LocalTime.now(ZoneId.of("IST"));
		
		//Ottenere la data dalla data di base, ovvero 01/01/1970
		LocalTime specificSecondTime = LocalTime.ofSecondOfDay(10000);
		System.out.println("10000th second time= "+specificSecondTime);

	}

}

Output:

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 è un oggetto data-ora immutabile che rappresenta una data-ora con il formato predefinito yyyy-MM-dd-HH-mm-ss.zzz. Fornisce un metodo factory che prende gli argomenti di input LocalDate e LocalTime per creare un’istanza di 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 attuale
		LocalDateTime today = LocalDateTime.now();
		System.out.println("Current DateTime="+today);
		
		//Data attuale utilizzando LocalDate e LocalTime
		today = LocalDateTime.of(LocalDate.now(), LocalTime.now());
		System.out.println("Current DateTime="+today);
		
		//Creazione di LocalDateTime fornendo gli argomenti di input
		LocalDateTime specificDate = LocalDateTime.of(2014, Month.JANUARY, 1, 10, 10, 30);
		System.out.println("Specific Date="+specificDate);
		
		
		//Prova a creare una data fornendo input non validi
		//LocalDateTime feb29_2014 = LocalDateTime.of(2014, Month.FEBRUARY, 28, 25,1,1);
		//Eccezione nel thread "main" java.time.DateTimeException: 
		//Valore non valido per HourOfDay (valori validi 0 - 23): 25

		
		//Data attuale in "Asia/Kolkata", puoi trovarla nella documentazione di ZoneId
		LocalDateTime todayKolkata = LocalDateTime.now(ZoneId.of("Asia/Kolkata"));
		System.out.println("Current Date in IST="+todayKolkata);

		//java.time.zone.ZoneRulesException: ID fuso orario sconosciuto: IST
		//LocalDateTime oggiIST = LocalDateTime.now(ZoneId.of("IST"));
		
		//Ottieni la data dalla data di base, ovvero 01/01/1970
		LocalDateTime dateFromBase = LocalDateTime.ofEpochSecond(10000, 0, ZoneOffset.UTC);
		System.out.println("10000th second time from 01/01/1970= "+dateFromBase);

	}

}

In tutti e tre gli esempi, abbiamo visto che se forniamo argomenti non validi per la creazione di Data/Ora, viene lanciata java.time.DateTimeException, che è una RuntimeException quindi non è necessario catturarla esplicitamente.

Abbiamo anche visto che possiamo ottenere dati Data/Ora passando ZoneId, è possibile ottenere l’elenco dei valori di ZoneId supportati dalla sua JavaDoc. Quando eseguiamo la classe sopra, otteniamo l’output seguente.

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. Istante

La classe Istante viene utilizzata per lavorare con il formato di tempo leggibile dalla macchina. Istante memorizza la data e l’ora nel 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 corrente
		Instant timestamp = Instant.now();
		System.out.println("Current Timestamp = "+timestamp);
		
		//Istante dal timestamp
		Instant specificTime = Instant.ofEpochMilli(timestamp.toEpochMilli());
		System.out.println("Specific Time = "+specificTime);
		
		//Esempio di durata
		Duration thirtyDay = Duration.ofDays(30);
		System.out.println(thirtyDay);
	}

}

Output:

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

Utilità API Data Java 8

La maggior parte delle classi di principio Data e Ora fornisce vari metodi di utilità come più/meno giorni, settimane, mesi ecc. Ci sono alcuni altri metodi di utilità per regolare la data utilizzando il TemporalAdjuster e per calcolare il periodo tra due date.

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();
		
		//Ottieni l'anno, controlla se è un anno bisestile
		System.out.println("Year "+today.getYear()+" is Leap Year? "+today.isLeapYear());
		
		//Confronta due LocalDate per prima e dopo
		System.out.println("Today is before 01/01/2015? "+today.isBefore(LocalDate.of(2015,1,1)));
		
		//Crea LocalDateTime da LocalDate
		System.out.println("Current Time="+today.atTime(LocalTime.now()));
		
		//Operazioni di più e meno
		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));
		
		//Adattatori temporali per regolare le date
		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

Parsing e formattazione della data in Java 8

È molto comune formattare la data in diversi formati e quindi analizzare una stringa per ottenere gli oggetti Data Time.

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) {
		
		//Esempi di formattazione
		LocalDate date = LocalDate.now();
		//formato predefinito
		System.out.println("Default format of LocalDate="+date);
		//formato specifico
		System.out.println(date.format(DateTimeFormatter.ofPattern("d::MMM::uuuu")));
		System.out.println(date.format(DateTimeFormatter.BASIC_ISO_DATE));
		
		
		LocalDateTime dateTime = LocalDateTime.now();
		//formato predefinito
		System.out.println("Default format of LocalDateTime="+dateTime);
		//formato specifico
		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 predefinito
		System.out.println("Default format of Instant="+timestamp);
		
		//Esempi di analisi
		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

Supporto per date e ora legacy dell’API Java

Le classi Legacy Date/Time sono utilizzate in quasi tutte le applicazioni, quindi avere la compatibilità all’indietro è un must. Ecco perché esistono diversi metodi di utilità attraverso i quali possiamo convertire le classi Legacy in nuove classi e viceversa.

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 a Instant
		Instant timestamp = new Date().toInstant();
		//Ora possiamo convertire Instant in LocalDateTime o altre classi simili
		LocalDateTime date = LocalDateTime.ofInstant(timestamp, 
						ZoneId.of(ZoneId.SHORT_IDS.get("PST")));
		System.out.println("Date = "+date);
		
		//Calendario a Instant
		Instant time = Calendar.getInstance().toInstant();
		System.out.println(time);
		//Fuso orario a ZoneId
		ZoneId defaultZone = TimeZone.getDefault().toZoneId();
		System.out.println(defaultZone);
		
		//ZonedDateTime da un calendario specifico
		ZonedDateTime gregorianCalendarDateTime = new GregorianCalendar().toZonedDateTime();
		System.out.println(gregorianCalendarDateTime);
		
		//API delle date alle classi Legacy
		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]

Come puoi vedere, i metodi toString() delle classi legacy TimeZone e GregorianCalendar sono troppo prolissi e non user-friendly.

Conclusione

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