web-dev-qa-db-fra.com

Java 8 - Problèmes de DateTimeFormatter et ISO_INSTANT avec ZonedDateTime

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?

37
asieira

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.

Formatage

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

Analyse

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

Sommaire

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.

57
JodaStephen

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.

3
Cristina

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.

1
assylias