Conversione da stringa a data in Java

Qual è il modo migliore per convertire una Stringa nel formato '2 gennaio 2010' in una Data in Java?

In definitiva, voglio scomporre il mese, il giorno e l'anno come numeri interi in modo da poter usare

Date date = new Date();
date.setMonth()..
date.setYear()..
date.setDay()..
date.setlong currentTime = date.getTime();

per convertire la data in tempo.

Soluzione

Questo è il modo più difficile, e quei metodi setter java.util.Date sono stati deprecati da Java 1.1 (1997). Formattate semplicemente la data usando SimpleDateFormat usando un pattern di formato corrispondente alla stringa di input.

Nel tuo caso specifico di "2 gennaio 2010" come stringa di input:

  1. "Gennaio" è il mese del testo completo, quindi usa il pattern MMMM per esso

  2. "2" è il giorno breve del mese, quindi usa il pattern d per esso.

  3. "2010" è l'anno a 4 cifre, quindi usa il modello yyyy.

  4. "!-->

String string = "January 2, 2010";
DateFormat format = new SimpleDateFormat("MMMM d, yyyy", Locale.ENGLISH);
Date date = format.parse(string);
System.out.println(date); // Sat Jan 02 00:00:00 GMT 2010

Si noti l'importanza dell'argomento esplicito Locale. Se lo ometti, allora userà il locale di default che non è necessariamente l'inglese come usato nel nome del mese della stringa di input. Se il locale non corrisponde alla stringa di input, allora si otterrebbe confusamente una java.text.ParseException anche se il modello di formato sembra valido.

Ecco un estratto di rilevanza da il javadoc, che elenca tutti i pattern di formato disponibili:

Letter  Date or Time Component  Presentation        Examples
------  ----------------------  ------------------  -------------------------------------
G       Era designator          Text                AD
y       Year                    Year                1996; 96
Y       Week year               Year                2009; 09
M/L     Month in year           Month               July; Jul; 07
w       Week in year            Number              27
W       Week in month           Number              2
D       Day in year             Number              189
d       Day in month            Number              10
F       Day of week in month    Number              2
E       Day in week             Text                Tuesday; Tue
u       Day number of week      Number              1
a       Am/pm marker            Text                PM
H       Hour in day (0-23)      Number              0
k       Hour in day (1-24)      Number              24
K       Hour in am/pm (0-11)    Number              0
h       Hour in am/pm (1-12)    Number              12
m       Minute in hour          Number              30
s       Second in minute        Number              55
S       Millisecond             Number              978
z       Time zone               General time zone   Pacific Standard Time; PST; GMT-08:00
Z       Time zone               RFC 822 time zone   -0800
X       Time zone               ISO 8601 time zone  -08; -0800; -08:00

Si noti che i modelli sono sensibili alle maiuscole e che i modelli basati sul testo di quattro o più caratteri rappresentano la forma completa; altrimenti viene usata una forma breve o abbreviata se disponibile. Quindi, ad esempio, MMMMM o più non è necessario.

Ecco alcuni esempi di modelli validi di SimpleDateFormat per analizzare una data stringa:

Input string                            Pattern
------------------------------------    ----------------------------
2001.07.04 AD at 12:08:56 PDT           yyyy.MM.dd G 'at' HH:mm:ss z
Wed, Jul 4, '01                         EEE, MMM d, ''yy
12:08 PM                                h:mm a
12 o'clock PM, Pacific Daylight Time    hh 'o''clock' a, zzzz
0:08 PM, PDT                            K:mm a, z
02001.July.04 AD 12:08 PM               yyyyy.MMMM.dd GGG hh:mm aaa
Wed, 4 Jul 2001 12:08:56 -0700          EEE, d MMM yyyy HH:mm:ss Z
010704120856-0700                       yyMMddHHmmssZ
2001-07-04T12:08:56.235-0700            yyyy-MM-dd'T'HH:mm:ss.SSSZ
2001-07-04T12:08:56.235-07:00           yyyy-MM-dd'T'HH:mm:ss.SSSXXX
2001-W27-3                              YYYY-'W'ww-u

Una nota importante è che SimpleDateFormat non è non thread safe. In altre parole, non si dovrebbe mai dichiararlo e assegnarlo come variabile statica o di istanza e poi riutilizzarlo da diversi metodi/thread. Dovreste sempre crearlo nuovo di zecca all'interno dell'ambito locale del metodo.


Java 8 update

Se ti capita di essere su Java 8 o più recente, allora usa DateTimeFormatter (anche qui, clicca sul link per vedere tutti i formattatori predefiniti e i modelli di formato disponibili; il tutorial è disponibile qui). Questa nuova API è ispirata da JodaTime.

String string = "January 2, 2010";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMMM d, yyyy", Locale.ENGLISH);
LocalDate date = LocalDate.parse(string, formatter);
System.out.println(date); // 2010-01-02

Nota: se il tuo schema di formato contiene anche la parte dell'ora, allora usa LocalDateTime#parse(text, formatter) invece di LocalDate#parse(text, formatter). E, se il tuo modello di formato contiene anche il fuso orario, allora usa ZonedDateTime#parse(text, formatter) invece di [ZonedDateTime#parse(text, formatter)].

Ecco un estratto rilevante dal javadoc, che elenca tutti i pattern di formato disponibili:

Symbol  Meaning                     Presentation  Examples
------  --------------------------  ------------  ----------------------------------------------
G       era                         text          AD; Anno Domini; A
u       year                        year          2004; 04
y       year-of-era                 year          2004; 04
D       day-of-year                 number        189
M/L     month-of-year               number/text   7; 07; Jul; July; J
d       day-of-month                number        10

Q/q     quarter-of-year             number/text   3; 03; Q3; 3rd quarter
Y       week-based-year             year          1996; 96
w       week-of-week-based-year     number        27
W       week-of-month               number        4
E       day-of-week                 text          Tue; Tuesday; T
e/c     localized day-of-week       number/text   2; 02; Tue; Tuesday; T
F       week-of-month               number        3

a       am-pm-of-day                text          PM
h       clock-hour-of-am-pm (1-12)  number        12
K       hour-of-am-pm (0-11)        number        0
k       clock-hour-of-am-pm (1-24)  number        0

H       hour-of-day (0-23)          number        0
m       minute-of-hour              number        30
s       second-of-minute            number        55
S       fraction-of-second          fraction      978
A       milli-of-day                number        1234
n       nano-of-second              number        987654321
N       nano-of-day                 number        1234000000

V       time-zone ID                zone-id       America/Los_Angeles; Z; -08:30
z       time-zone name              zone-name     Pacific Standard Time; PST
O       localized zone-offset       offset-O      GMT+8; GMT+08:00; UTC-08:00;
X       zone-offset 'Z' for zero    offset-X      Z; -08; -0830; -08:30; -083015; -08:30:15;
x       zone-offset                 offset-x      +0000; -08; -0830; -08:30; -083015; -08:30:15;
Z       zone-offset                 offset-Z      +0000; -0800; -08:00;

Si noti che ha diversi formattatori predefiniti per i modelli più popolari. Quindi, invece di ad esempio DateTimeFormatter.ofPattern("EEE, d MMM yyyy HH:mm:ss Z", Locale.ENGLISH);, si potrebbe usare DateTimeFormatter.RFC_1123_DATE_TIME. Questo è possibile perché sono, al contrario di SimpleDateFormat, thread safe. Potresti quindi anche definirne uno tuo, se necessario.

Per un particolare formato di stringa in ingresso, non è necessario utilizzare un esplicito DateTimeFormatter: una data standard ISO 8601, come 2016-09-26T17:44:57Z, può essere analizzata direttamente con LocalDateTime#parse(text) poiché utilizza già il formattatore ISO_LOCAL_DATE_TIME. Allo stesso modo, LocalDate#parse(text) analizza una data ISO senza la componente temporale (vedi ISO_LOCAL_DATE), e ZonedDateTime#parse(text) analizza una data ISO con un offset e un fuso orario aggiunto (vedi ISO_ZONED_DATE_TIME).

Commentari (4)

Ah sì, la discussione su Java Date, di nuovo. Per trattare la manipolazione delle date usiamo Date, Calendar, GregorianCalendar, e SimpleDateFormat. Per esempio usando la tua data di gennaio come input:

Calendar mydate = new GregorianCalendar();
String mystring = "January 2, 2010";
Date thedate = new SimpleDateFormat("MMMM d, yyyy", Locale.ENGLISH).parse(mystring);
mydate.setTime(thedate);
//breakdown
System.out.println("mydate -> "+mydate);
System.out.println("year   -> "+mydate.get(Calendar.YEAR));
System.out.println("month  -> "+mydate.get(Calendar.MONTH));
System.out.println("dom    -> "+mydate.get(Calendar.DAY_OF_MONTH));
System.out.println("dow    -> "+mydate.get(Calendar.DAY_OF_WEEK));
System.out.println("hour   -> "+mydate.get(Calendar.HOUR));
System.out.println("minute -> "+mydate.get(Calendar.MINUTE));
System.out.println("second -> "+mydate.get(Calendar.SECOND));
System.out.println("milli  -> "+mydate.get(Calendar.MILLISECOND));
System.out.println("ampm   -> "+mydate.get(Calendar.AM_PM));
System.out.println("hod    -> "+mydate.get(Calendar.HOUR_OF_DAY));

Poi si può manipolare con qualcosa come:

Calendar now = Calendar.getInstance();
mydate.set(Calendar.YEAR,2009);
mydate.set(Calendar.MONTH,Calendar.FEBRUARY);
mydate.set(Calendar.DAY_OF_MONTH,25);
mydate.set(Calendar.HOUR_OF_DAY,now.get(Calendar.HOUR_OF_DAY));
mydate.set(Calendar.MINUTE,now.get(Calendar.MINUTE));
mydate.set(Calendar.SECOND,now.get(Calendar.SECOND));
// or with one statement
//mydate.set(2009, Calendar.FEBRUARY, 25, now.get(Calendar.HOUR_OF_DAY), now.get(Calendar.MINUTE), now.get(Calendar.SECOND));
System.out.println("mydate -> "+mydate);
System.out.println("year   -> "+mydate.get(Calendar.YEAR));
System.out.println("month  -> "+mydate.get(Calendar.MONTH));
System.out.println("dom    -> "+mydate.get(Calendar.DAY_OF_MONTH));
System.out.println("dow    -> "+mydate.get(Calendar.DAY_OF_WEEK));
System.out.println("hour   -> "+mydate.get(Calendar.HOUR));
System.out.println("minute -> "+mydate.get(Calendar.MINUTE));
System.out.println("second -> "+mydate.get(Calendar.SECOND));
System.out.println("milli  -> "+mydate.get(Calendar.MILLISECOND));
System.out.println("ampm   -> "+mydate.get(Calendar.AM_PM));
System.out.println("hod    -> "+mydate.get(Calendar.HOUR_OF_DAY));
Commentari (1)

Mentre si tratta della classe SimpleDateFormat, è importante ricordare che Date non è thread-safe e non è possibile condividere un singolo oggetto Date con più thread.

Inoltre c'è una grande differenza tra "m" e "M" dove le minuscole sono usate per i minuti e le maiuscole per il mese. Lo stesso con "d" e "D". Questo può causare sottili bug che spesso vengono trascurati. Vedi Javadoc o Guida alla conversione di stringhe in date in Java per maggiori dettagli.

Commentari (2)