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.
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:
"Gennaio" è il mese del testo completo, quindi usa il pattern MMMM
per esso
"2" è il giorno breve del mese, quindi usa il pattern d
per esso.
"2010" è l'anno a 4 cifre, quindi usa il modello yyyy
.
"!-->
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.
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
).
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));
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.