Javaで文字列が有効な日付かどうかを確認する

1. 前書き

このチュートリアルでは、Javaで_String_に有効な日付が含まれているかどうかを確認するさまざまな方法について説明します。
Java 8以前、Java 8以降、およびhttps://commons.apache.org/proper/commons-validator/[Apache Commons Validator]を使用したソリューションについて説明します。

2. 日付検証の概要

アプリケーションでデータを受信するたびに、さらに処理を行う前に、そのデータが有効であることを確認する必要があります。
日付入力の場合、以下を確認する必要がある場合があります。
  • 入力には、MM / DD / YYYYなどの有効な形式の日付が含まれます

  • 入力のさまざまな部分が有効な範囲内にあります

  • 入力はカレンダーの有効な日付に解決されます

    link:/java-date-regular-expressions [正規表現]を使用して上記を実行できます。 ただし、さまざまな入力形式とロケールを処理するための正規表現は複雑で、エラーが発生しやすくなります。 さらに、パフォーマンスが低下する可能性があります。
    *日付検証を柔軟で堅牢かつ効率的な方法で実装するさまざまな方法について説明します。*
    最初に、日付検証用のインターフェースを作成しましょう。
public interface DateValidator {
   boolean isValid(String dateStr);
}
次のセクションでは、さまざまなアプローチを使用してこのインターフェイスを実装します。

3. _DateFormat_を使用した検証

Javaは、最初から日付をフォーマットおよび解析する機能を提供しています。 この機能は、https://docs.oracle.com/javase/8/docs/api/java/text/DateFormat.html [_DateFormat _] abstractクラスとその実装にあります— link:/ java-simple-date-format [_SimpleDateFormat_]。
_DateFormat_ classの_parse_メソッドを使用して、日付検証を実装しましょう。
public class DateValidatorUsingDateFormat implements DateValidator {
    private String dateFormat;

    public DateValidatorUsingDateFormat(String dateFormat) {
        this.dateFormat = dateFormat;
    }

    @Override
    public boolean isValid(String dateStr) {
        DateFormat sdf = new SimpleDateFormat(this.dateFormat);
        sdf.setLenient(false);
        try {
            sdf.parse(dateStr);
        } catch (ParseException e) {
            return false;
        }
        return true;
    }
}
  • _DateFormat_および関連するクラスはスレッドセーフではないため、メソッド呼び出しごとに新しいインスタンスを作成しています。

    次に、このクラスの単体テストを書きましょう。
DateValidator validator = new DateValidatorUsingDateFormat("MM/dd/yyyy");

assertTrue(validator.isValid("02/28/2019"));
assertFalse(validator.isValid("02/30/2019"));
これは、Java 8より前の最も一般的なソリューションです。

4. _LocalDate_を使用して検証する

Java 8はlink:/java-8-date-time-intro [改良された日時API]を導入しました。 * https://docs.oracle.com/javase/8/docs/api/java/time/LocalDate.html [_LocalDate_]クラスを追加しました。これは、時間のない日付を表します。 このクラスは不変でスレッドセーフです。*
_LocalDate_は、日付を解析するための2つの静的メソッドを提供します。 どちらもlink:/java-datetimeformatter[_DateTimeFormatter_]を使用して実際の解析を実行します。
public static LocalDate parse​(CharSequence text)
// parses dates using using DateTimeFormatter.ISO_LOCAL_DATE

public static LocalDate parse​(CharSequence text, DateTimeFormatter formatter)
// parses dates using the provided formatter
_parse_メソッドを使用して、日付検証を実装しましょう。
public class DateValidatorUsingLocalDate implements DateValidator {
    private DateTimeFormatter dateFormatter;

    public DateValidatorUsingLocalDate(DateTimeFormatter dateFormatter) {
        this.dateFormatter = dateFormatter;
    }

    @Override
    public boolean isValid(String dateStr) {
        try {
            LocalDate.parse(dateStr, this.dateFormatter);
        } catch (DateTimeParseException e) {
            return false;
        }
        return true;
    }
}
実装では、書式設定に_DateTimeFormatter_オブジェクトを使用します。 このクラスはスレッドセーフであるため、異なるメソッド呼び出しで同じインスタンスを使用しています。
この実装の単体テストも追加しましょう。
DateTimeFormatter dateFormatter = DateTimeFormatter.BASIC_ISO_DATE;
DateValidator validator = new DateValidatorUsingLocalDate(dateFormatter);

assertTrue(validator.isValid("20190228"));
assertFalse(validator.isValid("20190230"));

5. _ DateTimeFormatter _を使用して検証

前のセクションでは、_LocalDate_が解析に_DateTimeFormatter_オブジェクトを使用することを確認しました。 _DateTimeFormatter_クラスを直接使用して、フォーマットと解析を行うこともできます。
  • DateTimeFormatter _は、2つのフェーズでテキストを解析します。*フェーズ1では、構成に基づいてテキストをさまざまな日付および時刻フィールドに解析します。 フェーズ2では、解析されたフィールドを日付および/または時刻オブジェクトに解決します。

    _ResolverStyle_属性は、フェーズ2を制御します。 これは_enum_であり、3つの可能な値があります。
  • LENIENT –日付と時刻を寛大に解決します

  • SMART –インテリジェントな方法で日付と時刻を解決します

  • STRICT –日付と時刻を厳密に解決します

    それでは、_DateTimeFormatter_を直接使用して日付検証を記述しましょう。
public class DateValidatorUsingDateTimeFormatter implements DateValidator {
    private DateTimeFormatter dateFormatter;

    public DateValidatorUsingDateTimeFormatter(DateTimeFormatter dateFormatter) {
        this.dateFormatter = dateFormatter;
    }

    @Override
    public boolean isValid(String dateStr) {
        try {
            this.dateFormatter.parse(dateStr);
        } catch (DateTimeParseException e) {
            return false;
        }
        return true;
    }
}
次に、このクラスの単体テストを追加しましょう。
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("uuuu-MM-dd", Locale.US)
    .withResolverStyle(ResolverStyle.STRICT);
DateValidator validator = new DateValidatorUsingDateTimeFormatter(dateFormatter);

assertTrue(validator.isValid("2019-02-28"));
assertFalse(validator.isValid("2019-02-30"));
上記のテストでは、パターンとロケールに基づいて_DateTimeFormatter_を作成しています。 日付には厳密な解決策を使用しています。

6. Apache Commons Validatorを使用して検証する

https://commons.apache.org/[Apache Commons]プロジェクトは検証フレームワークを提供します。 *これには、日付、時刻、数字、通貨、IPアドレス、電子メール、URLなどの検証ルーチンが含まれます。*
この記事の目標として、https://commons.apache.org/proper/commons-validator/apidocs/org/apache/commons/validator/GenericValidator.html [_GenericValidator_]クラスを見てみましょう。 _String_に有効な日付が含まれているかどうかを確認するいくつかのメソッド:
public static boolean isDate(String value, Locale locale)

public static boolean isDate(String value,String datePattern, boolean strict)
ライブラリを使用するには、プロジェクトにhttps://search.maven.org/search?q=g:commons-validator%20AND%20a:commons-validator[_commons-validator_] Maven依存関係を追加します。
<dependency>
    <groupId>commons-validator</groupId>
    <artifactId>commons-validator</artifactId>
    <version>1.6</version>
</dependency>
次に、_GenericValidator_クラスを使用して日付を検証します。
assertTrue(GenericValidator.isDate("2019-02-28", "yyyy-MM-dd", true));
assertFalse(GenericValidator.isDate("2019-02-29", "yyyy-MM-dd", true));

7. 結論

この記事では、_String_に有効な日付が含まれているかどうかを確認するさまざまな方法を検討しました。
いつものように、完全なソースコードはhttps://github.com/eugenp/tutorials/tree/master/java-dates-2[GitHubで]にあります。