RegEx zum Abgleichen des Datumsmusters in Java

RegEx zum Abgleichen des Datumsmusters in Java

1. Einführung

Reguläre Ausdrücke sind ein leistungsfähiges Werkzeug zum Anpassen verschiedener Arten von Mustern, wenn sie ordnungsgemäß verwendet werden.

In diesem Artikel verwenden wir das Paketjava.util.regex, um festzustellen, ob ein bestimmtesStringein gültiges Datum enthält oder nicht.

Eine Einführung in reguläre Ausdrücke finden Sie unterour Guide To Java Regular Expressions API.

2. Datumsformatübersicht

Wir werden ein gültiges Datum in Bezug auf den internationalen Gregorianischen Kalender definieren. Unser Format wird dem allgemeinen Muster folgen:YYYY-MM-DD.

Nehmen wir auch das Konzept einesleap-Jahres auf, das ein Jahr mit einem Tag vom 29. Februar ist. According to the Gregorian calendar, we’ll call a year leap if the year number can be divided evenly by 4 except for those which are divisible by 100 but including those which are divisible by 400.

In allen anderen Fällen nennen wir, ein Jahrregular.

Beispiele für gültige Daten:

  • 2017-12-31

  • 2020-02-29

  • 2400-02-29

Beispiele für ungültige Daten:

  • 2017/12/31: falsches Token-Trennzeichen

  • 2018-1-1: fehlende führende Nullen

  • 2018-04-31: Für April zählen falsche Tage

  • 2100-02-29: Dieses Jahr ist kein Sprung, da der Wert durch100 dividiert wird. Daher ist der Februar auf 28 Tage begrenzt

3. Implementierung einer Lösung

Da wir ein Datum mit regulären Ausdrücken abgleichen, skizzieren wir zunächst eine SchnittstelleDateMatcher, die eine einzelnematches-Methode bereitstellt:

public interface DateMatcher {
    boolean matches(String date);
}

Im Folgenden wird die Implementierung Schritt für Schritt vorgestellt, um am Ende eine vollständige Lösung zu finden.

3.1. Anpassung an das breite Format

Wir beginnen mit der Erstellung eines sehr einfachen Prototyps, der die Formatbeschränkungen unseres Matchers behandelt:

class FormattedDateMatcher implements DateMatcher {

    private static Pattern DATE_PATTERN = Pattern.compile(
      "^\\d{4}-\\d{2}-\\d{2}$");

    @Override
    public boolean matches(String date) {
        return DATE_PATTERN.matcher(date).matches();
    }
}

Hier geben wir an, dassa valid date must consist of three groups of integers separated by a dash. Die erste Gruppe besteht aus vier Ganzzahlen, wobei die verbleibenden zwei Gruppen jeweils zwei Ganzzahlen haben.

Übereinstimmende Daten:2017-12-31,2018-01-31,0000-00-00,1029-99-72

Nicht übereinstimmende Daten:2018-01,2018-01-XX,2020/02/29

3.2. Übereinstimmung mit dem spezifischen Datumsformat

In unserem zweiten Beispiel werden Datumsbereiche sowie unsere Formatierungsbeschränkungen akzeptiert. Der Einfachheit halber haben wir unser Interesse auf die Jahre 1900 - 2999 beschränkt.

Nachdem wir unser allgemeines Datumsformat erfolgreich angepasst haben, müssen wir dies weiter einschränken, um sicherzustellen, dass die Daten tatsächlich korrekt sind:

^((19|2[0-9])[0-9]{2})-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])$

Hier haben wir dreigroups von Ganzzahlbereichen eingeführt, die übereinstimmen müssen:

  • (19|2[0-9])[0-9]{2} deckt einen begrenzten Bereich von Jahren ab, indem eine Zahl abgeglichen wird, die mit19 oder2X beginnt, gefolgt von einigen Ziffern.

  • 0[1-9]|1[012] entspricht einer Monatszahl in einem Bereich von01-12

  • 0[1-9]|[12][0-9]|3[01] entspricht einer Tageszahl in einem Bereich von01-31

Übereinstimmende Daten:1900-01-01,2205-02-31,2999-12-31

Nicht übereinstimmende Daten:1899-12-31,2018-05-35,2018-13-05,3000-01-01,2018-01-XX

3.3. Passend zum 29. Februar

Um die Schaltjahre korrekt abzugleichen, müssen wir zuerstidentify when we have encountered a leap year eingeben und dann sicherstellen, dass wir den 29. Februar als gültiges Datum für diese Jahre akzeptieren.

Da die Anzahl der Schaltjahre in unserem eingeschränkten Bereich groß genug ist, sollten wir die entsprechenden Teilungsregeln verwenden, um sie zu filtern:

  • Wenn die durch die letzten beiden Ziffern einer Zahl gebildete Zahl durch 4 teilbar ist, ist die ursprüngliche Zahl durch 4 teilbar

  • Wenn die letzten beiden Ziffern der Nummer 00 sind, ist die Nummer durch 100 teilbar

Hier ist eine Lösung:

^((2000|2400|2800|(19|2[0-9](0[48]|[2468][048]|[13579][26])))-02-29)$

Das Muster besteht aus folgenden Teilen:

  • 2000|2400|2800 entspricht einer Reihe von Schaltjahren mit einem Teiler von400 in einem eingeschränkten Bereich von1900-2999

  • 19|2[0-9](0[48]|[2468][048]|[13579][26])) entspricht allenwhite-list Jahreskombinationen, die einen Teiler von4 und keinen Teiler von100 haben

  • -02-29 entsprichtFebruary 2nd

Übereinstimmende Daten:2020-02-29,2024-02-29,2400-02-29

Nicht übereinstimmende Daten:2019-02-29,2100-02-29,3200-02-29,2020/02/29

3.4. Passende allgemeine Februar-Tage

we also need to match all other days of February (1 – 28) in all years entspricht nicht nur dem 29. Februar in Schaltjahren:

^(((19|2[0-9])[0-9]{2})-02-(0[1-9]|1[0-9]|2[0-8]))$

Übereinstimmende Daten:2018-02-01,2019-02-13,2020-02-25

Nicht übereinstimmende Daten:2000-02-30,2400-02-62,2018/02/28

3.5. Passende 31-Tage-Monate

Die Monate Januar, März, Mai, Juli, August, Oktober und Dezember sollten für 1 bis 31 Tage übereinstimmen:

^(((19|2[0-9])[0-9]{2})-(0[13578]|10|12)-(0[1-9]|[12][0-9]|3[01]))$

Übereinstimmende Daten:2018-01-31,2021-07-31,2022-08-31

Nicht übereinstimmende Daten:2018-01-32,2019-03-64,2018/01/31

3.6. Passende 30-Tage-Monate

Die Monate April, Juni, September und November sollten für 1 bis 30 Tage übereinstimmen:

^(((19|2[0-9])[0-9]{2})-(0[469]|11)-(0[1-9]|[12][0-9]|30))$

Übereinstimmende Daten:2018-04-30,2019-06-30,2020-09-30

Nicht übereinstimmende Daten:2018-04-31,2019-06-31,2018/04/30

3.7. Gregorianischer Date Matcher

Jetzt können wircombine all of the patterns above into a single matcher to have a complete GregorianDateMatcher erfüllen, um alle Bedingungen zu erfüllen:

class GregorianDateMatcher implements DateMatcher {

    private static Pattern DATE_PATTERN = Pattern.compile(
      "^((2000|2400|2800|(19|2[0-9](0[48]|[2468][048]|[13579][26])))-02-29)$"
      + "|^(((19|2[0-9])[0-9]{2})-02-(0[1-9]|1[0-9]|2[0-8]))$"
      + "|^(((19|2[0-9])[0-9]{2})-(0[13578]|10|12)-(0[1-9]|[12][0-9]|3[01]))$"
      + "|^(((19|2[0-9])[0-9]{2})-(0[469]|11)-(0[1-9]|[12][0-9]|30))$");

    @Override
    public boolean matches(String date) {
        return DATE_PATTERN.matcher(date).matches();
    }
}

We’ve used an alternation character “|” to match at least one der vier Zweige. Somit entspricht das gültige Datum Februar entweder dem ersten Zweig des 29. Februar eines Schaltjahres oder dem zweiten Zweig eines Tages von1 bis28. Die Termine der verbleibenden Monate stimmen mit dem dritten und vierten Zweig überein.

Da wir dieses Muster nicht zugunsten einer besseren Lesbarkeit optimiert haben, können Sie gerne mit einer Länge davon experimentieren.

In diesem Moment haben wir alle Einschränkungen erfüllt, die wir am Anfang eingeführt haben.

3.8. Hinweis zur Leistung

Parsing complex regular expressions may significantly affect the performance of the execution flow. Der Hauptzweck dieses Artikels bestand nicht darin, eine effiziente Methode zum Testen einer Zeichenfolge auf ihre Mitgliedschaft in einer Reihe aller möglichen Daten zu erlernen.

Erwägen Sie die Verwendung vonLocalDate.parse(), die von Java8 bereitgestellt werden, wenn ein zuverlässiger und schneller Ansatz zur Validierung eines Datums erforderlich ist.

4. Fazit

In diesem Artikel haben wir gelernt, wie Sie reguläre Ausdrücke verwenden, um das streng formatierte Datum des Gregorianischen Kalenders abzugleichen, indem Sie auch Regeln für das Format, den Bereich und die Länge der Monate angeben.

Der gesamte in diesem Artikel vorgestellte Code ist inover on Github verfügbar. Dies ist ein Maven-basiertes Projekt, daher sollte es einfach zu importieren und auszuführen sein.