Java: Warum ist der Date-Konstruktor veraltet und was verwende ich stattdessen?

211

Ich komme aus der C # -Welt, habe also noch keine allzu großen Erfahrungen mit Java. Mir wurde gerade von Eclipse gesagt, dass dies Dateveraltet sei:

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

Warum? Und was (besonders in Fällen wie oben) sollte stattdessen verwendet werden?

Svish
quelle
37
Ich erlebe eine ähnliche Lernkurve, auch von C # nach Java. Die andere Sache, die mich gebissen hat, ist, dass der Monat des Jahres ein 0-basiertes System ist (0 bis 11, wobei Jan. = 0 und Dez. = 11), aber die Tage des Monats 1-basiert sind (1 bis 31). Kopf hoch!
Paul Sasik
2
@ Paul Sasik, ja, aber es gibt Calendar.JANUARY Konstante zum Beispiel und eine für jeden Monat
Diogo
5
@ PaulSasik lol. Ja, dummes Java. Musste von C # zu Java wechseln und OMG den Schmerz und das Elend.
cbmeeks
8
Die snarky "lol" -Anmerkungen über Java von C # -Leuten brachten mich zum Lachen, weil .Net seine anständige Datums- / Uhrzeitbibliothek ( Noda Time ) von einem Port der exzellenten Java-Bibliothek Joda-Time erhielt .
Basil Bourque

Antworten:

97

Der spezifische DateKonstruktor ist veraltet und Calendarsollte stattdessen verwendet werden. Das JavaDocfor- Datum beschreibt, welche Konstruktoren veraltet sind und wie sie durch a ersetzt werden können Calendar.

Ruben
quelle
25
Der Kalender erfordert ein zusätzliches Objekt und wie 8 Zeilen mehr Code, um dasselbe zu tun, nämlich ein Datumsobjekt aus dem zu erstellen, was ich sagen kann. Es ist verwirrend und erscheint unnötig, wenn Sie nur ein Datum und keine zeitzonenangepasste Variable benötigen.
G_V
5
Zu Ihrer Information, die furchtbar lästig alten Datum Zeitklassen wie java.util.Date, java.util.Calendarund java.text.SimpleDateFormatsind jetzt Erbe , durch die verdrängte java.time Klassen in Java gebaut 8 und höher. Siehe Tutorial von Oracle .
Basil Bourque
250

Die java.util.DateKlasse ist nicht wirklich veraltet, nur dieser Konstruktor und einige andere Konstruktoren / Methoden sind veraltet. Es wurde abgelehnt, weil diese Art der Nutzung bei der Internationalisierung nicht gut funktioniert. Die CalendarKlasse sollte stattdessen verwendet werden:

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();

Schauen Sie sich das Datum Javadoc an:

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

BuffaloBuffalo
quelle
2
Dies sollte die beste Antwort sein. Vielen Dank.
Jordaniac89
Meinen Sie damit, dass Sie für Date keine TimeZone zuweisen können, wenn Sie sagen, dass die Internationalisierung nicht gut funktioniert? Danke
DiveInto
Mit welchem ​​Datum wurde ein String analysiert. Stattdessen müssen wir jetzt einen String mit einem Teilzeichenfolge versehen, der das Jahr, den Monat und den Tag enthält. Scheint viel zusätzlicher Aufwand für etwas zu sein, für das in den meisten Fällen keine so komplexe Logik und Methoden erforderlich sind.
G_V
1
Nur um dies hinzuzufügen, wird die Standardzeitzone berücksichtigt. Wenn wir eine andere Zeitzone angeben möchten, können wirCalendar cal = Calendar.getInstance(TimeZone.getTimeZone(<timezone id>));
Vikas Prasad
1
"Stattdessen sollte die Kalenderklasse verwendet werden" - Für Java 8 und höher sind die java.time.*Klassen die bessere Option ... wenn Sie Ihren Code ändern.
Stephen C
73

tl; dr

LocalDate.of( 1985 , 1 , 1 )

…oder…

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

Einzelheiten

Die java.util.Date, java.util.Calendarund java.text.SimpleDateFormatKlassen waren zu schnell gehetzt , wenn Java zum ersten Mal gestartet und weiterentwickelt. Die Klassen waren nicht gut gestaltet oder implementiert. Es wurden Verbesserungen versucht, daher die von Ihnen gefundenen Abwertungen. Leider sind die Verbesserungsversuche weitgehend gescheitert. Sie sollten diese Klassen insgesamt vermeiden . Sie werden in Java 8 durch neue Klassen ersetzt.

Probleme in Ihrem Code

Ein java.util.Date hat sowohl einen Datums- als auch einen Zeitabschnitt. Sie haben den Zeitanteil in Ihrem Code ignoriert. Die Date-Klasse nimmt also den Beginn des Tages, wie in der Standardzeitzone Ihrer JVM definiert, und wendet diese Zeit auf das Date-Objekt an. Die Ergebnisse Ihres Codes variieren also je nachdem, auf welchem ​​Computer er ausgeführt wird oder welche Zeitzone eingestellt ist. Wahrscheinlich nicht was du willst.

Wenn Sie nur das Datum ohne Zeitangabe möchten, z. B. für ein Geburtsdatum, möchten Sie möglicherweise kein DateObjekt verwenden. Möglicherweise möchten Sie nur eine Zeichenfolge des Datums im ISO 8601- Format von speichern YYYY-MM-DD. Oder verwenden Sie ein LocalDateObjekt aus Joda-Time (siehe unten).

Joda-Zeit

Das erste, was Sie in Java lernen sollten: Vermeiden Sie die notorisch lästigen Klassen java.util.Date & java.util.Calendar, die mit Java gebündelt sind.

Verwenden Sie , wie in der Antwort von user3277382 korrekt angegeben , entweder Joda-Time oder das neue Paket java.time. * In Java 8.

Beispielcode 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 );

Auf die Konsole werfen ...

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 );

Beim Laufen…

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 diesem Fall ist der Code für java.time fast identisch mit dem von Joda-Time .

Wir erhalten eine Zeitzone ( ZoneId) und erstellen ein Datums- / Zeitobjekt, das dieser Zeitzone ( ZonedDateTime) zugewiesen ist . Anschließend erstellen wir mithilfe des Musters " Unveränderliche Objekte" neue Datums- und Uhrzeitangaben basierend auf dem gleichen Zeitpunkt des alten Objekts (Anzahl der Nanosekunden seit der Epoche ), weisen jedoch eine andere Zeitzone zu. Zuletzt erhalten wir eine, LocalDatedie weder Tageszeit noch Zeitzone hat. Beachten Sie jedoch, dass die Zeitzone bei der Bestimmung dieses Datums gilt (ein neuer Tag bricht in Oslo früher an als beispielsweise in 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();

Über java.time

Das java.time- Framework ist in Java 8 und höher integriert. Diese Klassen verdrängen die lästigen alten Legacy - Datum-Zeit - Klassen wie java.util.Date, Calendar, & SimpleDateFormat.

Das Joda-Time- Projekt, das sich jetzt im Wartungsmodus befindet , empfiehlt die Migration zu den Klassen java.time .

Weitere Informationen finden Sie im Oracle-Lernprogramm . Suchen Sie im Stapelüberlauf nach vielen Beispielen und Erklärungen. Die Spezifikation ist JSR 310 .

Sie können java.time- Objekte direkt mit Ihrer Datenbank austauschen . Verwenden Sie einen JDBC-Treiber, der mit JDBC 4.2 oder höher kompatibel ist . Keine Notwendigkeit für Zeichenfolgen, keine Notwendigkeit für java.sql.*Klassen.

Woher bekomme ich die java.time-Klassen?

Das ThreeTen-Extra- Projekt erweitert java.time um zusätzliche Klassen. Dieses Projekt ist ein Testfeld für mögliche zukünftige Ergänzungen von java.time. Sie können einige nützliche Klassen hier wie finden Interval, YearWeek, YearQuarter, und mehr .

Basil Bourque
quelle
5
Requisiten für die tatsächliche Erwähnung der Grundursache und die Abdeckung aller verfügbaren Alternativen.
Mabi
Dies ist die richtige Antwort und die Kalenderklasse sollte vermieden werden. ThreeTen ist die beste Option
ant2009
11

Ein Grund dafür, dass der Konstruktor veraltet ist, ist, dass die Bedeutung des Jahresparameters nicht Ihren Erwartungen entspricht. Der Javadoc sagt:

Ab JDK Version 1.1, ersetzt durch Calendar.set(year + 1900, month, date).

Beachten Sie, dass das Feld Jahr die Anzahl der Jahre seitdem ist 1900, sodass Ihr Beispielcode höchstwahrscheinlich nicht das tut, was Sie von ihm erwarten. Und das ist der Punkt.

Im Allgemeinen unterstützt die DateAPI nur den modernen westlichen Kalender, verfügt über eigenwillig spezifizierte Komponenten und verhält sich inkonsistent, wenn Sie Felder festlegen.

Die Calendarund GregorianCalendarAPIs sind besser als Dateund die Joda-Time-APIs von Drittanbietern wurden allgemein als die besten angesehen. In Java 8 haben sie die java.timePakete eingeführt, und diese sind jetzt die empfohlene Alternative.

Stephen C.
quelle
Diese Lösung funktioniert hervorragend, außer dass sie keine Nullzeit für Stunden, Minuten, Sekunden und Millisekunden verwendet, wie dies beim neuen Datum (Jahr, Monat, Tag) der Fall war. Wir brauchen also etwas wie: Calendar cal = Calendar.getInstance (); cal.clear (); cal.set (Jahr + 1900, Monat, Datum); Datum dateRepresentation = cal.getTime ();
Joel Richard Koett
11

Ich bin auf diese Frage als Duplikat einer neueren Frage gestoßen, in der gefragt wurde, wie der nicht veraltete Weg zu Dateeinem bestimmten Jahr, Monat und Tag aussehen soll.

Die Antworten hier sagen bisher, dass die CalendarKlasse verwendet werden soll, und das war wahr, bis Java 8 herauskam. Ab Java 8 ist dies jedoch die Standardmethode:

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

Und wenn Sie wirklich wirklich einen brauchen java.util.Date, können Sie die Vorschläge in dieser Frage verwenden .

Weitere Informationen finden Sie in der API oder in den Tutorials für Java 8.

Kevin Workman
quelle
7

Bitte beachten Sie, dass dies nicht Calendar.getTime()deterministisch ist, da der Tageszeitteil standardmäßig die aktuelle Zeit ist.

Versuchen Sie zum Reproduzieren, den folgenden Code einige Male auszuführen:

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

Ausgabe zB:

Sun Mar 07 10:46:21 CET 2010

Wenn Sie einige Minuten später genau denselben Code ausführen, erhalten Sie Folgendes:

Sun Mar 07 10:57:51 CET 2010

Während die set()entsprechenden Felder gezwungen werden, Werte zu korrigieren, wird die Systemzeit für die anderen Felder verloren. (Oben mit Sun jdk6 & jdk7 getestet)

Tijn Porcelijn
quelle
2

Da der Date-Konstruktor veraltet ist, können Sie diesen Code ausprobieren.

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
Divyanshu Kumar
quelle
1
Diese schrecklichen Klassen sind jetzt Vermächtnis, da sie vor Jahren durch die in JSR 310 definierten java.time- Klassen ersetzt wurden. Vorschläge Dateund Calendar2019 sind schlechte Ratschläge.
Basil Bourque
@BasilBourque danke für den Vorschlag, ich freue mich darauf, ihn bald zu aktualisieren.
Divyanshu Kumar
1

Sie können eine Methode wie new Date(year,month,date)in Ihrem Code mithilfe der CalendarKlasse erstellen .

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();
}

Es wird genauso funktionieren wie der veraltete Konstruktor von Date

Sudhanshu Dubey
quelle
1
Diese furchtbar lästigen Klassen wurden vor Jahren durch die modernen Java.time- Klassen ersetzt, speziell Instantund ZonedDateTime.
Basil Bourque
Die obige Lösung sollte 1900 zum Jahr hinzufügen und NICHT 1 vom Monat subtrahieren. Außerdem sollte cal.clear () nach der Zeile Calendar.getInstance () ausgeführt werden, damit die Stunden / Minuten / Sekunden / Millisekunden auf Null gesetzt werden (as Sie tun dies im Date-Konstruktor.
Joel Richard Koett
1

Der Datumskonstruktor erwartet Jahre im Format von Jahren seit 1900, nullbasierte Monate, einbasierte Tage und setzt Stunden / Minuten / Sekunden / Millisekunden auf Null.

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

Wenn wir also den Kalenderersatz (nullbasierte Jahre, nullbasierte Monate, einbasierte Tage) für den veralteten Datumskonstruktor verwenden, benötigen wir Folgendes:

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

Oder verwenden Sie Java 1.8 (mit nullbasiertem Jahr und einbasierten Monaten und Tagen):

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

Hier sind gleiche Versionen von Datum, Kalender und 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));
Joel Richard Koett
quelle
1
Diese beiden schrecklichen Klassen wurden vor Jahren durch die in JSR 310 definierten Java.time- Klassen ersetzt.
Basil Bourque
Die Antwort wurde aktualisiert, um Datums-, Kalender- und Java 1.8-Versionen anzuzeigen.
Joel Richard Koett
0
new GregorianCalendar(1985, Calendar.JANUARY, 1).getTime();

(der Weg vor Java-8)

Curtis Yallop
quelle
Es ist wahrscheinlich das Beste, was Sie ohne java.time tun können. Aber auch vor Java 8 können Sie java.time verwenden. Es gibt einen Backport: die ThreeTen Backport-Bibliothek .
Ole VV