Prima dell'introduzione di Java 8, la gestione delle date si concretizzava nell'utilizzo delle classi java.text.DateFormat
e java.text.SimpleDateFormat
per avere, rispettivamente, un formato di base (DateFormat
) oppure uno più avanzato (SimpleDateFormat
). L'introduzione della classe java.time.format.DateTimeFormatter
nelle API Java 8 ha confinato queste classi nella categoria legacy.
In questo articolo vedremo esempi di formattazione delle date sia con stile Java 8 che antecedente ad esso. Con DateFormat
abbiamo essenzialmente una formattazione riassunta dalle seguenti tipologie:
Formato | Descrizione |
---|---|
SHORT | Completamente numerica, ad esempio 12/01/19. |
MEDIUM | Più lunga con elementi testuali, ad esempio Jan 12, 1952. |
LONG | Simile alla MEDIUM ma con un estensione testuale maggiore, ad esempio January 12, 1952. |
FULL | La specifica del formato più lunga e completa, ad esempio Tuesday, April 12, 1952 AD. |
Se il formato di cui abbiamo bisogno rientra in uno di questi tipi, possiamo creare un formattatore di date attraverso un'istanza della classe DateFormat
specificando un formato per la data stessa ed una posizione geografica:
DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, Locale.ITALY);
System.out.println(df.format(new Date()));
System.out.println(df.parse("12/11/19"));
df = DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.ITALY);
System.out.println(df.format(new Date()));
System.out.println(df.parse("12-gen-2019"));
df = DateFormat.getDateInstance(DateFormat.LONG, Locale.ITALY);
System.out.println(df.format(new Date()));
System.out.println(df.parse("12 gennaio 2019"));
df = DateFormat.getDateInstance(DateFormat.FULL, Locale.ITALY);
System.out.println(df.format(new Date()));
System.out.println(df.parse("sabato 12 gennaio 2019"));
Ottenendo in stampa:
12/01/19
Tue Nov 12 00:00:00 CET 2019
12-gen-2019
Sat Jan 12 00:00:00 CET 2019
12 gennaio 2019
Sat Jan 12 00:00:00 CET 2019
sabato 12 gennaio 2019
Sat Jan 12 00:00:00 CET 2019
Notiamo come la stampa della stringa, ottenuta dal metodo parse()
, non segua il tipo di Locale specificato (ITALY
). Questo comportamento Locale independent del metodo toString()
della classe Date
, non consente di fare affidamento ad esso, in generale, per la stampa di una data ottenuta come parsing di una stringa.
Un altro aspetto evidente è l'orario non specificato fisso sulla mezzanotte di ogni giorno. Per poter gestire date con ore, minuti e secondi dobbiamo utilizzare il metodo getDateTimeInstance(int dateStyle, int timeStyle, Locale aLocale)
. Vediamo un esempio con il formato SHORT
per data e orario:
DateFormat df = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, Locale.ITALY);
System.out.println(df.format(new Date()));
System.out.println(df.parse("12/11/19 12.00.00"));
La precedente stampa, relativamente al formato SHORT
, cambia quindi in:
12/01/19 14.45
Tue Nov 12 12:00:00 CET 2019
In cui appare evidente l'orario corrente e quello successivo da noi impostato. Quando il formato di cui abbiamo bisogno non rientra nelle categorie gestite dalla classe DateFormat
, possiamo utilizzare la sua classe di estensione SimpleDateFormat
.
La descrizione Javadoc di questa classe, suggerisce di utilizzare il metodo getDateInstance()
o getDateTimeInstance()
per ottenere un formattatore di default, da personalizzare successivamente con il metodo applyPattern()
specifico della classe SimpleDateFormat
.
L'operazione preliminare di costruzione di un oggetto SimpleDateFormat
è realizzabile con il seguente frammento di codice:
SimpleDateFormat dateFormat = (SimpleDateFormat)SimpleDateFormat.getDateInstance(DateFormat.SHORT, Locale.ITALY);
Prima di procedere è opportuna un piccola considerazione sulle sigle che evidenziano il fuso orario (in Java rappresentato dalla classe TimeZone
). L'UTC è il tempo coordinato universale corrispondente al fuso orario di Greenwich. In questo punto è stato posto il meridiano 0 e le ore 12 in punto arrivano quando il sole si trova esattamente a picco della linea del meridiano.
La vecchia sigla GMT, che significa tempo medio di Greenwich, ha lasciato il posto alla UTC. Il nuovo sistema è basato su orologi atomici e non più su fenomeni celesti. Il CET (Central European Time), il fuso orario che comprende anche l'Italia, nei mesi invernali può essere indicato con la sigla UTC+1, l'orario UTC corrisponde solo all'ora solare e non a quella legale.
Di conseguenza, per quanto riguarda il fuso orario che comprende anche l'Italia, in estate si passa da UTC+1 a UTC+2 e da CET a CEST (Central European Summer Time). Nelle stringhe di formattazione che vedremo, la parte finale indica proprio l'offset dall'ora UTC o GMT. Un esempio:
dateFormat.setTimeZone(TimeZone.getTimeZone("Europe/Rome"));
dateFormat.applyPattern("yyyy.MM.dd E 'at' HH:mm:ss z");
System.out.println(dateFormat.format(new Date()));
dateFormat.applyPattern("yyyy.MM.dd E 'at' HH:mm:ss Z");
System.out.println(dateFormat.format(new Date()));
Che produce in output:
2019.01.12 sab at 15:27:07 CET
2019.01.12 sab at 15:27:07 +0100
Nel codice di esempio abbiamo utilizzato un formato per la stringa della data che evidenzia in modo abbastanza intuitivo anno, giorno, mese, ore, minuti e secondi. Ma cosa rappresenta la z finale in minuscolo o maiuscolo? Ebbene la z
è proprio la lettera che consente la stampa delle informazioni di fuso orario. Con la lettera z richiediamo la stampa dell'informazione del fuso orario di tipo General time zone ovvero un fuso orario espresso come offset rispetto all'orario di Greenwich GMT.
La lettera z rappresenta invece il formato conforme alla RFC 822 time zone in cui l'offset GMT è espresso secondo il formato Sign TwoDigitHours Minutes in cui si specifica il segno di somma/sottrazione e la quantità in ore e minuti da sottrarre o sommare all'orario GMT. Nel nostro caso +0100
indica la somma di un'ora e zero minuti all'orario GMT per ottenere l'orario correntemente stampato.
Elenchiamo alcuni esempi di formato di date più comuni:
Formato | Esempio di data |
---|---|
dd-MM-yy | 12-01-2019 |
dd-MM-yyyy | 12-01-2019 |
MM-dd-yyyy | 01-12-2019 |
yyyy-MM-dd | 2019-01-12 |
yyyy-MM-dd HH:mm:ss | 2019-01-12 23:59:59 |
yyyy-MM-dd HH:mm:ss.SSS | 2019-01-12 23:59:59.999 |
yyyy-MM-dd HH:mm:ss.SSSZ | 2019-01-12 23:59:59.999+0100 |
EEEEE MMMMM yyyy HH:mm:ss.SSSZ | Saturday January 2019 13:45:00.720+0100 |
Se stiamo utilizzando la versione 8 o superiore di Java è consigliabile fare uso della nuova classe java.time.format.DateTimeFormatter
. Un formattatore con DateTimeFormatter
si ottiene attraverso l'uso del metodo statico ofPattern()
:
DateTimeFormatter formatter = DateTimeFormatter.
ofPattern("yyyy.MM.dd",Locale.ITALY).withZone(ZoneId.of("Europe/Rome"));
Possiamo notare come con una singola istruzione riusciamo ad ottenere un formattatore compreso di definizione del formato geografico e del fuso orario (classe java.time.ZoneId
). Successivamente, utilizzando la classe java.time.LocalDate
, possiamo ottenere la data corrente, formattarla e parsarla con il pattern appena definito:
LocalDate date = LocalDate.now();
String text = date.format(formatter);
System.out.println(text);
LocalDate parsedDate = LocalDate.parse(text, formatter);
System.out.println(parsedDate);
Il codice precedente fornisce in stampa:
2019.01.12
2019-01-12
Con la seconda stampa che produce un formato indipendente dal Locale definito a causa del comportamento del metodo toString()
. La classe LocalDate è utilizzabile per la formattazione di date che non prevedono l'utilizzo di ore, minuti e secondi. Se abbiamo bisogno di date estese, dobbiamo sostituirla con la classe LocalDateTime
:
DateTimeFormatter formatter = DateTimeFormatter.
ofPattern("yyyy.MM.dd HH:mm:ss z",Locale.ITALY).withZone(ZoneId.of("Europe/Rome"));
LocalDateTime dateTime = LocalDateTime.now();
String text = dateTime.format(formatter);
System.out.println(text);
LocalDateTime parsedDateTime = LocalDateTime.parse(text, formatter);
System.out.println(parsedDateTime);
Il cui output è:
2019.01.12 16:30:47 CET
2019-01-12T16:30:47
Concludiamo l'articolo sottolineando come DateTimeFormatter
sia una classe immutabile e Thread safe, caratteristica non presente nelle classi DateFormat
e SimpleDateFormat
.