Java: Pourquoi le constructeur Date est-il obsolète et que dois-je utiliser à la place?

212

Je viens du monde C #, donc pas encore très expérimenté avec Java. Eclipse vient de me dire que Datec'était obsolète:

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

Pourquoi? Et quoi (en particulier dans les cas comme ci-dessus) devrait être utilisé à la place?

Svish
la source
37
J'expérimente une courbe d'apprentissage similaire, passant également de C # à Java. L'autre chose qui m'a mordu est que le mois de l'année est un système basé sur 0 (0 à 11 où Jan. = 0 et Dec. = 11) mais les jours du mois sont basés sur 1 (1 à 31). Attention à celui-là!
Paul Sasik
2
@Paul Sasik, oui, mais il y a la constante Calendar.JANUARY par exemple, et une pour chaque mois
Diogo
5
@PaulSasik lol. Ouais, stupide Java. J'ai dû passer de C # à Java et OMG la douleur et la misère.
cbmeeks
8
Les remarques sordides "lol" à propos de Java de la part de C # m'ont fait rire parce que .Net a obtenu sa bibliothèque de date-heure décente ( Noda Time ) à partir d'un port de l'excellente bibliothèque Java Joda-Time .
Basil Bourque

Réponses:

98

Le Dateconstructeur spécifique est obsolète et Calendardoit être utilisé à la place. leJavaDoc pour date décrit les constructeurs sont déconseillés et comment les remplacer en utilisant un Calendar.

Ruben
la source
25
Le calendrier nécessite un objet supplémentaire et comme 8 lignes de code supplémentaires pour faire la même chose qui est de créer un objet Date à partir de ce que je peux dire. C'est déroutant et semble inutile lorsque vous avez juste besoin d'une date et non d'une variable ajustée au fuseau horaire.
G_V
5
Pour info, les anciennes classes de date-heure terriblement gênantes telles que java.util.Date, java.util.Calendaret java.text.SimpleDateFormatsont désormais héritées , supplantées par les classes java.time intégrées à Java 8 et versions ultérieures. Voir Tutoriel par Oracle .
Basil Bourque
250

La java.util.Dateclasse n'est pas vraiment obsolète, juste ce constructeur, ainsi que quelques autres constructeurs / méthodes sont obsolètes. Il est obsolète car ce type d'utilisation ne fonctionne pas bien avec l'internationalisation. La Calendarclasse doit être utilisée à la place:

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

Jetez un oeil à la date Javadoc:

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

BuffaloBuffalo
la source
2
Cela devrait être la meilleure réponse. Merci.
jordaniac89
En disant "ne fonctionne pas bien avec l'internationalisation", voulez-vous dire que pour Date, vous ne pouvez pas lui affecter TimeZone? Merci
DiveInto
Quelle date a été d'analyser une chaîne, nous devons donc maintenant sous-chaîne une chaîne qui contient l'année, le mois et le jour? On dirait beaucoup de tracas supplémentaires pour quelque chose qui, dans la plupart des cas, n'a pas besoin d'une logique et de méthodes aussi complexes.
G_V
1
Juste pour ajouter, cela prendra en considération le fuseau horaire par défaut. Si nous voulons spécifier un autre fuseau horaire, nous pouvons utiliserCalendar cal = Calendar.getInstance(TimeZone.getTimeZone(<timezone id>));
Vikas Prasad
1
"La classe Calendar doit être utilisée à la place" - Pour Java 8 et versions ultérieures, les java.time.*classes sont la meilleure option ... si vous modifiez votre code.
Stephen C
73

tl; dr

LocalDate.of( 1985 , 1 , 1 )

…ou…

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

Détails

Les java.util.Date, java.util.Calendaret les java.text.SimpleDateFormatclasses ont été précipités trop rapidement lorsque Java d' abord lancé et évolué. Les cours n'étaient pas bien conçus ou mis en œuvre. Des améliorations ont été tentées, d'où les dépréciations que vous avez trouvées. Malheureusement, les tentatives d'amélioration ont largement échoué. Vous devez éviter ces cours complètement . Ils sont supplantés en Java 8 par de nouvelles classes.

Problèmes dans votre code

Un java.util.Date a à la fois une date et une heure. Vous avez ignoré la portion de temps dans votre code. Ainsi, la classe Date prendra le début de la journée tel que défini par le fuseau horaire par défaut de votre JVM et appliquera cette heure à l'objet Date. Les résultats de votre code varient donc en fonction de la machine sur laquelle il s'exécute ou du fuseau horaire défini. Probablement pas ce que vous voulez.

Si vous voulez juste la date, sans la partie heure, comme pour une date de naissance, vous ne voudrez peut-être pas utiliser un Dateobjet. Vous voudrez peut-être stocker juste une chaîne de la date, au format ISO 8601 de YYYY-MM-DD. Ou utilisez unLocalDate objet de Joda-Time (voir ci-dessous).

Joda-Time

Première chose à apprendre en Java: évitez les classes java.util.Date et java.util.Calendar notoirement gênantes fournies avec Java.

Comme indiqué correctement dans la réponse de user3277382 , utilisez Joda-Time ou le nouveau package java.time. * Dans Java 8.

Exemple de code dans 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 sur 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 );

Lors de l'exécution ...

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

Dans ce cas, le code de java.time est presque identique à celui de Joda-Time .

Nous obtenons un fuseau horaire (ZoneId ) et construisons un objet date-heure assigné à ce fuseau horaire ( ZonedDateTime). Ensuite, en utilisant le modèle Objets immuables , nous créons de nouvelles dates-heures basées sur le même instant de l'ancien objet (nombre de nanosecondes depuis l' époque ) mais assigné à un autre fuseau horaire. Enfin, nous obtenons un LocalDatequi n'a pas d'heure ni de fuseau horaire bien que le fuseau horaire s'applique lors de la détermination de cette date (un nouveau jour se lève plus tôt à Oslo qu'à New York par exemple).

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

À propos de java.time

Le framework java.time est intégré à Java 8 et versions ultérieures. Ces classes supplantent les anciens gênants hérités des classes date-heure tels que java.util.Date, Calendar, et SimpleDateFormat.

Le projet Joda-Time , désormais en mode maintenance , conseille la migration vers java.time classes .

Pour en savoir plus, consultez le didacticiel Oracle . Et recherchez Stack Overflow pour de nombreux exemples et explications. La spécification est JSR 310 .

Vous pouvez échanger des objets java.time directement avec votre base de données. Utilisez un pilote JDBC compatible avec JDBC 4.2 ou version ultérieure. Pas besoin de cordes, pas besoin dejava.sql.* cours.

Où obtenir les classes java.time?

  • Java SE 8 , Java SE 9 , Java SE 10 et versions ultérieures
    • Intégré.
    • Une partie de l'API Java standard avec une implémentation groupée.
    • Java 9 ajoute quelques fonctionnalités et correctifs mineurs.
  • Java SE 6 et Java SE 7
    • Une grande partie de la fonctionnalité java.time est rétroportée vers Java 6 et 7 dans ThreeTen-Backport .
  • Android
    • Versions ultérieures des implémentations de bundles Android des classes java.time.
    • Pour les versions antérieures d'Android (<26), le projet ThreeTenABP adapte ThreeTen-Backport (mentionné ci-dessus). Voir Comment utiliser ThreeTenABP… .

Le projet ThreeTen-Extra étend java.time avec des classes supplémentaires. Ce projet est un terrain d'essai pour de futurs ajouts possibles à java.time. Vous trouverez peut - être des classes utiles ici, comme Interval, YearWeek, YearQuarteret plus .

Basil Bourque
la source
5
Des accessoires pour mentionner la cause profonde et fournir une couverture de toutes les alternatives disponibles.
mabi
C'est la bonne réponse et la classe Calendar doit être évitée. ThreeTen est la meilleure option
ant2009
11

L'une des raisons pour lesquelles le constructeur est obsolète est que la signification du paramètre year n'est pas celle que vous attendez. Le javadoc dit:

Depuis JDK version 1.1, remplacé par Calendar.set(year + 1900, month, date).

Notez que le champ année est le nombre d'années depuis 1900 , donc votre exemple de code ne fera probablement pas ce que vous attendez de lui. Et c'est le point.

En général, l' DateAPI ne prend en charge que le calendrier occidental moderne, possède des composants spécifiés de manière idiosyncrasique et se comporte de manière incohérente si vous définissez des champs.

Les API Calendaret GregorianCalendarsont mieux que Date, et les API Joda-time tierces étaient généralement considérées comme les meilleures. Dans Java 8, ils ont introduit les java.timepackages, et ceux-ci sont désormais l'alternative recommandée.

Stephen C
la source
Cette solution fonctionne très bien, sauf qu'elle n'utilise pas le temps zéro pendant des heures-minutes-secondes-millisecondes comme le faisait la nouvelle date (année, mois, jour). Nous avons donc besoin de quelque chose comme: Calendar cal = Calendar.getInstance (); cal.clear (); cal.set (année + 1900, mois, date); Date dateRepresentation = cal.getTime ();
Joel Richard Koett
11

Je suis tombé sur cette question en double d' une question plus récente qui demandait quel était le moyen non obsolète d'obtenir un Dateà une année, un mois et un jour spécifiques.

Jusqu'à présent, les réponses disent d'utiliser la Calendarclasse, et c'était vrai jusqu'à la sortie de Java 8. Mais à partir de Java 8, la façon standard de le faire est:

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

Et puis si vous en avez vraiment besoin java.util.Date, vous pouvez utiliser les suggestions de cette question .

Pour plus d'informations, consultez l'API ou les didacticiels pour Java 8.

Kevin Workman
la source
7

Veuillez noter que cela Calendar.getTime()n'est pas déterministe dans le sens où la partie heure par défaut est l'heure actuelle.

Pour reproduire, essayez d'exécuter le code suivant plusieurs fois:

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

Sortie par exemple:

Sun Mar 07 10:46:21 CET 2010

L'exécution du même code exact quelques minutes plus tard donne:

Sun Mar 07 10:57:51 CET 2010

Ainsi, alors qu'il set()force les champs correspondants à corriger les valeurs, il fait perdre du temps système aux autres champs. (Testé ci-dessus avec Sun jdk6 & jdk7)

Tijn Porcelijn
la source
2

Comme le constructeur Date est obsolète, vous pouvez essayer ce code.

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
la source
1
Ces classes terribles sont maintenant héritées, ayant été remplacées il y a des années par les classes java.time définies dans JSR 310. Suggérer Dateet Calendaren 2019 est un mauvais conseil.
Basil Bourque
@BasilBourque merci pour la suggestion, j'ai hâte de la mettre à jour bientôt.
Divyanshu Kumar
1

Vous pouvez créer une méthode comme new Date(year,month,date)dans votre code en utilisant la Calendarclasse.

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

Cela fonctionnera comme le constructeur obsolète de Date

Sudhanshu Dubey
la source
1
Ces classes terriblement gênantes ont été remplacées il y a des années par les classes java.time modernes , spécifiquement Instantet ZonedDateTime.
Basil Bourque
La solution ci-dessus doit ajouter 1900 à l'année, et ne doit PAS soustraire 1 du mois, et doit également exécuter cal.clear () après la ligne Calendar.getInstance (), afin que les heures / minutes / secondes / millisecondes soient mises à zéro (comme ils le font dans le constructeur Date).
Joel Richard Koett
1

Le constructeur Date attend les années au format années depuis 1900, mois à base zéro, jours à base unique, et définit les heures / minutes / secondes / millisecondes à zéro.

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

Donc, en utilisant le remplacement du calendrier (années de base zéro, mois de base zéro, jours de base un) pour le constructeur Date obsolète, nous avons besoin de quelque chose comme:

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

Ou en utilisant Java 1.8 (qui a une année à base zéro et des mois et jours à base unique):

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

Voici des versions égales de Date, Calendar et 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
la source
1
Ces deux classes terribles ont été remplacées il y a des années par les classes java.time définies dans JSR 310.
Basil Bourque
Réponse mise à jour pour afficher les versions Date, Calendrier et Java 1.8.
Joel Richard Koett
0
new GregorianCalendar(1985, Calendar.JANUARY, 1).getTime();

(façon pré-Java-8)

Curtis Yallop
la source
C'est probablement le mieux que vous puissiez faire sans java.time. Cependant, même avant Java 8, vous pouvez utiliser java.time, il existe un backport: la bibliothèque ThreeTen Backport .
Ole VV