web-dev-qa-db-ja.com

Java 8 LocalDateTimeは無効な日付を解析しています

クライアント側で日付を検証したいので、次のコードを書きました。しかし、例外を取得する代わりに、2月31日の日付文字列の適切な日付オブジェクトを取得しています。これは明らかに無効な日付です。

public class Test {

    public static void main(String[] args) {
        String dateFormat = "HH:mm:ss MM/dd/yyyy";
        String dateString = "11:30:59 02/31/2015";
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(dateFormat, Locale.US);
        try {
            LocalDateTime date = LocalDateTime.parse(dateString, dateTimeFormatter);
            System.out.println(date);
        } catch (Exception e) {
            // Throw invalid date message
        }
    }
}

出力:2015-02-28T11:30:59

LocalDateTimeが例外をスローする代わりにこの日付を解析する理由を誰もが知っていますか。

21
Zeeshan

厳密な ResolverStyle が必要です。

テキスト文字列の解析は、2つのフェーズで発生します。フェーズ1は、ビルダーに追加されたフィールドに応じた基本的なテキスト解析です。フェーズ2は、解析されたフィールドと値のペアを日付および/または時刻オブジェクトに解決します。このスタイルは、フェーズ2の解決方法を制御するために使用されます。

サンプルコード-withResolverStyle(ResolverStyle.STRICT)は重要な変更であり、uuuuではなくyyyyを使用しています(uuuuは「年」で、「yyyy」は「年の年」であるため、あいまいです):

import Java.time.*;
import Java.time.format.*;
import Java.util.*;

public class Test {

    public static void main(String[] args) {
        String dateFormat = "HH:mm:ss MM/dd/uuuu";
        String dateString = "11:30:59 02/31/2015";
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter
            .ofPattern(dateFormat, Locale.US)
            .withResolverStyle(ResolverStyle.STRICT);
        try {
            LocalDateTime date = LocalDateTime.parse(dateString, dateTimeFormatter);
            System.out.println(date);
        } catch (DateTimeParseException e) {
            // Throw invalid date message
            System.out.println("Exception was thrown");
        }
    }
}
34
Jon Skeet

Java 8 DateTimeFormatterはyyyyを使用してYEAR_OF_ERAを意味し、uuuuを使用してYEARを意味します。パターン文字列を次のように変更する必要があります。

String dateFormat = "HH:mm:ss MM/dd/uuuu";

DateTimeFormatterはデフォルトでSMARTリゾルバースタイルを使用しますが、STRICTリゾルバースタイルを使用する必要があります。dateTimeFormatter初期化コードを次のように変更します。

DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(dateFormat, Locale.US)
                                                       .withResolverStyle(ResolverStyle.STRICT);
9
Palamino

切り捨てではありません。 2月には31日はありませんでした。また、日付/時刻の検証オブジェクトを使用して、存在しない日を表すことはできません。

その結果、無効な入力を受け取り、正しい日付(その年の2月の最後の日付)に最適な近似値を提供します。

SimpleDateFormatは、setLenient(boolean value)メソッドを持つDateFormatから継承します。解析の前​​にsetLenient(true)を呼び出した場合、おそらくもっと文句を言うでしょう javadocsで詳述されているように

3
Edwin Buck
try {
    SimpleDateFormat df = new Java.text.SimpleDateFormat("HH:mm:ss MM/dd/yyyy");
    df.setLenient(false);
    System.out.println(df.parse("11:30:59 02/29/2015"));
} catch (Java.text.ParseException e) {
  System.out.println(e);
}

DateFormat.setLenient(boolean) で有効な日付として日付を認識する1つのソリューションを見つけました。無効な日付を解析しようとすると、解析例外がスローされます。

編集:

Java 8ですが、月が112の間にない場合、日が32より大きい場合、例外が発生します。まったく機能していません。しかし、月間は機能します。

try {
TemporalAccessor ta = DateTimeFormatter.ofPattern("HH:mm:ss MM/dd/yyyy").parse("11:30:59 02/32/2015");
} catch (Exception e) {
System.out.println(e);
}

出力:

Java.time.format.DateTimeParseException: Text '11:30:59 02/32/2015' could not be
 parsed: Invalid value for DayOfMonth (valid values 1 - 28/31): 32
2
SatyaTNV

LocalDateTime.parseは、渡された文字列に無効な文字、31日を超える日数、または12を超える月が含まれる場合にのみエラーをスローします。

たとえば、コードを次のように変更した場合:

String dateString = "11:30:59 0zz2/31/2015";

指定した日付内の無効な「zz」文字が原因で例外がスローされます。なぜそれが日付を「端数切り捨て」するかについては、私にはわかりません。

ソース: https://docs.Oracle.com/javase/8/docs/api/Java/time/LocalDateTime.html#parse-Java.lang.CharSequence-

0
jste89