web-dev-qa-db-fra.com

SimpleDateFormat n'analyse pas correctement les millisecondes

Contexte:

Dans ma table de base de données, j'ai deux horodatages

timeStamp1 = 2011-08-23 14:57:26.662
timeStamp2 = 2011-08-23 14:57:26.9

Lorsque je fais un "ORDER BY TIMESTAMP ASC", timeStamp2 est considéré comme le plus grand horodatage (ce qui est correct).

Exigence: J'ai besoin d'obtenir la différence de ces horodatages (timeStamp2 - timeStamp1)

Mon implémentation:

public static String timeDifference(String now, String prev) {
    try {
        final Date currentParsed = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").parse(now);
        final Date previousParsed = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").parse(prev);
        long difference = currentParsed.getTime() - previousParsed.getTime();
        return "" + difference;
    } catch (ParseException e) {
        return "Unknown";
    }
}

La réponse aurait dû être de 238 ms, mais la valeur renvoyée est -653 ms. Je ne suis pas sûr de ce que je fais mal. Aucune suggestion?

26
dkulkarni

Je ne suis pas tout à fait sûr, mais le JavaDoc déclare ceci:

Pour l'analyse, le nombre de lettres de modèle est ignoré sauf s'il est nécessaire de séparer deux champs adjacents.

Cela indique que les millisecondes de 2011-08-23 14:57:26.9 serait analysé comme 9 au lieu de 900. L'ajout de zéros de fin peut fonctionner: 2011-08-23 14:57:26.900.

12
Thomas

Le format que vous analysez et le format utilisé ne correspondent pas. Vous vous attendez à un champ à trois chiffres et ne fournissez qu'un seul chiffre. Ça prend 9 et suppose que vous voulez dire 009 quand ce que vous voulez c'est 900. Les formats de date sont compliqués et lorsque vous prouvez des dates dans un format différent, cela peut les analyser différemment.

La documentation indique que S signifie le nombre de millisecondes et le nombre dans ce champ est 9, donc il se comporte correctement.


EDIT: Cet exemple peut aider

final SimpleDateFormat ss_SSS = new SimpleDateFormat("ss.SSS");
ss_SSS.setTimeZone(TimeZone.getTimeZone("GMT"));
for (String text : "0.9, 0.456, 0.123456".split(", ")) {
  System.out.println(text + " parsed as \"ss.SSS\" is "
      + ss_SSS.parse(text).getTime() + " millis");
}

impressions

0.9 parsed as "ss.SSS" is 9 millis
0.456 parsed as "ss.SSS" is 456 millis
0.123456 parsed as "ss.SSS" is 123456 millis
17
Peter Lawrey

J'ai eu le même problème avec l'heure trop précise de mes fichiers journaux avec 6 millisecondes numériques. Le temps d'analyse a donné jusqu'à 16 minutes de différence! WTF?

16-JAN-12 04.00.00.999999 PM GMT --> 16 Jan 2012 04:16:39 GMT

Changer le nombre de chiffres a réduit la différence erronée et grâce à ce fil, j'ai pu identifier le problème:

16-JAN-12 04.00.00.99999 PM GMT --> 16 Jan 2012 04:01:39 GMT
16-JAN-12 04.00.00.9999 PM GMT --> 16 Jan 2012 04:00:09 GMT
16-JAN-12 04.00.00.999 PM GMT --> 16 Jan 2012 04:00:00 GMT

Comme SimpleDateFormat ne gère en interne que 3 chiffres, j'ai supprimé l'inutile avec une petite expression régulière (en ignorant les erreurs d'arrondi, en travaillant de 1 à n chiffres):

str = str.replaceAll("(\\.[0-9]{3})[0-9]*( [AP]M)", "$1$2");

Merci à @Peter Lawrey pour votre réponse, ça m'a empêché de devenir fou :-)

3
arnep

Je suggère d'utiliser Joda-Time . Il gère ces situations correctement. Dans l'exemple suivant, les millisecondes sont correctement analysées comme 200 ms.

import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

public class ParseMillis {

  public static void main(String[] args) {
    String s = "00:00:01.2";
    DateTimeFormatter format = DateTimeFormat.forPattern("HH:mm:ss.S");
    DateTime dateTime = format.parseDateTime(s);
    System.out.println(dateTime.getMillisOfSecond());
  }
}
3
Richard Povinelli