web-dev-qa-db-ja.com

SimpleDateFormatはミリ秒を正しく解析していません

背景:

データベーステーブルには、2つのタイムスタンプがあります

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

「ORDER BY TIMESTAMP ASC」を実行すると、timeStamp2は大きいタイムスタンプと見なされます(正しい)。

要件:これらのタイムスタンプの差を取得する必要があります(timeStamp2-timeStamp1)

私の実装:

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";
    }
}

回答は238msだったはずですが、返される値は-653msです。何が間違っているのかわかりません。助言がありますか?

26
dkulkarni

私は完全にはわかりませんが、JavaDocはこれを述べています:

解析では、隣接する2つのフィールドを分離する必要がない限り、パターン文字の数は無視されます。

これは、2011-08-23 14:57:26.9からのミリ秒が9ではなく900として解析されることを示します。末尾のゼロを追加すると動作する場合があります:2011-08-23 14:57:26.900

12
Thomas

解析している形式と使用している形式が一致しません。 3桁のフィールドを想定しており、1桁のみを提供しています。それはとります 9そして、あなたが009欲しいものが900。日付形式は複雑であり、異なる形式で日付を証明する場合、日付の解析方法が異なる場合があります。

ドキュメントでは、Sはミリ秒の数を意味し、そのフィールドの数は9であるため、正しく動作していると書かれています。


編集:この例は役立つかもしれません

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");
}

プリント

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

ログファイルからの正確な時間が6桁のミリ秒であるという同じ問題がありました。解析時間は最大16分の差を与えました! WTF?

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

桁数を変更すると、誤った差が減り、このスレッドのおかげで問題を特定できました。

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

SimpleDateFormatは内部で3桁のみを処理するため、小さな正規表現を使用して不要なものを削除しました(丸めエラーを無視し、1からn桁まで):

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

答えてくれた@Peter Lawreyに感謝します。

3
arnep

Joda-Time を使用することをお勧めします。これらの状況を適切に処理します。次の例では、ミリ秒は200ミリ秒として正しく解析されます。

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