web-dev-qa-db-fra.com

Convertir Java.util.Date en quel type "Java.time"?

J'ai un Java.util.Date objet, ou un Java.util.Calendar objet. Comment puis-je convertir cela au bon type dans le framework Java.time ?

J'ai entendu dire que nous devrions maintenant faire l'essentiel de notre logique métier avec les types Java.time. Lorsque je travaille avec un ancien code qui n'a pas encore été mis à jour pour Java.time, je dois pouvoir effectuer des conversions dans les deux sens. Quels types correspondent à Java.util.Date ou Java.util.Calendar?

32
Basil Bourque

Oui, vous devriez certainement utiliser le cadre Java.time autant que possible.

Évitez les anciennes classes de date-heure

Les anciennes classes date-heure, y compris Java.util.Date , Java.util.Calendar , et Java.text.SimpleTextFormat Et autres se sont avérés mal conçus, déroutants et gênants . Évitez-les là où vous le pouvez. Mais lorsque vous devez interagir avec ces anciens types, vous pouvez convertir entre l'ancien et le nouveau.

Lisez la suite pour une introduction de base, un peu trop simplifiée, pour vous orienter dans les va-et-vient entre les anciennes et les nouvelles classes date-heure.

Java.time

Le framework Java.time est défini par JSR 310 , inspiré par le très réussi Joda-Time et étendue par le projet ThreeTen-Extra . La majeure partie de la fonctionnalité a été rétroportée vers Java 6 & 7 dans le projet ThreeTen-Backport , avec une adaptation supplémentaire pour Android dans le projet ThreeTenABP .

À quel type Java.time correspond Java.util.Date ? Eh bien, un objet Java.util.Date Représente essentiellement un moment sur la timeline en UTC, une combinaison d'une date et d'une heure. Nous pouvons traduire cela en plusieurs types dans Java.time. Chacun est discuté ci-dessous. Notez que de nouvelles méthodes ont été ajoutées aux anciennes classes date-heure pour faciliter les conversions.

diagram of converting from Java.util.Date/.Calendar through ZonedDateTime (or OffsetDateTime) to the three <code>Local…</code> types

Instant

Le bloc de construction dans Java.time est un Instant , un moment sur la chronologie dans [~ # ~] utc [~ # ~] avec une résolution de nanosecondes .

En règle générale, vous devriez faire une grande partie de votre logique métier en UTC. Dans un tel travail, Instant sera utilisé fréquemment. Passez autour des objets Instant, en appliquant un fuseau horaire uniquement pour la présentation à un utilisateur. Lorsque vous faites devez appliquer un décalage ou un fuseau horaire, utilisez les types décrits ci-dessous.

De Java.util.Date À Instant

Étant donné que Instant et Java.util.Date Sont tous les deux sur la timeline en UTC, nous pouvons facilement passer d'un Java.util.Date à un Instant. L'ancienne classe a gagné une nouvelle méthode, Java.util.Date::toInstant .

Instant instant = myUtilDate.toInstant();

Vous pouvez aller dans l'autre sens, d'un Instant à un Java.util.Date. Mais vous risquez de perdre des informations sur la fraction de seconde. Un Instant suit nanosecondes , jusqu'à neuf chiffres après la décimale, comme 2016-01-23T12:34:56.123456789Z. Java.util.Date et .Calendar sont limités à millisecondes , pour un maximum de trois chiffres après la décimale, comme 2016-01-23T12:34:56.123Z. Dans cet exemple, le passage de Instant à Date signifie la troncature de 456789.

Java.util.Date myUtilDate = Java.util.Date.from(instant);

De Java.util.Calendar À Instant

Qu'en est-il d'un Java.util.Calendar au lieu d'un Java.util.Date? En interne à l'objet Calendar, la date-heure est suivie en tant que nombre millisecondes à partir de la date-heure de référence Epoch du premier moment de 1970 en UTC (1970-01-01T00:00:00.0Z). Cette valeur peut donc être convertie facilement en Instant.

Instant instant = myUtilCalendar.toInstant() ;

De Java.util.GregorianCalendar À ZonedDateTime

Encore mieux, si votre objet Java.util.Calendar est en fait un Java.util.GregorianCalendar , vous pouvez facilement accéder directement à un ZonedDateTime . Cette approche présente l'avantage de conserver les informations de fuseau horaire intégrées.

Downcast de l'interface de Calendar vers la classe concrète de GregorianCalendar. Appelez ensuite les méthodes toZonedDateTime et from pour aller et venir.

if (myUtilCalendar instanceof GregorianCalendar) {
    GregorianCalendar gregCal = (GregorianCalendar) myUtilCalendar; // Downcasting from the interface to the concrete class.
    ZonedDateTime zdt = gregCal.toZonedDateTime();  // Create `ZonedDateTime` with same time zone info found in the `GregorianCalendar`
}

Aller dans l'autre sens…

Java.util.Calendar myUtilCalendar = Java.util.GregorianCalendar.from(zdt); // Produces an instant of `GregorianCalendar` which implements `Calendar` interface.

Comme indiqué ci-dessus, sachez que vous risquez de perdre des informations sur la fraction de seconde . Les nanosecondes dans le type Java.time (ZonedDateTime) sont tronquées à millisecondes dans le .Calendar/.GregorianCalendar.

OffsetDateTime

A partir d'un Instant, nous pouvons appliquer un décalage par rapport à UTC pour passer à une heure d'horloge murale pour une localité. Un décalage est un nombre d'heures, et éventuellement de minutes et de secondes, devant [~ # ~] utc [~ # ~] (vers l'est) ou derrière UTC (vers l'ouest). La classe ZoneOffset représente cette idée. Le résultat est un objet OffsetDateTime .

ZoneOffset offset = ZoneOffset.of("-04:00"); 
OffsetDateTime odt = OffsetDateTime.ofInstant(instant, zoneOffset);

Vous pouvez aller dans l'autre sens, d'un OffsetDateTime à un Java.util.Date. Extrayez un Instant puis procédez comme nous l'avons vu dans le code ci-dessus. Comme indiqué ci-dessus, toute nanosecondes est tronquée à millisecondes ( perte de données ).

Java.util.Date myUtilDate = Java.util.Date.from(odt.toInstant());

ZonedDateTime

Mieux encore, appliquez un fuseau horaire complet . Un fuseau horaire est un décalage plus des règles de gestion des anomalies telles que l'heure d'été (DST) .

L'application d'un ZoneId vous donne un objet ZonedDateTime . Utilisez un nom de fuseau horaire correct (continent/région). N'utilisez jamais les abréviations de 3 à 4 lettres couramment utilisées telles que EST ou IST car elles ne sont ni standardisées ni uniques.

ZoneId zoneId = ZoneId.of("America/Montreal");
ZonedDateTime zdt = ZonedDateTime.ofInstant(instant, zoneId);

Vous pouvez aller dans l'autre sens, d'un ZonedDateTime à un Java.util.Date. Extrayez un Instant puis procédez comme nous l'avons vu dans le code ci-dessus. Comme indiqué ci-dessus, toute nanosecondes est tronquée à millisecondes ( perte de données ).

Java.util.Date myUtilDate = Java.util.Date.from( zdt.toInstant() );

Et nous avons vu plus haut qu'un ZonedDateTime peut être converti en GregorianCalendar.

LocalDate

Parfois, vous pouvez souhaiter une valeur de date uniquement, sans heure et sans fuseau horaire. Pour cela, utilisez un objet Java.time.LocalDate .

Voir cette question pour plus de discussion, Convertir Java.util.Date en Java.time.LocalDate , en particulier cette réponse écrite par l'homme principal derrière le invention de Joda-Time et Java.time.

La clé est de passer par un ZonedDateTime (tel que généré dans le code ci-dessus). Nous avons besoin d'un fuseau horaire pour déterminer une date. La date varie dans le monde, avec un nouveau jour qui se lève plus tôt dans l'est. Par exemple, après minuit à Paris, c'est un nouveau jour alors qu'il est encore "hier" à Montréal. Ainsi, alors qu'un LocalDate ne contient pas contient un fuseau horaire, un fuseau horaire est requis pour déterminer un LocalDate.

LocalDate localDate = zdt.toLocalDate();

La conversion dans l'autre sens de LocalDate vers une date-heure signifie inventer une heure. Vous pouvez choisir n'importe quel moment de la journée qui a du sens dans votre scénario d'entreprise. Pour la plupart des gens, le premier moment de la journée est logique. Vous pourriez être tenté de coder en dur ce premier moment comme l'heure 00:00:00.0. Dans certains fuseaux horaires, cette heure peut ne pas être valide comme premier moment en raison de l'heure d'été (DST) ou d'autres anomalies. Laissez donc Java.time déterminer l'heure correcte avec un appel à atStartOfDay .

ZonedDateTime zdt = localDate.atStartOfDay(zoneId);

LocalTime

En de rares occasions, vous souhaiterez peut-être uniquement une heure sans date et sans fuseau horaire. Ce concept est représenté par la classe LocalTime . Comme discuté ci-dessus avec LocalDate, nous avons besoin d'un fuseau horaire pour déterminer un LocalTime même si l'objet LocalTime ne contient pas (ne "se souvient") pas de ce fuseau horaire. Donc, encore une fois, nous passons par un objet ZonedDateTime obtenu à partir d'un Instant comme vu ci-dessus.

LocalTime localTime = zdt.toLocalTime();

LocalDateTime

Comme pour les deux autres types Local…, Un LocalDateTime n'a pas de fuseau horaire ni de décalage attribué. En tant que tel vous pouvez rarement utiliser ceci. Il vous donne une idée approximative d'une date-heure mais est pas un point sur la chronologie. Utilisez ceci si vous voulez dire une date générale et une heure qui pourraient être appliquées à un fuseau horaire.

Par exemple, "Noël commence cette année" serait 2016-12-25T00:00:00.0. Notez l'absence de décalage ou de fuseau horaire dans cette représentation textuelle d'un LocalDateTime. Noël commence plus tôt à Delhi en Inde qu'à Paris en France, et plus tard encore à Montréal Québec Canada. L’application de chacun des fuseaux horaires de ces zones donnerait un moment différent sur la chronologie.

LocalDateTime ldt = zdt.toLocalDateTime();

enter image description here

96
Basil Bourque