Est java.le temps ne parvient pas à analyser la fraction de seconde?


Avec la première version de Java 8 (b132) sur Mac OS X (Mavericks), ce code utilise le nouveau java.le paquet de temps fonctionne:

String input = "20111203123456"; 
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "yyyyMMddHHmmss");
LocalDateTime localDateTime = LocalDateTime.parse( input, formatter );

Rendu:

2011-12-03T12:34:56

Mais lorsque j'ajoute " SS " pour la fraction de seconde (et "55" en entrée), comme spécifié dans la classe DateTimeFormatter doc , une exception est levée:

java.time.format.DateTimeParseException: Text '2011120312345655' could not be parsed at index 0

Le document indique que le mode Strict est utilisé par défaut et nécessite le même nombre de caractères de format que les chiffres d'entrée. Je suis donc confus pourquoi ce code échoue:

String input = "2011120312345655"; 
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "yyyyMMddHHmmssSS");
LocalDateTime localDateTime = LocalDateTime.parse( input, formatter );

Un autre exemple utilisant l'exemple de la documentation ("978") (échoue):

String input = "20111203123456978"; 
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "yyyyMMddHHmmssSSS");
LocalDateTime localDateTime = LocalDateTime.parse( input, formatter );

Cet exemple fonctionne, en ajoutant un point décimal (mais je ne trouve pas une telle exigence dans le doc):

String input = "20111203123456.978"; 
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "yyyyMMddHHmmss.SSS");
LocalDateTime localDateTime = LocalDateTime.parse( input, formatter );

Rend:

localDateTime: 2011-12-03T12:34:56.978

Omettre le caractère point de la chaîne d'entréeou le format provoque un échec.

Échoue:

String input = "20111203123456.978"; 
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "yyyyMMddHHmmssSSS");
LocalDateTime localDateTime = LocalDateTime.parse( input, formatter );

Échoue:

String input = "20111203123456978"; 
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "yyyyMMddHHmmss.SSS");
LocalDateTime localDateTime = LocalDateTime.parse( input, formatter );
Author: calvinf, 2014-03-23

2 answers

Correction d'un bug dans Java 9

Ce problème a déjà été signalé dans JDK-bug-log. Stephen Colebourne mentionne comme solution de contournement suivante:

DateTimeFormatter dtf = 
  new DateTimeFormatterBuilder()
  .appendPattern("yyyyMMddHHmmss")
  .appendValue(ChronoField.MILLI_OF_SECOND, 3)
  .toFormatter();

Remarque: Cette solution de contournement ne couvre pas votre cas d'utilisation de seulement deux symboles de motif SS. Un ajustement peut uniquement consister à utiliser d'autres champs comme MICRO_OF_SECOND (6 fois SSSSSS) ou NANO_OF_SECOND (9 fois SSSSSSSSS). Pour deux chiffres de fraction, voir ma mise à jour ci-dessous.

@ PeterLawrey Sur la signification de symbole de motif" S " voir cette documentation:

Fraction: Produit le champ nano-de-seconde sous forme de fraction de seconde. La valeur nano-de-seconde a neuf chiffres, donc le nombre de motif lettres est de 1 à 9. Si elle est inférieure à 9, alors le nano-de-seconde la valeur est tronquée, seuls les chiffres les plus significatifs étant sortie. Lors de l'analyse en mode strict, le nombre de chiffres analysés doit faites correspondre le nombre de lettres de motif. Lors de l'analyse en mode indulgent, le le nombre de chiffres analysés doit être au moins le nombre de lettres de motif, jusqu'à 9 chiffres.

Nous voyons donc que S représente n'importe quelle fraction de seconde (y compris la nanoseconde), pas seulement les millisecondes. De plus, la partie fractionnaire ne prend pas bien pour le moment dans l'analyse des valeurs adjacentes, malheureusement.

MODIFIER:

En arrière-plan voici quelques remarques sur l'analyse des valeurs adjacentes. Tant que les champs sont séparés par des littéraux comme un point décimal ou une partie temporelle séparateurs (côlon), l'interprétation des champs de texte à analyser n'est pas difficile car le parseur sait alors facilement quand arrêter c'est à dire lorsque le champ la partie est terminée et lorsque le champ suivant commence. Par conséquent, l'analyseur JSR-310 peut traiter la séquence de texte si vous spécifiez un point décimal.

Mais si vous avez une séquence de chiffres adjacents couvrant plusieurs champs, des difficultés d'implémentation surviennent. Afin de faire savoir à l'analyseur quand un champ s'arrête dans le texte il est nécessaire d'instruire l'analyseur à l'avance qu'un champ donné est représenté par une largeur fixe de chiffres caractères. Cela fonctionne avec toutes les méthodes appendValue(...) qui supposent des représentations numériques.

Malheureusement, JSR-310 n'a pas bien réussi à le faire également avec la partie fractionnaire (appendFraction(...)). Si vous recherchez le mot-clé "adjacent" dans le javadoc de la classe DateTimeFormatterBuilder, vous constatez que cette fonctionnalité n'est réalisée que par appendValue(...)-methods. Notez que la spécification pour la lettre de motif S est légèrement différente mais délègue en interne à appendFraction()-méthode. Je suppose que nous devrons au moins nous évanouir jusqu'à Java 9 (comme indiqué dans JDK-bug-log, ou plus tard???) jusqu'à ce que les parties de fraction puissent également gérer l'analyse des valeurs adjacentes.


Mise à Jour de 2015-11-25:

Le code suivant utilisant seulement deux chiffres de fraction ne fonctionne pas et jette un DateTimeParseException.

DateTimeFormatter dtf = 
  new DateTimeFormatterBuilder()
  .appendPattern("yyyyMMddHHmmssSS")
  .appendValue(ChronoField.MILLI_OF_SECOND, 2)
  .toFormatter();
String input = "2011120312345655"; 
LocalDateTime.parse(input, dtf); // abort

La solution de contournement

String input = "2011120312345655"; 
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSS");
Date d = sdf.parse(input);
System.out.println(d.toInstant()); // 2011-12-03T12:34:56.055Z

Ne fonctionne pas car SimpleDateFormat interprète mal la fraction (voir sortie, 55 ms au lieu de 550 ms).

Que reste-t-il en tant que solution soit en attente d'un long délai jusqu'à Java 9 (ou plus tard?) ou écrire votre propre hack ou utiliser des bibliothèques tierces comme solution.

Solution basée sur un hack sale:

String input = "2011120312345655"; 
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
int len = input.length();
LocalDateTime ldt = LocalDateTime.parse(input.substring(0, len - 2),  dtf);
int millis = Integer.parseInt(input.substring(len - 2)) * 10;
ldt = ldt.plus(millis, ChronoUnit.MILLIS);
System.out.println(ldt); // 2011-12-03T12:34:56.550

La Solution à l'aide de Joda-Time:

String input = "2011120312345655"; 
DateTimeFormatter dtf = DateTimeFormat.forPattern("yyyyMMddHHmmssSS");
System.out.println(dtf.parseLocalDateTime(input)); // 2011-12-03T12:34:56.550

Solution à l'aide de ma bibliothèque Time4J:

String input = "2011120312345655"; 
ChronoFormatter<PlainTimestamp> f = 
  ChronoFormatter.ofTimestampPattern("yyyyMMddHHmmssSS", PatternType.CLDR, Locale.ROOT);
System.out.println(f.parse(input)); // 2011-12-03T12:34:56.550

Mise à Jour de 2016-04-29:

Comme les gens peuvent le voir via le problème JDK mentionné ci - dessus, il est maintenant marqué comme résolu-pour Java 9.

 29
Author: Meno Hochschild, 2017-02-01 21:21:46

Voici un algorithme qui ajuste l'ordre des zéros de fin qui sont classiquement renvoyés à partir de la date formatée String.

/**
 * Takes a Date and provides the format whilst compensating for the mistaken representation of sub-second values.
 * i.e. 2017-04-03-22:46:19.000991 -> 2017-04-03-22:46:19.991000
 * @param pDate Defines the Date object to format.
 * @param pPrecision Defines number of valid subsecond characters contained in the system's response.
 * */
private static final String subFormat(final Date pDate, final SimpleDateFormat pSimpleDateFormat, final int pPrecision) throws ParseException {
    // Format as usual.
    final String lString        = pSimpleDateFormat.format(pDate);
    // Count the number of characters.
    final String lPattern       = pSimpleDateFormat.toLocalizedPattern();
    // Find where the SubSeconds are.
    final int    lStart         = lPattern.indexOf('S');
    final int    lEnd           = lPattern.lastIndexOf('S');
    // Ensure they're in the expected format.
    for(int i = lStart; i <= lEnd; i++) { if(lPattern.charAt(i) != 'S') {
        // Throw an Exception; the date has been provided in the wrong format.
       throw new ParseException("Unable to process subseconds in the provided form. (" + lPattern + ").", i);
    } }
    // Calculate the number of Subseconds. (Account for zero indexing.)
    final int lNumSubSeconds = (lEnd - lStart) + 1;
    // Fetch the original quantity.
    String lReplaceString = lString.substring(lStart + (lNumSubSeconds - pPrecision), lStart + lNumSubSeconds);
    // Append trailing zeros.
    for(int i = 0; i < lNumSubSeconds - pPrecision; i++) { lReplaceString += "0"; }
    // Return the String.
    return lString.substring(0, lStart) + lReplaceString;
}
 0
Author: Cawfree, 2017-04-04 10:48:50