Datums-Regex: TT/MM/JJJJ und JJJJ-MM-TT prüfen (JavaScript, PHP, HTML)

Eine Datums-Regex, die den 30. Februar und den 31. April ablehnt, mit ISO- und US-Variante, den Funktionen für JavaScript und PHP, dem HTML-Attribut und dem Moment, in dem man die Regex besser beiseitelässt.

Regex für die Datumsprüfung in JavaScript, PHP und HTML
Schnelle Antwort

Die Regex unten validiert ein französisches Datum im Format TT/MM/JJJJ (Trennzeichen /, - oder .) und lehnt unmögliche Datumsangaben wie den 30. Februar oder den 31. April ab, Schaltjahre eingeschlossen. Für das ISO-Format JJJJ-MM-TT genügt eine kurze Regex, wenn du danach mit dem Date-Objekt in JavaScript oder mit checkdate() in PHP bestätigst.

Ein von Hand eingetipptes Datum zu prüfen ist heikler, als es aussieht: „30/02/2026“ hat die richtige Form und existiert nicht. Die Datums-Regex unten prüft das Format TT/MM/JJJJ mit /, - oder . als Trennzeichen und lehnt unmögliche Tage ab, auch den 29. Februar in Nicht-Schaltjahren. Danach folgen die Fassungen für JavaScript, PHP und HTML, die ISO-Variante und der Fall, in dem das Date-Objekt besser abschneidet als eine Regex.

Die Datums-Regex zum Kopieren

javascript
^(?:(?:31(\/|-|\.)(?:0?[13578]|1[02]))\1|(?:(?:29|30)(\/|-|\.)(?:0?[13-9]|1[0-2])\2))(?:(?:1[6-9]|[2-9]\d)?\d{2})$|^(?:29(\/|-|\.)0?2\3(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))$|^(?:0?[1-9]|1\d|2[0-8])(\/|-|\.)(?:(?:0?[1-9])|(?:1[0-2]))\4(?:(?:1[6-9]|[2-9]\d)?\d{2})$

Sie ist lang, weil sie wie ein Kalender denkt. Drei Zweige, getrennt durch |:

  1. Den 31. gibt es nur in Monaten mit 31 Tagen (01, 03, 05, 07, 08, 10, 12), den 29. und den 30. in allen Monaten außer Februar.
  2. Den 29. Februar nimmt sie nur an, wenn das Jahr ein Schaltjahr ist: teilbar durch 4 (0[48]|[2468][048]|[13579][26]) oder ein durch 400 teilbares Jahrhundert ((?:16|[2468][048]|[3579][26])00). 2000 und 2024 kommen durch, 1900 und 2023 werden abgelehnt.
  3. Vom 1. bis zum 28. nimmt jeder Monat das Datum an.

Die Rückwärtsverweise \1, \2, \3 und \4 verlangen, dass das zweite Trennzeichen dem ersten gleicht: 07/09/2026 und 07-09-2026 kommen durch, 07/09-2026 nicht.

Nur ein Trennzeichen?

Wenn du ausschließlich den Schrägstrich zulassen willst, ersetze jedes (\/|-|\.) durch (\/). Die Klammern bleiben nötig: Die Rückwärtsverweise \1 bis \4 zeigen auf diese Gruppen.

Die getesteten Datumsangaben

Tatsächliches Ergebnis der Regex unter Node.js 22 und PHP 8.5, in beiden Engines identisch:

Eingabe Ergebnis Grund
31/12/2026 akzeptiert der 31. in einem Monat mit 31 Tagen
29/02/2024 akzeptiert 2024 ist ein Schaltjahr
29/02/2000 akzeptiert 2000 ist durch 400 teilbar
01/01/2026 und 1/1/2026 akzeptiert die führende Null ist optional
07-09-2026 und 07.09.2026 akzeptiert weitere erlaubte Trennzeichen
29/02/2023 abgelehnt 2023 ist kein Schaltjahr
29/02/1900 abgelehnt Jahrhundert nicht durch 400 teilbar
30/02/2026 abgelehnt der Februar hat nie 30 Tage
31/04/2026 abgelehnt der April hat 30 Tage
00/01/2026 abgelehnt den Tag 0 gibt es nicht
12/13/2026 abgelehnt Monat 13: die Regex erwartet wirklich TT/MM und nicht MM/TT
07/09-2026 abgelehnt unterschiedliche Trennzeichen
2026-09-07 abgelehnt ISO-Format, siehe weiter unten
07/09/26 und 31/12/1899 akzeptiert zweistelliges Jahr oder Jahr vor 1900: bei Bedarf im eigenen Code eingrenzen

JavaScript-Fassung

Die Funktion verankert die Regex mit ^ und $, entfernt die Leerzeichen rund um die Eingabe und gibt einen booleschen Wert zurück.

validateDate.js
/**
 * Prüft ein Datum im Format TT/MM/JJJJ, TT-MM-JJJJ oder TT.MM.JJJJ.
 * @param {string} date
 * @returns {boolean}
 */
function validateDate(date) {
  const reg = /^(?:(?:31(\/|-|\.)(?:0?[13578]|1[02]))\1|(?:(?:29|30)(\/|-|\.)(?:0?[13-9]|1[0-2])\2))(?:(?:1[6-9]|[2-9]\d)?\d{2})$|^(?:29(\/|-|\.)0?2\3(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))$|^(?:0?[1-9]|1\d|2[0-8])(\/|-|\.)(?:(?:0?[1-9])|(?:1[0-2]))\4(?:(?:1[6-9]|[2-9]\d)?\d{2})$/;
  return reg.test(String(date).trim());
}

console.log(validateDate('29/02/2024')); // true
console.log(validateDate('29/02/2023')); // false
console.log(validateDate('31/04/2026')); // false

Im Formular ist das Vorgehen dasselbe wie bei der Prüfung einer E-Mail-Adresse: auf submit hören, bei ungültigem Datum preventDefault() aufrufen und die Meldung neben dem Feld anzeigen.

PHP-Fassung

Die Kontrolle auf dem Server ist Pflicht: JavaScript lässt sich abschalten. Dasselbe Muster funktioniert mit preg_match(); die Rückwärtsverweise schreibt man \\1, wenn die PHP-Zeichenkette in einfachen Anführungszeichen steht.

isValidDate.php
/**
 * Prüft ein Datum im Format TT/MM/JJJJ, TT-MM-JJJJ oder TT.MM.JJJJ.
 */
function isValidDate(string $date): bool
{
    $pattern = '/^(?:(?:31(\/|-|\.)(?:0?[13578]|1[02]))\\1|(?:(?:29|30)(\/|-|\.)(?:0?[13-9]|1[0-2])\\2))(?:(?:1[6-9]|[2-9]\d)?\d{2})$|^(?:29(\/|-|\.)0?2\\3(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))$|^(?:0?[1-9]|1\d|2[0-8])(\/|-|\.)(?:(?:0?[1-9])|(?:1[0-2]))\\4(?:(?:1[6-9]|[2-9]\d)?\d{2})$/';

    return preg_match($pattern, trim($date)) === 1;
}

var_dump(isValidDate('29/02/2024')); // bool(true)
var_dump(isValidDate('30/02/2026')); // bool(false)

Die Alternative ohne Regex: checkdate()

PHP weiß bereits, ob es ein Tripel aus Tag, Monat und Jahr gibt. Eine kleine Regex zum Zerlegen, dann checkdate() zum Urteilen: kürzer und lesbarer.

isValidDateFr.php
function isValidDateFr(string $date): bool
{
    if (!preg_match('#^(\d{1,2})[/.-](\d{1,2})[/.-](\d{4})$#', trim($date), $m)) {
        return false;
    }

    return checkdate((int) $m[2], (int) $m[1], (int) $m[3]); // Monat, Tag, Jahr
}

var_dump(isValidDateFr('29/02/2024')); // bool(true)
var_dump(isValidDateFr('29/02/2023')); // bool(false)
var_dump(isValidDateFr('31/04/2026')); // bool(false)

Vorsicht bei DateTime::createFromFormat('d/m/Y', '29/02/2023'): Es gibt nicht false zurück, sondern den 1. März 2023, und meldet die Abweichung in DateTime::getLastErrors(). Für eine strenge Prüfung ist checkdate() sicherer.

HTML-Fassung

Wenn der Nutzer das Datum als freien Text eingibt, wendet das Attribut pattern die Regex vor dem Absenden an. Es ist implizit verankert: ^ und $ fallen weg.

formulaire.html
<label for="naissance">Date de naissance (JJ/MM/AAAA)</label>
<input
  id="naissance"
  name="naissance"
  type="text"
  inputmode="numeric"
  placeholder="JJ/MM/AAAA"
  pattern="(?:(?:31(\/|-|\.)(?:0?[13578]|1[02]))\1|(?:(?:29|30)(\/|-|\.)(?:0?[13-9]|1[0-2])\2))(?:(?:1[6-9]|[2-9]\d)?\d{2})|(?:29(\/|-|\.)0?2\3(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))|(?:0?[1-9]|1\d|2[0-8])(\/|-|\.)(?:(?:0?[1-9])|(?:1[0-2]))\4(?:(?:1[6-9]|[2-9]\d)?\d{2})"
  title="Une date valide au format JJ/MM/AAAA"
  required
>

Die beste HTML-Lösung ist aber nicht die Regex, sondern type="date". Der Browser zeigt eine zur Sprache des Nutzers passende Auswahl, verhindert den 30. Februar und überträgt den Wert immer im ISO-Format JJJJ-MM-TT, unabhängig von der Anzeige.

html
<input id="naissance" name="naissance" type="date" min="1900-01-01" max="2026-12-31" required>

Die Folge auf dem Server: Mit type="date" ist das ISO-Format zu prüfen, nicht das französische.

ISO-Variante JJJJ-MM-TT

Für ein ISO-Datum prüft eine kurze Regex die Form, und das Date-Objekt bestätigt, dass es den Tag gibt. Zusammen ersetzen die beiden die große Regex.

isValidIsoDate.js
function isValidIsoDate(value) {
  const m = /^(\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/.exec(value);
  if (!m) return false;

  const [, y, mo, d] = m.map(Number);
  const date = new Date(y, mo - 1, d);
  // new Date(2026, 1, 30) liefert den 2. März: wir prüfen, dass nichts verrutscht ist
  return date.getFullYear() === y && date.getMonth() === mo - 1 && date.getDate() === d;
}

console.log(isValidIsoDate('2026-09-07')); // true
console.log(isValidIsoDate('2026-02-30')); // false
console.log(isValidIsoDate('2026-13-01')); // false

Dieselbe Technik gilt für das französische Format: mit /^(\d{1,2})[\/.-](\d{1,2})[\/.-](\d{4})$/ zerlegen, dann mit Date prüfen. Das ist das JavaScript-Pendant zu checkdate() in PHP.

Und das amerikanische Format MM/TT/JJJJ?

Die Regex dieses Artikels ist französisch: erst der Tag, dann der Monat. Für ein amerikanisches Publikum vertauschst du die beiden ersten Blöcke oder normalisierst, einfacher noch, alles schon bei der Eingabe mit type="date" auf ISO. Eine Anwendung, die beide Reihenfolgen annimmt, ohne sie zu unterscheiden, liegt bei jedem Datum vom 1. bis zum 12. in der Hälfte der Fälle falsch.

Zwei Datumsangaben nach der Prüfung vergleichen

Sobald das Datum geprüft und in ein Date-Objekt umgewandelt ist, sind Vergleiche unmittelbar möglich: Die Operatoren < und > vergleichen Zeitpunkte, und die Subtraktion liefert einen Abstand in Millisekunden.

javascript
const debut = new Date('2026-09-07');
const fin = new Date('2026-12-25');

console.log(debut < fin);                                  // true
console.log(Math.round((fin - debut) / 86_400_000));       // 109 Tage

Die Formatierung für die Anzeige (toLocaleDateString, Intl.DateTimeFormat) ist ein anderes Thema, ebenso wie Datum und Uhrzeit in PHP oder Carbon in Laravel.

Regex oder Date-Objekt: was wählen?

  • Die große Regex, wenn du eine einzige Regel willst, identisch in JavaScript, PHP und HTML, ohne Abhängigkeit von einer Bibliothek oder von einem Date-Objekt.
  • Zerlegen + Date oder checkdate(), wenn Lesbarkeit zählt oder wenn du danach mit dem Datum weiterarbeiten musst: Du hast es bereits als Objekt.
  • type="date" für jedes moderne Formular: Die Eingabe ist geführt, der Wert normalisiert, und übrig bleibt nur die ISO-Prüfung auf dem Server.

Die anderen Validierungen der Reihe: E-Mail-Adresse, IP-Adresse, Postleitzahl und Passwort.

Häufige Fehler

Trennzeichen mischen Die Regex erfasst das erste Trennzeichen und verlangt danach dasselbe: „07/09-2026“ wird abgelehnt. Das ist gewollt, überrascht aber, wenn ein Formular mehrere Schreibweisen annimmt.
Vergessen, dass new Date stillschweigend korrigiert new Date(2026, 1, 30) wirft keinen Fehler: Es liefert den 2. März 2026. Vergleiche nach dem Erzeugen immer Jahr, Monat und Tag.
Ein ISO-Datum mit der französischen Regex prüfen 2026-09-07 wird von der TT/MM/JJJJ-Regex abgelehnt. Nimm die ISO-Variante aus diesem Artikel oder besser input type="date", das dieses Format schon liefert.
Sich auf das Anzeigeformat des Browsers verlassen Ein Feld type="date" zeigt auf Französisch „07/09/2026“ an, überträgt aber „2026-09-07“. Die Prüfung auf dem Server muss das ISO-Format erwarten.

HTMLJavaScriptPHP

Damien Flandrin Webentwickler seit 2010, Gründer von Gekkode und Email Impact. Jeder Artikel wird vor der Veröffentlichung an einem echten Projekt getestet. Kontakt
Newsletter

Neue Tests, Tutorials und Projekte, per E-Mail.

Reproduzierbare Tests, versionierter Code, datierte Ergebnisse. Niemals Spam.