web-dev-qa-db-fra.com

Java: Pourquoi le constructeur Date est-il déconseillé et que dois-je utiliser à la place?

Je viens du monde C #, donc je ne connais pas encore très bien Java. Eclipse vient de me dire que la Date était obsolète.

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

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

182
Svish

Le constructeur spécifique Date est obsolète et un calendrier doit être utilisé à la place. Le JavaDoc pour Date décrit quels constructeurs sont déconseillés et comment les remplacer à l’aide d’un calendrier.

90
Ruben

La classe Java.util.Date n'est pas réellement obsolète, mais ce constructeur, ainsi que quelques autres constructeurs/méthodes, sont déconseillés. Il est devenu obsolète car ce type d’utilisation ne fonctionne pas bien avec l’internationalisation. La classe Calendar devrait ê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

222
BuffaloBuffalo

tl; dr

LocalDate.of( 1985 , 1 , 1 )

…ou…

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

Détails

Les classes Java.util.Date, Java.util.Calendar et Java.text.SimpleDateFormat ont été lancées trop rapidement lors du lancement et de l'évolution de Java. Les classes n'étaient pas bien conçues ni implémentées. Des améliorations ont été tentées, d’où les déprécations trouvées. Malheureusement, les tentatives d'amélioration ont en grande partie échoué. Vous devriez éviter complètement ces classes. Ils sont supplantés en Java 8 par de nouvelles classes.

Problèmes dans votre code

Un fichier Java.util.Date comporte à la fois une date et une heure. Vous avez ignoré la partie heure dans votre code. Ainsi, la classe Date prendra le début du jour tel que défini par le fuseau horaire par défaut de votre JVM et appliquera cette heure à l’objet Date. Ainsi, les résultats de votre code varient en fonction de la machine sur laquelle il s'exécute ou du fuseau horaire défini. Probablement pas ce que tu veux.

Si vous souhaitez uniquement la date, sans la partie heure, par exemple pour une date de naissance, vous ne souhaitez peut-être pas utiliser d'objet Date. Vous souhaiterez peut-être ne stocker qu'une chaîne de date, au format ISO 8601 de YYYY-MM-DD. Ou utilisez un objet LocalDate de Joda-Time (voir ci-dessous).

Joda-Time

Première chose à apprendre en Java: Évitez les classes notoirement gênantes Java.util.Date & Java.util.Calendar 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 );

Décharger pour consoler…

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

Quand couru…

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 pour Java.time est presque identique à celui de Joda-Time

Nous obtenons un fuseau horaire ( ZoneId ) et construisons un objet date-heure affecté à ce fuseau horaire ( ZonedDateTime ). Ensuite, en utilisant le modèle Objets immuables , nous créons de nouvelles dates-dates basées sur le même instant de l’ancien objet (nombre de nanosecondes depuis Epoch ) mais attribué à un autre fuseau horaire. Enfin, nous obtenons un LocalDate qui n'a ni heure ni fuseau horaire bien que vous remarquiez que le fuseau horaire s'applique lors de la détermination de cette date (un nouveau jour se lève plus tôt dans Oslo que dans 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 cadre Java.time est intégré à Java 8 et versions ultérieures. Ces classes supplantent les anciennes classes gênantes héritées _ date-heure telles que Java.util.Date , Calendar _, & SimpleDateFormat .

Le projet Joda-Time , désormais en mode { mode maintenance }, conseille la migration vers les classes Java.time .

Pour en savoir plus, consultez le Oracle Tutorial . 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 une version ultérieure. Pas besoin de chaînes, pas besoin de Java.sql.* classes.

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 fournie.
    • Java 9 ajoute quelques fonctionnalités et corrections mineures.
  • 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
    • Les versions ultérieures d'Android regroupent des implémentations des classes Java.time.
    • Pour les anciens Android (<26), le projet ThreeTenABP s’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 d'éventuels ajouts à Java.time. Vous pouvez y trouver des classes utiles telles que Interval _, YearWeek , YearQuarter _ et plus _.

61
Basil Bourque

Je suis tombé sur cette question comme un duplicata de une question plus récente qui demandait quelle était la manière non dépréciée d’obtenir Date à une année, un mois et un jour spécifiques.

Jusqu'à présent, les réponses recommandent d'utiliser la classe Calendar, et c'était le cas jusqu'à la sortie de Java 8. Mais à partir de Java 8, la méthode standard consiste à:

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

Et puis, si vous avez vraiment besoin d'un Java.util.Date, vous pouvez utiliser les suggestions de cette question .

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

11
Kevin Workman

L'une des raisons pour lesquelles le constructeur est obsolète est que la signification du paramètre année n'est pas ce à quoi vous pourriez vous attendre. Le javadoc dit:

À partir de la version 1.1 du JDK, remplacé par Calendar.set(year + 1900, month, date).

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

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

Les API Calendar et GregorianCalendar sont meilleures 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 packages Java.time, qui constituent désormais l’alternative recommandée.

9
Stephen C

Veuillez noter que Calendar.getTime() est non déterministe dans le sens où le paramètre heure partie est défini par défaut sur 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 exacte du même code quelques minutes plus tard donne:

Sun Mar 07 10:57:51 CET 2010

Ainsi, alors que set() force les champs correspondants à corriger les valeurs, il laisse fuir l'heure système pour les autres champs. (Testé ci-dessus avec Sun jdk6 et jdk7)

7
Tijn Porcelijn

Date lui-même n'est pas obsolète. C'est juste que beaucoup de ses méthodes sont. Voir ici pour plus de détails .

Utilisez Java.util.Calendar à la place.

6
Mike Thomsen

La plupart des développeurs Java utilisent actuellement le package tiers Joda-Time . Il est largement considéré que sa mise en œuvre est bien meilleure. 

Java 8 aura toutefois un nouveau package Java.time. * . Voir cet article, Présentation de la nouvelle API de date et heure pour JDK 8 .

5
davesbrain

Semblable à ce que suggère binnyb, vous pouvez envisager d'utiliser la nouvelle méthode Calendar> GregorianCalendar. Voir ces docs plus récents:

http://download.Oracle.com/javase/6/docs/api/Java/util/GregorianCalendar.html

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

(la manière pré-Java-8)

0
Curtis Yallop

Le constructeur Date attend des années au format d'années depuis 1900, des mois basés sur zéro et définit les heures/minutes/secondes/millisecondes sur zéro.

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

Donc, en utilisant le remplacement de calendrier (années à base zéro, mois à base zéro, jours à un) pour le constructeur Date déconseillé, 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 et des mois et des jours uniques):

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

Voici les mêmes versions 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));
0

Vous pouvez créer une méthode similaire à new Date(year,month,date) dans votre code en utilisant Calendar class. 

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 

0
Sudhanshu Dubey