web-dev-qa-db-ja.com

SimpleDateFormatは、4桁を超えるミリ秒を解析できません

このようにタイムスタンプを解析したい--"2016-03-16 01:14:21.6739"。しかし、SimpleDateFormatを使用して解析すると、誤った解析値が出力されることがわかりました。残り739ミリ秒で、6739ミリ秒から6秒になります。日付をこの形式に変換しました-Wed Mar 16 01:14:27 PDT 2016。秒の部分が21秒から27秒に変更されたのはなぜですか(6秒の追加?)。以下は私のコードスニペットです:

final SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSS");
String parsedate="2016-03-16 01:14:21.6739";
try {
    Date outputdate = sf.parse(parsedate);
    String newdate = outputdate.toString();  //==output date is: Wed Mar 16 01:14:27 PDT 2016 
    System.out.println(newdate);
} catch (ParseException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}
7
Alter Hu

SimpleDateFormatを使用して、ミリ秒よりも細かい粒度で時間を表現することは不可能のようです。何が起こっているのかというと、6739を入力すると、Javaはそれを6739ミリ秒、つまり6秒と739ミリ秒として理解するため、6秒の違いが観察されます。

これらを確認してください。非常によく説明されています。 文字列-ナノ秒での日付変換マイクロ秒またはナノ秒の精度でのJava日付解析

5
Bruno Piqueras

SSSimpleDateFormatはミリ秒です。 6739ミリ秒あります。これは、時間に6.7秒を追加することを意味します。おそらく、6739673に切り捨てて(または、必要に応じて674に丸めて)、ミリ秒単位で正しく解析できるようにすることができます。

5
khelwood

tl; dr

LocalDateTime.parse(
    "2016-03-16 01:14:21.6739".replace( " " , "T" )  // Comply with ISO 8601 standard format.
)

ミリ秒対マイクロ秒

他の人が指摘したように、Java.util.Dateの分解能はミリ秒です。これは、秒の小数部の最大3桁を意味します。

入力文字列に4桁あり、1桁が多すぎます。入力値には、マイクロ秒やナノ秒などのより細かい解像度が必要です。

Java.time

欠陥があり、紛らわしく、面倒なJava.util.Date/.Calendarクラスを使用する代わりに、それらの代わりに移動します。 Java.time フレームワークがJava 8以降。

Java.timeクラスの解像度は ナノ秒、最大nine秒の小数部の数字。例えば:

2016-03-17T05:19:24.123456789Z

ISO 8601

文字列入力はほぼ標準です ISO 8601 Java.timeで、日時値のテキスト表現を解析/生成するときにデフォルトで使用される形式です。 ISO 8601に準拠するには、中央のスペースをTに置き換えます。

String input = "2016-03-16 01:14:21.6739".replace( " " , "T" );

ゾーン化されていない

LocalDateTime は、タイムゾーンコンテキストを含まない日時の概算です。 タイムライン上の瞬間ではありません

LocalDateTime ldt = LocalDateTime.parse( input );

UTC

LocalDateTime 目的のタイムゾーンを適用して、タイムライン上の実際の瞬間にします。 UTC向けの場合は、Instantを作成します。

Instant instant = ldt.toInstant( ZoneOffset.UTC );

ゾーニング

特定のタイムゾーンを対象としている場合は、 ZoneId を指定して ZoneDateTime を取得します。

ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ldt.atZone( zoneId );
3
Basil Bourque

少しトピックから外れていますが、SimpleDateFormatクラスはスレッドセーフではありません-ある程度理解できる解析だけでなく、フォーマットについてもです。ネット上にはこれに関する多くの情報があります、ここに一例があります: http://javarevisited.blogspot.co.il/2012/03/simpledateformat-in-Java-is- not-thread.html 。この問題は修正されません。 Java 8には、まったく新しいパッケージがありますJava.time完全または部分的な日付と時刻で作業できる素晴らしい新機能を備えています。また、大幅に改善されたフォーマットおよび解析機能を提供する新しいクラス DateTimeFormatter があります。ただし、Java古いJava 8を使用する場合は、 Jodaタイムライブラリ またはApache FastDateFormat

0
Michael Gantman

上記のコメントで述べたように、あなたの質問にはすでに答えが含まれています。ミリ秒は3桁を超えてはなりません。そうでない場合は、すでに少なくとも1秒を表します。

コードが機能しているのは、Java.text.SimpleDateFormatの非常に疑わしい機能であるlenientによるものです( ここ を参照) )オプション。デフォルトでは、SimpleDateFormatsetLenienttrueに設定されています。これは、パーサーがパターンに100%一致しない文字列を解釈しようとし、それらを日付オブジェクトに変換することを意味します。いくつかのヒューリスティックの手段。たとえば、日付31.04.2016を受け入れ、それを01.05.2016に変換します。この機能は状況によっては優れている場合がありますが、ほとんどの場合、疑わしい結果が生成されます。

コードでlenientをfalseに設定すると、日付文字列は解析されなくなります。パターンのエラーをより明確にする:

final SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSS");
sf.setLenient(false);
String parsedate="2016-03-16 01:14:21.6739";
...

Java.util.Dateはミリ秒未満の精度を表すことができないため、日付を解析するための最良のオプションは、ドットの後の部分にそれ以上の数字がある場合は、入力日付の最後の桁を単純に取り除くことだと思います。 4桁より。コードは次のようになります。

final SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
sf.setLenient(false);
String parsedate="2016-03-16 01:14:21.6739";
try {
    // 23 is the length of the date pattern
    if (parsedate.length() > 23) {
        parsedate = parsedate.substring(0, 23);
    }
    Date outputdate = sf.parse(parsedate);
    String newdate = sf.format(outputdate);  //==output date is: 2016-03-16 01:14:21.673
    System.out.println(newdate);
} catch (ParseException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

4桁目のすべての情報を失わないようにするために、丸めロジックを追加することもできます...

0
dpr

String newdate = outputdate.toString();の代わりにString newdate = sf.format(outputdate);を使用できます。

0
visingh

最終出力として文字列を取得する必要がある場合は、formatの代わりにparseを使用しないでください。

        final SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ");
        sf.setTimeZone(TimeZone.getTimeZone("UTC")); 
        Date curDate = new Date();

        String outputdate = sf.format(curDate);
        // 2016-03-17 09:45:28.658+0000
        System.out.println(outputdate);

        Date strToDate = new Date();
        try {
            strToDate = sf.parse(outputdate);
        } catch (ParseException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        //Thu Mar 17 17:11:30 MYT 2016
        System.out.println(strToDate);

"yyyy-MM-dd HH:mm:ss.SSSS"の代わりに"yyyy-MM-dd HH:mm:ss.SSSZ"を使用してここで確認してください https://docs.Oracle.com/javase/7/docs/api/Java/text/SimpleDateFormat.html

"yyyy-MM-dd'T'HH:mm:ss.SSSZ"    2001-07-04T12:08:56.235-0700
0
Raunak Kathuria