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
?
Oui, vous devriez certainement utiliser le cadre Java.time autant que possible.
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.
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.
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.
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);
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() ;
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();