Je m'attends donc à ce que ce code fonctionne sous le nouveau package de date/heure Java 8 car il ne fait que convertir un ZonedDateTime donné en chaîne et inversement en utilisant la même instance intégrée de DateTimeFormatter (ISO_INSTANT ):
ZonedDateTime now = ZonedDateTime.now();
System.out.println(ZonedDateTime.parse(
now.format(DateTimeFormatter.ISO_INSTANT),
DateTimeFormatter.ISO_INSTANT));
Mais apparemment, ce n'est pas le cas:
Exception in thread "main" Java.time.format.DateTimeParseException: Text '2014-09-01T19:37:48.549Z' could not be parsed: Unable to obtain ZonedDateTime from TemporalAccessor: {MilliOfSecond=549, NanoOfSecond=549000000, MicroOfSecond=549000, InstantSeconds=1409600268},ISO of type Java.time.format.Parsed
at Java.time.format.DateTimeFormatter.createError(DateTimeFormatter.Java:1918)
at Java.time.format.DateTimeFormatter.parse(DateTimeFormatter.Java:1853)
at Java.time.ZonedDateTime.parse(ZonedDateTime.Java:597)
J'ai déjà vu cette entrée, mais cela ne m'a pas aidé car j'ai besoin d'un objet ZonedDateTime et non local et aussi parce que j'ai déjà installé 8u20: Impossible d'obtenir ZonedDateTime de TemporalAccessor en utilisant DateTimeFormatter et ZonedDateTime dans = Java 8
Quelqu'un a une idée de ce qui se passe ici?
Le formateur ISO_INSTANT
Est documenté ici - "Il s'agit d'un formateur de cas spécial destiné à permettre une forme lisible par un humain d'un instant". En tant que tel, ce formateur est destiné à être utilisé avec un Instant
et non un ZonedDateTime
.
Lors du formatage, ISO_INSTANT
Peut formater tout objet temporel pouvant fournir ChronoField.INSTANT_SECONDS
Et ChronoField.NANO_OF_SECOND
. Instant
et ZonedDateTime
peuvent fournir ces deux champs, donc les deux fonctionnent:
// works with Instant
Instant instant = Instant.now();
System.out.println(DateTimeFormatter.ISO_INSTANT.format(instant));
// works with ZonedDateTime
ZonedDateTime zdt = ZonedDateTime.now();
System.out.println(zdt.format(DateTimeFormatter.ISO_INSTANT));
// example output
2014-09-02T08:05:23.653Z
Lors de l'analyse, ISO_INSTANT
Ne produira que ChronoField.INSTANT_SECONDS
Et ChronoField.NANO_OF_SECOND
. Un Instant
peut être construit à partir de ces deux champs, mais ZonedDateTime
nécessite également un ZoneId
:
Pour analyser un ZonedDateTime
, il est essentiel qu'un fuseau horaire ZoneId
soit présent. Le fuseau horaire peut être (a) analysé à partir de la chaîne, ou (b) spécifié dans le formateur (à l'aide de JDK 8u20):
// option a - parsed from the string
DateTimeFormatter f = DateTimeFormatter.ISO_DATE_TIME;
ZonedDateTime zdt = ZonedDateTime.parse("2014-09-02T08:05:23.653Z", f);
// option b - specified in the formatter - REQUIRES JDK 8u20 !!!
DateTimeFormatter f = DateTimeFormatter.ISO_INSTANT.withZone(ZoneId.systemDefault());
ZonedDateTime zdt = ZonedDateTime.parse("2014-09-02T08:05:23.653Z", f);
Voir la documentation pour ISO_ZONED_DATE_TIME
, ISO_OFFSET_DATE_TIME
et ISO_DATE_TIME
(chacun de ces trois peut être utilisé pour analyser un ZonedDateTime
sans spécifier withZone()
).
Le formateur ISO_INSTANT
Est un formateur de cas spécial conçu pour fonctionner avec Instant
. Si vous utilisez un ZonedDateTime
, vous devez utiliser un formateur différent, tel que ISO_DATE_TIME
Ou ISO_ZONED_DATE_TIME
.
Je ne suis pas sûr, mais cela pourrait être un bogue dans Java 8. Peut-être qu'il était destiné à se comporter de cette façon, mais je pense que la solution de contournement que je vais vous proposer devrait être le comportement par défaut (quand aucun ZoneId n'est spécifié, prenez simplement le système par défaut):
ZonedDateTime now = ZonedDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ISO_INSTANT
.withZone(ZoneId.systemDefault());
System.out
.println(ZonedDateTime.parse(now.format(formatter), formatter));
Il y a un bug similaire qui a été corrigé dans OpenJDK: JDK-8033662 - mais c'est seulement similaire, pas exactement le même.
Je ne sais pas si c'est le comportement attendu ou non (c'est probablement le cas) mais techniquement le ISO_INSTANT
le formateur n'inclut pas de fuseau horaire. Si vous essayez avec un DateTimeFormatter.ISO_ZONED_DATE_TIME
à la place, vous obtiendrez ce que vous attendez.