Migrazione alla nuova API Java 8 Date Time

1. Panoramica

In questo tutorial imparerai come effettuare il refactoring del tuo codice per sfruttare la nuova API Date Time introdotta in Java 8.

2. Panoramica delle nuove API

Lavorare con le date in Java era difficile. La vecchia libreria di date fornita da JDK includeva solo tre classi: java.util.Date, java.util.Calendar e java.util.Timezone .

Questi erano adatti solo per i compiti più elementari. Per qualsiasi cosa anche lontanamente complessa, gli sviluppatori dovevano utilizzare librerie di terze parti o scrivere tonnellate di codice personalizzato.

Java 8 ha introdotto un'API Date Time completamente nuova ( java.util.time. * ) Che è vagamente basata sulla popolare libreria Java chiamata JodaTime. Questa nuova API ha notevolmente semplificato l'elaborazione di data e ora e ha risolto molte carenze della vecchia libreria di date.

1.1. Chiarezza API

Un primo vantaggio della nuova API è la chiarezza : l'API è molto chiara, concisa e di facile comprensione. Non ha molte incongruenze trovate nella vecchia libreria come la numerazione dei campi (in Calendar i mesi sono a base zero, ma i giorni della settimana sono a base uno).

1.2. Flessibilità API

Un altro vantaggio è la flessibilità: lavorare con più rappresentazioni del tempo . La vecchia libreria di date includeva solo una singola classe di rappresentazione dell'ora - java.util.Date , che nonostante il nome, è in realtà un timestamp. Memorizza solo il numero di millisecondi trascorsi dall'epoca di Unix.

La nuova API ha molte rappresentazioni temporali diverse, ciascuna adatta a diversi casi d'uso:

  • Istantaneo : rappresenta un punto nel tempo (timestamp)
  • LocalDate : rappresenta una data (anno, mese, giorno)
  • LocalDateTime : uguale a LocalDate , ma include l'ora con una precisione al nanosecondo
  • OffsetDateTime : uguale a LocalDateTime , ma con offset di fuso orario
  • LocalTime : ora con precisione al nanosecondo e senza informazioni sulla data
  • ZonedDateTime : uguale a OffsetDateTime , ma include un ID fuso orario
  • OffsetLocalTime : uguale a LocalTime , ma con differenza di fuso orario
  • MonthDay - mese e giorno, senza anno o ora
  • YearMonth - mese e anno, senza giorno o ora
  • Durata : quantità di tempo rappresentata in secondi, minuti e ore. Ha una precisione al nanosecondo
  • Periodo : quantità di tempo rappresentata in giorni, mesi e anni

1.3. Immutabilità e thread-safety

Un altro vantaggio è che tutte le rappresentazioni dell'ora in Java 8 Date Time API sono immutabili e quindi thread-safe.

Tutti i metodi di mutazione restituiscono una nuova copia invece di modificare lo stato dell'oggetto originale.

Le vecchie classi come java.util.Date non erano thread-safe e potevano introdurre bug di concorrenza molto sottili.

1.4. Concatenamento di metodi

Tutti i metodi mutanti possono essere concatenati, consentendo di implementare trasformazioni complesse in una singola riga di codice.

ZonedDateTime nextFriday = LocalDateTime.now() .plusHours(1) .with(TemporalAdjusters.next(DayOfWeek.FRIDAY)) .atZone(ZoneId.of("PST")); 

2. Esempi

Gli esempi seguenti dimostreranno come eseguire attività comuni sia con la vecchia che con la nuova API.

Ottenere l'ora corrente

// Old Date now = new Date(); // New ZonedDateTime now = ZonedDateTime.now(); 

Rappresentare il tempo specifico

// Old Date birthDay = new GregorianCalendar(1990, Calendar.DECEMBER, 15).getTime(); // New LocalDate birthDay = LocalDate.of(1990, Month.DECEMBER, 15); 

Estrazione di campi specifici

// Old int month = new GregorianCalendar().get(Calendar.MONTH); // New Month month = LocalDateTime.now().getMonth(); 

Sommare e sottrarre tempo

// Old GregorianCalendar calendar = new GregorianCalendar(); calendar.add(Calendar.HOUR_OF_DAY, -5); Date fiveHoursBefore = calendar.getTime(); // New LocalDateTime fiveHoursBefore = LocalDateTime.now().minusHours(5); 

Modifica di campi specifici

// Old GregorianCalendar calendar = new GregorianCalendar(); calendar.set(Calendar.MONTH, Calendar.JUNE); Date inJune = calendar.getTime(); // New LocalDateTime inJune = LocalDateTime.now().withMonth(Month.JUNE.getValue()); 

Troncamento

Il troncamento reimposta tutti i campi temporali più piccoli del campo specificato. Nell'esempio sotto, i minuti e tutto ciò che segue sarà impostato a zero

// Old Calendar now = Calendar.getInstance(); now.set(Calendar.MINUTE, 0); now.set(Calendar.SECOND, 0); now.set(Calendar.MILLISECOND, 0); Date truncated = now.getTime(); // New LocalTime truncated = LocalTime.now().truncatedTo(ChronoUnit.HOURS); 

Conversione del fuso orario

// Old GregorianCalendar calendar = new GregorianCalendar(); calendar.setTimeZone(TimeZone.getTimeZone("CET")); Date centralEastern = calendar.getTime(); // New ZonedDateTime centralEastern = LocalDateTime.now().atZone(ZoneId.of("CET")); 

Ottenere l'intervallo di tempo tra due punti nel tempo

// Old GregorianCalendar calendar = new GregorianCalendar(); Date now = new Date(); calendar.add(Calendar.HOUR, 1); Date hourLater = calendar.getTime(); long elapsed = hourLater.getTime() - now.getTime(); // New LocalDateTime now = LocalDateTime.now(); LocalDateTime hourLater = LocalDateTime.now().plusHours(1); Duration span = Duration.between(now, hourLater); 

Formattazione e analisi dell'ora

DateTimeFormatter sostituisce il vecchio SimpleDateFormat che è thread-safe e fornisce funzionalità aggiuntive.

// Old SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); Date now = new Date(); String formattedDate = dateFormat.format(now); Date parsedDate = dateFormat.parse(formattedDate); // New LocalDate now = LocalDate.now(); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); String formattedDate = now.format(formatter); LocalDate parsedDate = LocalDate.parse(formattedDate, formatter); 

Numero di giorni in un mese

// Old Calendar calendar = new GregorianCalendar(1990, Calendar.FEBRUARY, 20); int daysInMonth = calendar.getActualMaximum(Calendar.DAY_OF_MONTH); // New int daysInMonth = YearMonth.of(1990, 2).lengthOfMonth();

3. Interazione con il codice legacy

In molti casi un utente potrebbe dover garantire l'interoperabilità con le librerie di terze parti che si basano sulla vecchia libreria della data.

In Java 8 le vecchie classi di libreria di data sono state estese con metodi che le convertono in oggetti corrispondenti dalla nuova API Date.

Le nuove classi forniscono funzionalità simili.

Instant instantFromCalendar = GregorianCalendar.getInstance().toInstant(); ZonedDateTime zonedDateTimeFromCalendar = new GregorianCalendar().toZonedDateTime(); Date dateFromInstant = Date.from(Instant.now()); GregorianCalendar calendarFromZonedDateTime = GregorianCalendar.from(ZonedDateTime.now()); Instant instantFromDate = new Date().toInstant(); ZoneId zoneIdFromTimeZone = TimeZone.getTimeZone("PST").toZoneId(); 

4. Conclusione

In questo articolo abbiamo esplorato la nuova API Date Time disponibile in Java 8. Abbiamo esaminato i suoi vantaggi rispetto all'API deprecata e evidenziato le differenze utilizzando più esempi.

Tieni presente che abbiamo appena scalfito la superficie delle funzionalità della nuova API Date Time. Assicurati di leggere la documentazione ufficiale per scoprire la gamma completa di strumenti offerti dalla nuova API.

Esempi di codice possono essere trovati nel progetto GitHub.