130 votes

PHP Regex pour vérifier que la date est au format AAAA-MM-JJ

J'essaie de vérifier que les dates saisies par les utilisateurs finaux sont dans le format AAAA-MM-JJ. Les Regex n'ont jamais été mon point fort, je continue à obtenir une fausse valeur de retour pour le preg_match() que j'ai configuré.

Je suppose donc que j'ai fait une erreur dans l'expression rationnelle, détaillée ci-dessous.

$date="2012-09-12";

if (preg_match("^[0-9]{4}-[0-1][0-9]-[0-3][0-9]$",$date))
    {
        return true;
    }else{
        return false;
    }

Des idées ?

230voto

Avin Varghese Points 1693

Essayez ça.

$date="2012-09-12";

if (preg_match("/^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$/",$date)) {
    return true;
} else {
    return false;
}

136voto

Jon Points 194296

Il est probablement préférable d'utiliser un autre mécanisme pour cela.

La solution moderne, avec DateTime :

$dt = DateTime::createFromFormat("Y-m-d", $date);
return $dt !== false && !array_sum($dt::getLastErrors());

Cela valide également l'entrée : $dt !== false garantit que la date peut être analysée dans le format spécifié et que l'option array_sum est un moyen concis de s'assurer que PHP n'a pas fait de "décalage de mois" (par exemple, considérer que le 32 janvier est le 1er février). Voir DateTime::getLastErrors() pour plus d'informations.

Une solution à l'ancienne avec explode y checkdate :

list($y, $m, $d) = array_pad(explode('-', $date, 3), 3, 0);
return ctype_digit("$y$m$d") && checkdate($m, $d, $y);

Cela permet de valider que l'entrée est une date valide également. Vous pouvez le faire avec une regex bien sûr, mais cela va être plus compliqué -- et le 29 février ne peut pas du tout être validé avec une regex.

L'inconvénient de cette approche est que vous devez faire très attention à rejeter toutes les "mauvaises" entrées possibles tout en n'émettant en aucun cas un avis. Voici comment procéder :

  • explode est limité à retourner 3 jetons (de sorte que si l'entrée est "1-2-3-4", $d deviendra "3-4")
  • ctype_digit est utilisé pour s'assurer que l'entrée ne contient pas de caractères non numériques (à l'exception des tirets).
  • array_pad est utilisé (avec une valeur par défaut qui entraînera l'utilisation de l'option checkdate pour échouer) pour s'assurer que suffisamment d'éléments sont retournés de sorte que si l'entrée est "1-2" list() n'émettra pas d'avis

51voto

Shyju KP Points 511

aaaa-mm-jj : /^((((19|[2-9]\d)\d{2})\-(0[13578]|1[02])\-(0[1-9]|[12]\d|3[01]))|(((19|[2-9]\d)\d{2})\-(0[13456789]|1[012])\-(0[1-9]|[12]\d|30))|(((19|[2-9]\d)\d{2})\-02\-(0[1-9]|1\d|2[0-8]))|(((1[6-9]|[2-9]\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00))\-02\-29))$/g

aaaa/mm/jj : /^((((19|[2-9]\d)\d{2})\/(0[13578]|1[02])\/(0[1-9]|[12]\d|3[01]))|(((19|[2-9]\d)\d{2})\/(0[13456789]|1[012])\/(0[1-9]|[12]\d|30))|(((19|[2-9]\d)\d{2})\/02\/(0[1-9]|1\d|2[0-8]))|(((1[6-9]|[2-9]\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00))\/02\/29))$/g

mm-jj-aaaa : /^(((0[13578]|1[02])\-(0[1-9]|[12]\d|3[01])\-((19|[2-9]\d)\d{2}))|((0[13456789]|1[012])\-(0[1-9]|[12]\d|30)\-((19|[2-9]\d)\d{2}))|(02\-(0[1-9]|1\d|2[0-8])\-((19|[2-9]\d)\d{2}))|(02\-29\-((1[6-9]|[2-9]\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00))))$/g

mm/jj/aaaa : /^(((0[13578]|1[02])\/(0[1-9]|[12]\d|3[01])\/((19|[2-9]\d)\d{2}))|((0[13456789]|1[012])\/(0[1-9]|[12]\d|30)\/((19|[2-9]\d)\d{2}))|(02\/(0[1-9]|1\d|2[0-8])\/((19|[2-9]\d)\d{2}))|(02\/29\/((1[6-9]|[2-9]\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00))))$/g

jj/mm/aaaa : /^(((0[1-9]|[12]\d|3[01])\/(0[13578]|1[02])\/((19|[2-9]\d)\d{2}))|((0[1-9]|[12]\d|30)\/(0[13456789]|1[012])\/((19|[2-9]\d)\d{2}))|((0[1-9]|1\d|2[0-8])\/02\/((19|[2-9]\d)\d{2}))|(29\/02\/((1[6-9]|[2-9]\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00))))$/g

jj-mm-aaaa : /^(((0[1-9]|[12]\d|3[01])\-(0[13578]|1[02])\-((19|[2-9]\d)\d{2}))|((0[1-9]|[12]\d|30)\-(0[13456789]|1[012])\-((19|[2-9]\d)\d{2}))|((0[1-9]|1\d|2[0-8])\-02\-((19|[2-9]\d)\d{2}))|(29\-02\-((1[6-9]|[2-9]\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00))))$/g

40voto

Graham Points 694

Critères :

Toute année divisible par 4 est une année bissextile, sauf si elle est divisible par 100, sauf si elle est divisible par 400. Donc :

2004 - leap year - divisible by 4
1900 - not a leap year - divisible by 4, but also divisible by 100
2000 - leap year - divisible by 4, also divisible by 100, but divisible by 400

Le mois de février compte 29 jours dans une année bissextile et 28 jours dans une année non bissextile.

30 jours en avril, juin, septembre et novembre

31 jours en janvier, mars, mai, juillet, août, octobre et décembre.

Test :

Les dates suivantes devraient toutes être validées :

1976-02-29
2000-02-29
2004-02-29
1999-01-31

Les dates suivantes ne devraient pas être validées :

2015-02-29
2015-04-31
1900-02-29
1999-01-32
2015-02-00

Gamme :

Nous allons tester les dates du 1er janvier 1000 au 31 décembre 2999. Techniquement, le calendrier grégorien actuellement utilisé n'est entré en vigueur qu'en 1753 pour l'Empire britannique et à diverses dates dans les années 1600 pour les pays d'Europe, mais je ne vais pas m'en inquiéter.

Regex pour tester la présence d'une année bissextile :

Les années divisibles par 400 :

1200|1600|2000|2400|2800
can be shortened to:
(1[26]|2[048])00

if you wanted all years from 1AD to 9999 then this would do it:
(0[48]|[13579][26]|[2468][048])00
if you're happy with accepting 0000 as a valid year then it can be shortened:
([13579][26]|[02468][048])00

Les années divisibles par 4 :

[12]\d([02468][048]|[13579][26])

Les années divisibles par 100 :

[12]\d00

Non divisible par 100 :

[12]\d([1-9]\d|\d[1-9])

Les années divisibles par 100 mais pas par 400 :

((1[1345789])|(2[1235679]))00

Divisible par 4 mais pas par 100 :

[12]\d([2468][048]|[13579][26]|0[48])

Les années bissextiles :

divisible by 400 or (divisible by 4 and not divisible by 100)
((1[26]|2[048])00)|[12]\d([2468][048]|[13579][26]|0[48])

Non divisible par 4 :

[12]\d([02468][1235679]|[13579][01345789])

Pas une année bissextile :

Not divisible by 4 OR is divisible by 100 but not by 400
([12]\d([02468][1235679]|[13579][01345789]))|(((1[1345789])|(2[1235679]))00)

Mois et jour valides, sauf février (MM-DD) :

((01|03|05|07|08|10|12)-(0[1-9]|[12]\d|3[01]))|((04|06|09|11)-(0[1-9]|[12]\d|30))
shortened to:
((0[13578]|1[02])-(0[1-9]|[12]\d|3[01]))|((0[469]|11)-(0[1-9]|[12]\d|30))

Février avec 28 jours :

02-(0[1-9]|1\d|2[0-8])

Février avec 29 jours :

02-(0[1-9]|[12]\d)

Date de validité :

(leap year followed by (valid month-day-excluding-february OR 29-day-february)) 
OR
(non leap year followed by (valid month-day-excluding-february OR 28-day-february))

((((1[26]|2[048])00)|[12]\d([2468][048]|[13579][26]|0[48]))-((((0[13578]|1[02])-(0[1-9]|[12]\d|3[01]))|((0[469]|11)-(0[1-9]|[12]\d|30)))|(02-(0[1-9]|[12]\d))))|((([12]\d([02468][1235679]|[13579][01345789]))|((1[1345789]|2[1235679])00))-((((0[13578]|1[02])-(0[1-9]|[12]\d|3[01]))|((0[469]|11)-(0[1-9]|[12]\d|30)))|(02-(0[1-9]|1\d|2[0-8]))))

Voilà donc une regex pour les dates comprises entre le 1er janvier 1000 et le 31 décembre 2999 au format AAAA-MM-JJ.

Je pense qu'il peut être raccourci, mais je laisse le soin à quelqu'un d'autre de le faire.

Cela correspondra à toutes les dates valides. Si vous voulez qu'elle ne soit valide que lorsqu'elle ne contient qu'une seule date et rien d'autre, il faut l'entourer de la mention ^( )$ comme ça :

^(((((1[26]|2[048])00)|[12]\d([2468][048]|[13579][26]|0[48]))-((((0[13578]|1[02])-(0[1-9]|[12]\d|3[01]))|((0[469]|11)-(0[1-9]|[12]\d|30)))|(02-(0[1-9]|[12]\d))))|((([12]\d([02468][1235679]|[13579][01345789]))|((1[1345789]|2[1235679])00))-((((0[13578]|1[02])-(0[1-9]|[12]\d|3[01]))|((0[469]|11)-(0[1-9]|[12]\d|30)))|(02-(0[1-9]|1\d|2[0-8])))))$

Si vous souhaitez qu'il s'agisse d'une entrée de date facultative (c'est-à-dire qu'elle peut être vide ou une date valide), ajoutez alors ^$| au début, comme ça :

^$|^(((((1[26]|2[048])00)|[12]\d([2468][048]|[13579][26]|0[48]))-((((0[13578]|1[02])-(0[1-9]|[12]\d|3[01]))|((0[469]|11)-(0[1-9]|[12]\d|30)))|(02-(0[1-9]|[12]\d))))|((([12]\d([02468][1235679]|[13579][01345789]))|((1[1345789]|2[1235679])00))-((((0[13578]|1[02])-(0[1-9]|[12]\d|3[01]))|((0[469]|11)-(0[1-9]|[12]\d|30)))|(02-(0[1-9]|1\d|2[0-8])))))$

20voto

k102 Points 3481

Vous pouvez le faire comme ça :

if (preg_match("/\d{4}\-\d{2}-\d{2}/", $date)) {
    echo 'true';
} else {
    echo 'false';
}

mais vous feriez mieux d'utiliser celui-ci :

$date = DateTime::createFromFormat('Y-m-d', $date);
if ($date) {
    echo $date -> format('Y-m-d');
}

dans ce cas, vous obtiendrez un objet, ce qui est bien plus facile à utiliser que de simples chaînes de caractères.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X