Java: Perché il costruttore Date è deprecato e cosa devo usare invece?


212

Vengo dal mondo C #, quindi non ho ancora molta esperienza con Java. Mi è stato appena detto da Eclipse che Dateera deprecato:

Person p = new Person();
p.setDateOfBirth(new Date(1985, 1, 1));

Perché? E cosa (specialmente in casi come sopra) dovrebbe essere usato invece?


37
Sto sperimentando una curva di apprendimento simile, passando anche da C # a Java. L'altra cosa che mi ha morso è che il mese dell'anno è un sistema basato su 0 (da 0 a 11, dove gennaio = 0 e dicembre = 11), ma i giorni del mese sono basati su 1 (da 1 a 31). Testa su quello!
Paul Sasik,

2
@Paul Sasik, sì, ma c'è una costante Calendar.JANUARY per esempio, e una per ogni mese
Diogo,

5
@PaulSasik lol. Sì, stupido Java. Ho dovuto passare da C # a Java e OMG per il dolore e la miseria.
cbmeeks,


8
Le sgradevoli osservazioni "lol" su Java delle persone C # mi hanno fatto ridere perché .Net ha ottenuto la sua discreta libreria di data e ora ( Noda Time ) da una porta dell'eccellente libreria Java Joda-Time .
Basil Bourque,

Risposte:


98

Il Datecostruttore specifico è obsoleto e Calendardeve essere utilizzato al suo posto. La dataJavaDoc for descrive quali costruttori sono obsoleti e come sostituirli utilizzando a .Calendar


25
Calendar richiede un oggetto extra e come 8 righe di codice in più per fare la stessa cosa che è creare un oggetto Date da quello che posso dire. È confuso e sembra inutile quando hai solo bisogno di una data e non di una variabile aggiustata per il fuso orario.
G_V

5
Cordiali saluti, le vecchie classi data-ora terribilmente fastidiose come java.util.Date, java.util.Calendare java.text.SimpleDateFormatora sono legacy , soppiantate dalle classi java.time integrate in Java 8 e versioni successive. Vedi Tutorial di Oracle .
Basil Bourque,

250

La java.util.Dateclasse non è in realtà deprecata, solo quel costruttore, insieme a un paio di altri costruttori / metodi sono deprecati. È stato deprecato perché questo tipo di utilizzo non funziona bene con l'internazionalizzazione. La Calendarclasse dovrebbe essere usata invece:

Calendar cal = Calendar.getInstance();
cal.set(Calendar.YEAR, 1988);
cal.set(Calendar.MONTH, Calendar.JANUARY);
cal.set(Calendar.DAY_OF_MONTH, 1);
Date dateRepresentation = cal.getTime();

Dai un'occhiata alla data Javadoc:

http://download.oracle.com/javase/6/docs/api/java/util/Date.html


2
Questa dovrebbe essere la migliore risposta. Grazie.
jordaniac89,

Dicendo "non funziona bene con l'internazionalizzazione", vuoi dire che per Date non puoi assegnare TimeZone per questo? Grazie
DiveInto

In quale data è stata analizzata una stringa, quindi ora dobbiamo sottostringa di una stringa che contiene l'anno, il mese e il giorno? Sembra una seccatura extra per qualcosa che nella maggior parte dei casi non ha bisogno di una logica così complessa e di metodi aggiunti ad esso.
G_V

1
Solo per aggiungere, questo prenderà in considerazione il fuso orario predefinito. Se vogliamo specificare qualsiasi altro fuso orario, possiamo usareCalendar cal = Calendar.getInstance(TimeZone.getTimeZone(<timezone id>));
Vikas Prasad il

1
"Dovrebbe essere usata la classe Calendar" - Per Java 8 e versioni successive, le java.time.*classi sono l'opzione migliore ... se stai cambiando il tuo codice.
Stephen C,

73

tl; dr

LocalDate.of( 1985 , 1 , 1 )

…o…

LocalDate.of( 1985 , Month.JANUARY , 1 )

Dettagli

I java.util.Date, java.util.Calendare java.text.SimpleDateFormatle classi sono stati portati troppo in fretta quando Java lanciata e si è evoluta. Le classi non sono state ben progettate o implementate. Sono stati tentati miglioramenti, quindi le deprecazioni che hai trovato. Sfortunatamente, i tentativi di miglioramento fallirono ampiamente. Dovresti evitare del tutto queste classi . Sono sostituiti in Java 8 da nuove classi.

Problemi nel tuo codice

Un java.util.Date ha sia una data che una porzione di ora. Hai ignorato la parte temporale nel tuo codice. Quindi la classe Date prenderà l'inizio del giorno come definito dal fuso orario predefinito della tua JVM e applicherà quell'ora all'oggetto Date. Quindi i risultati del tuo codice varieranno a seconda della macchina che esegue o del fuso orario impostato. Probabilmente non quello che vuoi.

Se si desidera solo la data, senza la parte dell'ora, ad esempio per una data di nascita, è possibile che non si desideri utilizzare un Dateoggetto. Potresti voler memorizzare solo una stringa della data, nel formato ISO 8601 di YYYY-MM-DD. Oppure usa un LocalDateoggetto da Joda-Time (vedi sotto).

Joda-Time

Prima cosa da imparare in Java: evitare le notoriamente problematiche classi java.util.Date & java.util.Calendar in bundle con Java.

Come indicato correttamente nella risposta dall'utente3277382 , utilizzare Joda-Time o il nuovo pacchetto java.time. * In Java 8.

Codice di esempio in Joda-Time 2.3

DateTimeZone timeZoneNorway = DateTimeZone.forID( "Europe/Oslo" );
DateTime birthDateTime_InNorway = new DateTime( 1985, 1, 1, 3, 2, 1, timeZoneNorway );

DateTimeZone timeZoneNewYork = DateTimeZone.forID( "America/New_York" );
DateTime birthDateTime_InNewYork = birthDateTime_InNorway.toDateTime( timeZoneNewYork ); 

DateTime birthDateTime_UtcGmt = birthDateTime_InNorway.toDateTime( DateTimeZone.UTC );

LocalDate birthDate = new LocalDate( 1985, 1, 1 );

Dump per console ...

System.out.println( "birthDateTime_InNorway: " + birthDateTime_InNorway );
System.out.println( "birthDateTime_InNewYork: " + birthDateTime_InNewYork );
System.out.println( "birthDateTime_UtcGmt: " + birthDateTime_UtcGmt );
System.out.println( "birthDate: " + birthDate );

Quando eseguito ...

birthDateTime_InNorway: 1985-01-01T03:02:01.000+01:00
birthDateTime_InNewYork: 1984-12-31T21:02:01.000-05:00
birthDateTime_UtcGmt: 1985-01-01T02:02:01.000Z
birthDate: 1985-01-01

java.time

In questo caso il codice per java.time è quasi identico a quello di Joda-Time .

Otteniamo un fuso orario ( ZoneId) e costruiamo un oggetto data-ora assegnato a quel fuso orario ( ZonedDateTime). Quindi, utilizzando il modello Immutable Objects , creiamo nuove date-time basate sullo stesso istante dell'oggetto vecchio (conteggio dei nanosecondi dal dall'epoca ) ma assegnato ad altro fuso orario. Infine otteniamo un LocalDateorario che non ha né l'ora del giorno né il fuso orario, sebbene si noti che il fuso orario si applica quando si determina quella data (ad esempio un nuovo giorno sorge prima a Oslo che a New York ).

ZoneId zoneId_Norway = ZoneId.of( "Europe/Oslo" );
ZonedDateTime zdt_Norway = ZonedDateTime.of( 1985 , 1 , 1 , 3 , 2 , 1 , 0 , zoneId_Norway );

ZoneId zoneId_NewYork = ZonedId.of( "America/New_York" );
ZonedDateTime zdt_NewYork = zdt_Norway.withZoneSameInstant( zoneId_NewYork );

ZonedDateTime zdt_Utc = zdt_Norway.withZoneSameInstant( ZoneOffset.UTC );  // Or, next line is similar.
Instant instant = zdt_Norway.toInstant();  // Instant is always in UTC.

LocalDate localDate_Norway = zdt_Norway.toLocalDate();

Informazioni su java.time

The java.time framework è integrato in Java 8 e successive. Queste classi soppiantare la vecchia fastidiosi legacy classi data-time come java.util.Date, Calendar, e SimpleDateFormat.

Il progetto Joda-Time , ora in modalità manutenzione , consiglia la migrazione alle classi java.time .

Per saperne di più, consulta il tutorial Oracle . E cerca Stack Overflow per molti esempi e spiegazioni. La specifica è JSR 310 .

Puoi scambiare java.time oggetti direttamente con il tuo database. Utilizzare un driver JDBC conforme a JDBC 4.2 o successivo. Non c'è bisogno di stringhe, non c'è bisogno di java.sql.*classi.

Dove ottenere le classi java.time?

Il progetto ThreeTen-Extra estende java.time con classi aggiuntive. Questo progetto è un banco di prova per possibili aggiunte future a java.time. Si possono trovare alcune classi utili, come per esempio Interval, YearWeek, YearQuartere altro .


5
Puntelli per menzionare effettivamente la causa principale e fornire copertura di tutte le alternative disponibili.
mabi,

Questa è la risposta corretta e la classe Calendar dovrebbe essere evitata. ThreeTen è l'opzione migliore
ant2009

11

Una ragione per cui il costruttore è deprecato è che il significato del parametro year non è quello che ti aspetteresti. Il javadoc dice:

A partire dalla versione 1.1 di JDK, sostituita da Calendar.set(year + 1900, month, date).

Si noti che il campo dell'anno è il numero di anni da allora 1900 , quindi il codice di esempio molto probabilmente non farà ciò che ci si aspetta che faccia. E questo è il punto.

In generale, l' DateAPI supporta solo il moderno calendario occidentale, ha componenti specificati in modo idiosincratico e si comporta in modo incoerente se si impostano i campi.

Le API Calendare GregorianCalendarsono migliori di Date, e le API di Joda di terze parti erano generalmente ritenute le migliori. In Java 8, hanno introdotto i java.timepacchetti e questi sono ora l'alternativa consigliata.


Questa soluzione funziona alla grande, tranne per il fatto che non usa zero-time per ore-minuti-secondi-millisecondi come ha fatto la nuova data (anno, mese, giorno). Quindi abbiamo bisogno di qualcosa del tipo: Calendar cal = Calendar.getInstance (); cal.clear (); cal.set (anno + 1900, mese, data); Date dateRepresentation = cal.getTime ();
Joel Richard Koett,

11

Mi sono imbattuto in questa domanda come un duplicato di una nuova domanda che chiedeva quale fosse il modo non deprecato di ottenere un Dateanno, un mese e un giorno specifici.

Le risposte qui finora dicono di usare la Calendarclasse, e questo era vero fino alla pubblicazione di Java 8. Ma a partire da Java 8, il modo standard per farlo è:

LocalDate localDate = LocalDate.of(1985, 1, 1);

E poi se hai davvero bisogno di un java.util.Date, puoi usare i suggerimenti in questa domanda .

Per ulteriori informazioni, consulta l'API o le esercitazioni per Java 8.


7

Si noti che Calendar.getTime()non è deterministico nel senso che la parte dell'ora del giorno passa automaticamente all'ora corrente.

Per riprodurre, prova a eseguire il seguente codice un paio di volte:

Calendar c = Calendar.getInstance();
c.set(2010, 2, 7); // NB: 2 means March, not February!
System.err.println(c.getTime());

Uscita ad es .:

Sun Mar 07 10:46:21 CET 2010

L'esecuzione dello stesso codice un paio di minuti dopo produce:

Sun Mar 07 10:57:51 CET 2010

Pertanto, mentre set()obbliga i campi corrispondenti a correggere i valori, perde tempo di sistema per gli altri campi. (Testato sopra con Sun jdk6 e jdk7)




2

Poiché il costruttore Date è obsoleto, puoi provare questo codice.

import java.util.Calendar;

  Calendar calendar = Calendar.getInstance();
     calendar.set(Calendar.HOUR_OF_DAY, 6);// for 6 hour
     calendar.set(Calendar.MINUTE, 0);// for 0 min
     calendar.set(Calendar.SECOND, 0);// for 0 sec
     calendar.set(1996,0,26);// for Date [year,month(0 to 11), date]

    Date date = new Date(calendar.getTimeInMillis());// calendar gives long value

    String mConvertedDate = date.toString();// Fri Jan 26 06:00:00 GMT+05:30 1996

1
Queste classi terribili sono ormai ereditate, essendo state soppiantate anni fa dalle classi java.time definite in JSR 310. SuggerireDate e Calendarnel 2019 è un cattivo consiglio.
Basil Bourque,

@BasilBourque grazie per il suggerimento, non vedo l'ora di aggiornarlo presto.
Divyanshu Kumar il


1

Puoi creare un metodo proprio come new Date(year,month,date)nel tuo codice usando Calendarclass.

private Date getDate(int year,int month,int date){
    Calendar cal = Calendar.getInstance();
    cal.set(Calendar.YEAR, year);
    cal.set(Calendar.MONTH, month-1);
    cal.set(Calendar.DAY_OF_MONTH, day);
    return cal.getTime();
}

Funzionerà proprio come il costruttore deprecato di Date


1
Queste classi terribilmente fastidiose furono soppiantate anni fa dalle moderne classi java.time , in particolare Instante ZonedDateTime.
Basil Bourque,

La soluzione sopra dovrebbe aggiungere 1900 all'anno e NON sottrarre 1 dal mese e dovrebbe anche eseguire cal.clear () dopo la riga Calendar.getInstance (), in modo che le ore / minuti / secondi / millisecondi vengano azzerate (come lo fanno nel costruttore Date).
Joel Richard Koett,

1

Il costruttore Date si aspetta anni nel formato di anni dal 1900, mesi a base zero, giorni a una base e imposta ore / minuti / secondi / millisecondi a zero.

Date result = new Date(year, month, day);

Quindi, usando la sostituzione del calendario (anni a base zero, mesi a base zero, giorni a base singola) per il costruttore della data deprecata, abbiamo bisogno di qualcosa di simile:

Calendar calendar = Calendar.getInstance();
calendar.clear(); // Sets hours/minutes/seconds/milliseconds to zero
calendar.set(year + 1900, month, day);
Date result = calendar.getTime();

O usando Java 1.8 (che ha un anno a base zero e mesi e giorni a una base):

Date result = Date.from(LocalDate.of(year + 1900, month + 1, day).atStartOfDay(ZoneId.systemDefault()).toInstant());

Ecco le versioni uguali di Date, Calendar e Java 1.8:

int year = 1985; // 1985
int month = 1; // January
int day = 1; // 1st

// Original, 1900-based year, zero-based month, one-based day
Date date1 = new Date(year - 1900, month - 1, day);

// Calendar, zero-based year, zero-based month, one-based day
Calendar calendar = Calendar.getInstance();
calendar.clear(); // Sets hours/minutes/seconds/milliseconds to zero
calendar.set(year, month - 1, day);
Date date2 = calendar.getTime();

// Java-time back to Date, zero-based year, one-based month, one-based day
Date date3 = Date.from(LocalDate.of(year, month, day).atStartOfDay(ZoneId.systemDefault()).toInstant());

SimpleDateFormat format = new SimpleDateFormat("yyyy-MMM-dd HH:mm:ss.SSS");

// All 3 print "1985-Jan-01 00:00:00.000"
System.out.println(format.format(date1));
System.out.println(format.format(date2));
System.out.println(format.format(date3));

1
Entrambe queste classi terribili furono soppiantate anni fa dalle classi java.time definite in JSR 310.
Basil Bourque

Risposta aggiornata per mostrare le versioni di Data, Calendario e Java 1.8.
Joel Richard Koett

0
new GregorianCalendar(1985, Calendar.JANUARY, 1).getTime();

(il modo pre-Java-8)


È probabilmente il meglio che puoi fare senza java.time. Tuttavia, anche prima di Java 8, è possibile utilizzare java.time, esiste un backport: la libreria ThreeTen Backport .
Ole VV,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.