2017-09-16 9 views
2

tl; dr

Échec. Mais le même moment avec le même décalage dans un ZonedDateTime fonctionne.OffsetDateTime échoue avec le formateur localisé avec FormatStyle de LONG ou FULL

Pourquoi?

Détails

lors de la location java.time localisent automatiquement la représentation de chaîne d'un OffsetDateTime via DateTimeFormatter.ofLocalizedDateTime, appelant format fonctionne que si le formatter porte un FormatStyle de SHORT ou MEDIUM. Mais lorsque le formateur porte LONG ou FULL, un DateTimeException est levé. Pourtant ZonedDateTime réussit en utilisant le même moment avec le même offset. Pourquoi?

DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG) ; 

OffsetDateTime odt = OffsetDateTime.now(ZoneId.systemDefault()) ; 
ZonedDateTime zdt = odt.atZoneSameInstant(odt.getOffset()) ; // Generate a `ZonedDateTime` with same moment and same offset as the `OffsetDateTime`. 

// Succeeds. 
String outputZdt = zdt.format(f) ; 
System.out.println("outputZdt: " + outputZdt) ; 

// Fails. Throws exception. 
if (false) { 
String outputOdt = odt.format(f) ; // Throws exception. 
System.out.println("outputOdt: " + outputOdt) ; 
} 

Voir ceci code run live at IdeOne.com.

Lors de l'exécution ...

Le bon.

outputZdt 16 Septembre, 2017 08:42:14 Z

mauvais.

Exception in thread "main" java.time.DateTimeException: Unable to extract value: class java.time.OffsetDateTime 
    at java.time.format.DateTimePrintContext.getValue(DateTimePrintContext.java:282) 
    at java.time.format.DateTimeFormatterBuilder$ZoneTextPrinterParser.format(DateTimeFormatterBuilder.java:3682) 
    at java.time.format.DateTimeFormatterBuilder$CompositePrinterParser.format(DateTimeFormatterBuilder.java:2179) 
    at java.time.format.DateTimeFormatterBuilder$LocalizedPrinterParser.format(DateTimeFormatterBuilder.java:4347) 
    at java.time.format.DateTimeFormatterBuilder$CompositePrinterParser.format(DateTimeFormatterBuilder.java:2179) 
    at java.time.format.DateTimeFormatter.formatTo(DateTimeFormatter.java:1746) 
    at java.time.format.DateTimeFormatter.format(DateTimeFormatter.java:1720) 
    at java.time.OffsetDateTime.format(OffsetDateTime.java:1674) 
    at Ideone.main(Main.java:28) 

J'ai écrit le cœur de ce code pour contourner l'exception levée, odt.atZoneSameInstant(odt.getOffset()). Puis j'ai réalisé, pourquoi java.time ne fait-il pas la même chose en interne? Pourquoi le OffsetDateTime ne parvient pas à formater lorsqu'un ZonedDateTime avec le même moment et le même décalage réussit? Pourquoi devrais-je besoin de faire cette conversion de OffsetDateTime à ZonedDateTime?

➟ Ce comportement d'échec de mise en forme OffsetDateTime est-il un bug ou une fonctionnalité?

Je déposerais un rapport de bogue, mais je veux m'assurer que je ne comprends pas quelque chose.

Répondre

1

Débogage le code, j'ai trouvé que le formatter finit dans this line (ligne de grepcode est pas exactement le même nombre de mon installation de JDK, mais le code est):

ZoneId zone = context.getValue(TemporalQueries.zoneId()); 

Il essaie d'extraire la zone, en utilisant la requête intégrée TemporalQueries.zoneId(). Selon javadoc, cette requête retourne null si l'objet temporel est un OffsetDateTime:

Ainsi, un ZonedDateTime retournera le résultat de getZone(), mais un OffsetDateTime retournera null.

Vous pouvez confirmer cela en appelant odt.query(TemporalQueries.zoneId()) - il retourne null en effet.

plus tard, le résultat de cette requête est checked by a DateTimePrintContext:

R result = temporal.query(query); 
if (result == null && optional == 0) { 
    throw new DateTimeException("Unable to extract value: " + temporal.getClass()); 
} 

Comme le result est null, il jette l'exception.


En fait, en essayant d'obtenir le nom de zone (modèle z) à partir d'un OffsetDateTime lancera une exception:

// java.time.DateTimeException: Unable to extract value: class java.time.OffsetDateTime 
DateTimeFormatter.ofPattern("z").format(OffsetDateTime.now()); 

Parce que ce modèle se retrouve dans la ligne problématique décrite ci-dessus.

Et vérifier le style de date pour tous les paramètres régionaux, en utilisant getLocalizedDateTimePattern:

// did this for all locales 
DateTimeFormatterBuilder.getLocalizedDateTimePattern(FormatStyle.LONG, FormatStyle.LONG, 
    IsoChronology.INSTANCE, locale); 

Je n'ai pas vérifié tous, mais la plupart d'entre eux ont le modèle minuscule z, ce qui signifie qu'il échouera pour la plupart (si pas tous) les locales.


pas directement lié, mais comme vous appelez atZoneSameInstant avec un ZoneOffset en tant que paramètre, vous pouvez simplement appeler odt.toZonedDateTime() à la place.

3

On dirait que le bug Javadoc a été signalé here. Dans l'exemple fourni, ils utilisent LocalDateTime, mais le comportement est le même.

L'utilisation de FormatStyle.LONG et FormatStyle.FULLseems d'exiger un ZoneId qui OffsetDateTime n'a pas

S'il vous plaît en revue java.time améliorations javadoc pour mettre en évidence une commune malentendu sur le formatage des éléments qui nécessitent un fuseau horaire dans plus du temps .

Lorsque vous utilisez la mise en forme spécifique locale, il peut fonctionner si les paramètres régionaux mise en forme ne nécessite pas de fuseau horaire ou échouer si les paramètres régionaux mise en forme nécessite un fuseau horaire et un fuseau horaire n'est pas fourni.

qui est pourquoi they clarified the javadoc mentionner

* The {@code FULL} and {@code LONG} styles typically require a time-zone. 
* When formatting using these styles, a {@code ZoneId} must be available, 
* either by using {@code ZonedDateTime} or {@link DateTimeFormatter#withZone}. 

Vous pouvez créer le DateTimeFormatter avec l » ZoneOffset du OffsetDateTime.

DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG) 
            .withZone(odt.getOffset()); 

auquel cas le OffsetDateTime sera converti en un ZonedDateTime avant le formatage se produit.