Datum regex: DD/MM/JJJJ of JJJJ-MM-DD valideren (JavaScript, PHP)

Een datum regex die 30 februari en 31 april weigert, met de ISO- en Amerikaanse variant, de JavaScript- en PHP-functies, het HTML-attribuut, en het moment waarop je de regex beter laat voor wat ze is.

Regex om een datum te valideren: JavaScript, PHP en HTML
Kort antwoord

De regex hieronder valideert een Franse datum DD/MM/JJJJ (scheidingsteken /, - of .) en weigert onmogelijke datums zoals 30 februari of 31 april, schrikkeljaren inbegrepen. Voor een ISO-formaat JJJJ-MM-DD volstaat een korte regex als je daarna bevestigt met het object Date in JavaScript of checkdate() in PHP.

Een met de hand ingetypte datum valideren is lastiger dan het lijkt: “30/02/2026” heeft de juiste vorm en bestaat niet. De datum regex hieronder controleert het formaat DD/MM/JJJJ, met /, - of . als scheidingsteken, en weigert onmogelijke dagen, inclusief 29 februari in niet-schrikkeljaren. Daarna vind je de versies voor JavaScript, PHP en HTML, de ISO-variant en het geval waarin het object Date het beter doet dan een regex.

De kant-en-klare datum regex

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})$

Ze is lang omdat ze redeneert als een kalender. Drie takken, gescheiden door |:

  1. De 31 bestaat alleen in maanden met 31 dagen (01, 03, 05, 07, 08, 10, 12), en de 29 en de 30 in alle maanden behalve februari.
  2. 29 februari wordt alleen geaccepteerd als het jaar een schrikkeljaar is: deelbaar door 4 (0[48]|[2468][048]|[13579][26]), of een eeuwjaar deelbaar door 400 ((?:16|[2468][048]|[3579][26])00). 2000 en 2024 komen erdoor, 1900 en 2023 worden geweigerd.
  3. Van 1 tot en met 28 accepteren alle maanden de datum.

De backreferences \1, \2, \3 en \4 eisen dat het tweede scheidingsteken hetzelfde is als het eerste: 07/09/2026 en 07-09-2026 komen erdoor, 07/09-2026 niet.

Maar één scheidingsteken?

Om alleen de schuine streep te accepteren, vervang je elke (\/|-|\.) door (\/). De haakjes blijven nodig: de backreferences \1 tot \4 wijzen naar deze groepen.

De geteste datums

Echt resultaat van de regex onder Node.js 22 en PHP 8.5, identiek in beide engines:

Invoer Resultaat Reden
31/12/2026 geaccepteerd 31 in een maand met 31 dagen
29/02/2024 geaccepteerd 2024 is een schrikkeljaar
29/02/2000 geaccepteerd 2000 is deelbaar door 400
01/01/2026 en 1/1/2026 geaccepteerd de voorloopnul is optioneel
07-09-2026 en 07.09.2026 geaccepteerd andere scheidingstekens toegestaan
29/02/2023 geweigerd 2023 is geen schrikkeljaar
29/02/1900 geweigerd eeuwjaar niet deelbaar door 400
30/02/2026 geweigerd februari heeft nooit 30 dagen
31/04/2026 geweigerd april heeft 30 dagen
00/01/2026 geweigerd dag 0 bestaat niet
12/13/2026 geweigerd maand 13: de regex is inderdaad DD/MM en niet MM/DD
07/09-2026 geweigerd verschillende scheidingstekens
2026-09-07 geweigerd ISO-formaat, zie verderop
07/09/26 en 31/12/1899 geaccepteerd jaar met twee cijfers of vóór 1900: begrens dit indien nodig in je eigen code

JavaScript-versie

De functie verankert de regex met ^ en $, verwijdert de spaties rond de invoer en geeft een boolean terug.

validateDate.js
/**
 * Controleert een datum in het formaat DD/MM/JJJJ, DD-MM-JJJJ of DD.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

In een formulier is het patroon hetzelfde als bij het valideren van een e-mailadres: luisteren naar submit, preventDefault() aanroepen als de datum ongeldig is, het bericht naast het veld tonen.

PHP-versie

Controle aan de kant van de server is verplicht: JavaScript kan worden uitgeschakeld. Hetzelfde patroon werkt met preg_match(), en de backreferences schrijf je als \1 in een PHP-string tussen aanhalingstekens.

isValidDate.php
/**
 * Controleert een datum in het formaat DD/MM/JJJJ, DD-MM-JJJJ of DD.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)

Het alternatief zonder regex: checkdate()

PHP kan al zelf zeggen of een combinatie dag, maand, jaar bestaat. Een kleine regex om op te splitsen, dan checkdate() om te oordelen: dat is korter en leesbaarder.

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]); // maand, dag, jaar
}

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

Let op DateTime::createFromFormat('d/m/Y', '29/02/2023'): dit geeft niet false terug maar 1e maart 2023, en meldt de afwijking in DateTime::getLastErrors(). Voor een strikte controle is checkdate() veiliger.

HTML-versie

Typt de gebruiker de datum in vrije tekst, dan past het attribuut pattern de regex toe vóór het versturen. Het is impliciet verankerd: je laat ^ en $ 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
>

Maar de beste HTML-oplossing is geen regex: het is type="date". De browser toont een kiezer die is afgestemd op de taal van de gebruiker, verhindert 30 februari, en geeft de waarde altijd door in het ISO-formaat JJJJ-MM-DD, ongeacht de weergave.

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

Gevolg aan de kant van de server: met type="date" moet je het ISO-formaat valideren, niet het Franse formaat.

ISO-variant JJJJ-MM-DD

Voor een ISO-datum controleert een korte regex de vorm, en bevestigt het object Date dat de dag bestaat. Samen vervangen ze de grote 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) geeft 2 maart terug: we controleren dat er niets is verschoven
  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

Dezelfde techniek geldt voor het Franse formaat: opsplitsen met /^(\d{1,2})[\/.-](\d{1,2})[\/.-](\d{4})$/, en dan controleren met Date. Dat is het JavaScript-equivalent van checkdate() in PHP.

En het Amerikaanse formaat MM/DD/JJJJ?

De regex uit dit artikel is Frans: eerst de dag, dan de maand. Voor een Amerikaans publiek verwissel je de eerste twee blokken, of, eenvoudiger, normaliseer je alles naar ISO al bij de invoer met type="date". Een toepassing die beide volgordes accepteert zonder onderscheid, zit er in de helft van de gevallen naast voor alle datums van 1 tot en met 12.

Twee datums vergelijken na validatie

Zodra de datum gevalideerd en omgezet is naar een Date-object, zijn vergelijkingen direct: de operatoren < en > vergelijken de tijdstippen, en de aftrekking geeft een verschil in milliseconden.

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 dagen

Opmaak voor weergave (toLocaleDateString, Intl.DateTimeFormat) is een ander onderwerp, zoals datum en tijd in PHP of Carbon in Laravel.

Regex of Date-object: wat kies je?

  • De grote regex als je één regel wilt die identiek is in JavaScript, PHP en HTML, zonder afhankelijk te zijn van een library of een Date-object.
  • Opsplitsen + Date of checkdate() als leesbaarheid telt, of als je de datum daarna moet bewerken: je hebt hem al als object.
  • type="date" voor elk modern formulier: de invoer wordt begeleid, de waarde genormaliseerd, en alleen de ISO-controle aan de kant van de server blijft over.

De andere validaties uit de reeks: e-mailadres, IP-adres, postcode en wachtwoord.

Veelgemaakte fouten

Scheidingstekens mengen De regex legt het eerste scheidingsteken vast en eist daarna hetzelfde: “07/09-2026” wordt geweigerd. Dat is bedoeld, maar verrassend als een formulier meerdere invoerstijlen accepteert.
Vergeten dat new Date stilzwijgend corrigeert new Date(2026, 1, 30) geeft geen foutmelding: het levert 2 maart 2026 op. Vergelijk altijd jaar, maand en dag na constructie.
Een ISO-datum valideren met de Franse regex 2026-09-07 wordt geweigerd door de regex DD/MM/JJJJ. Gebruik de ISO-variant uit dit artikel, of beter nog, input type="date", die dit formaat al teruggeeft.
Vertrouwen op het weergaveformaat van de browser Een veld type="date" toont “07/09/2026” in het Nederlands, maar de doorgegeven waarde is “2026-09-07”. De validatie aan de kant van de server moet het ISO-formaat verwachten.

HTMLJavaScriptPHP

Damien Flandrin Webdeveloper sinds 2010, maker van Gekkode en Email Impact. Elk artikel wordt vóór publicatie getest op een echt project. Contact
Nieuwsbrief

Nieuwe tests, tutorials en projecten, per e-mail.

Reproduceerbare tests, geversioneerde code, gedateerde resultaten. Nooit spam.