web-dev-qa-db-fra.com

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

J'essaie de vérifier que les dates saisies par les utilisateurs finaux sont dans AAAA-MM-JJ. Regex n'a jamais été mon point fort, je reçois toujours une valeur de retour fausse pour le paramètre preg_match () que j'ai configuré.

Donc, je suppose que j'ai gâché la regex, 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 pensées?

76
cosmicsafari

Essaye ç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;
}
156
Avin Varghese

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());

Ceci valide également la saisie: $dt !== false garantit que la date peut être analysée avec le format spécifié et l’astuce array_sum est une manière fastidieuse de s’assurer que PHP n’a pas effectué le "décalage de mois" (par exemple, considérons que le 32 janvier est le 1er février ). Voir DateTime::getLastErrors() pour plus d'informations.

Solution old-school avec explode et checkdate :

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

Ceci valide que la saisie est également une date valide. Vous pouvez le faire avec une expression régulière bien sûr, mais cela va être plus compliqué - et le 29 février ne peut pas être validé avec une expression régulière.

L'inconvénient de cette approche est que vous devez faire très attention à rejeter toutes les "mauvaises" entrées possibles sans émettre d'avis, quelles que soient les circonstances. Voici comment:

  • explode est limité pour renvoyer 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 aucun caractère non numérique (à l'exception des tirets)
  • array_pad est utilisé (avec une valeur par défaut provoquant l'échec de checkdate) pour s'assurer que suffisamment d'éléments sont retournés pour que, si l'entrée est "1-2", list() n'émette pas d'avis
84
Jon

Critères:

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

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

Février a 29 jours dans une année bissextile et 28 quand ce n'est pas une année 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 doivent toutes être validées:

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

Les dates suivantes doivent toutes échouer la validation:

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

Intervalle:

Nous allons tester les dates du 1er janvier 1000 au 31 décembre 2999. Techniquement, le calendrier grégorien actuellement utilisé n'a été utilisé qu'en 1753 pour l'Empire britannique et à diverses années dans les années 1600 pour les pays d'Europe, mais je ne va pas se soucier de ça.

Regex à tester pour 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)

Valable mois et jour sauf février (MM-JJ):

((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 valide:

(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]))))

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

Je suppose que cela peut être raccourci un peu, mais je laisserai cela à quelqu'un d'autre.

Cela correspondra à toutes les dates valides. Si vous voulez qu'il ne soit valide que s'il ne contient qu'une date et rien d'autre, placez-le dans ^( )$ comme suit:

^(((((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 le souhaitez pour une entrée de date facultative (c’est-à-dire qu’elle peut être vide ou valide), ajoutez ^$| au début, comme suit:

^$|^(((((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])))))$
33
Graham

Vous pouvez le faire de cette façon:

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 plus facile à utiliser que de simples chaînes.

12
k102

Vous pouvez utiliser un preg_match avec une fonction php checkdate

$date  = "2012-10-05";
$split = array();
if (preg_match ("/^([0-9]{4})-([0-9]{2})-([0-9]{2})$/", $date, $split))
{
    return checkdate($split[2], $split[3], $split[1]);
}

return false;
7
Jonathan Muller

Je sais que c'est une vieille question. Mais je pense avoir une bonne solution.

$date = "2016-02-21";
$format = "Y-m-d";

if(date($format, strtotime($date)) == date($date)) {
    echo "true";
} else {
    echo "false";
}

Tu peux l'essayer. Si vous modifiez la date en 21.02.2016, l'écho est faux. Et si vous changez le format par la suite à d.m.Y, l'écho est vrai.

Avec ce code simple, vous devriez pouvoir vérifier quel format de date est utilisé sans le vérifier par la regex.

Peut-être qu'une personne le testera dans tous les cas. Mais je pense que mon idée est généralement valable. Pour moi, cela semble logique.

6
Micha93

preg_match nécessite un/ou un autre caractère comme séparateur.

preg_match("/^[0-9]{4}-[0-1][0-9]-[0-3][0-9]$/",$date)

vous devriez également vérifier la validité de cette date pour ne pas vous retrouver avec quelque chose comme 9999-19-38

bool checkdate ( int $month , int $day , int $year )
4
marianboda

vous pouvez utiliser 

function validateDate($date, $format = 'Y-m-d H:i:s')
{
    $d = DateTime::createFromFormat($format, $date);
    return $d && $d->format($format) == $date;
}

$date="2012-09-12";    
echo validateDate($date, 'Y-m-d'); // true or false
3
jack

Vérifier et valider la date YYYY-MM-DD en une seule ligne

function isValidDate($date) {
    return preg_match("/^(\d{4})-(\d{1,2})-(\d{1,2})$/", $date, $m)
        ? checkdate(intval($m[2]), intval($m[3]), intval($m[1]))
        : false;
}

La sortie sera:

var_dump(isValidDate("2018-01-01")); // bool(true)
var_dump(isValidDate("2018-1-1"));   // bool(true)
var_dump(isValidDate("2018-02-28")); // bool(true)
var_dump(isValidDate("2018-02-30")); // bool(false)

Le jour et le mois sans zéro sont autorisés. Si vous ne voulez pas autoriser cela, l'expression rationnelle devrait être:

"/^(\d{4})-(\d{2})-(\d{2})$/"
1
Vladislav Savchuk

Cela devrait vous dire si le format est valide et si la date de saisie est valide.

    $datein = '2012-11-0';

    if(preg_match('/^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$/', $datein)){
        echo 'good';
    }else{
        echo 'no good';
    }
1
Kenzo

Si cela peut vous aider, voici une regex pour le format j-n-Y (l'année doit être supérieure à 2018):

if (preg_match('/^([1-9]|[1-2][0-9]|[3][0-1])\-([1-9]|[1][0-2])\-(?:20)([1][8-9]|[2-9][0-9])$/', $date)) {
   // Do stuff
}
1
Filip

Vous pouvez aussi le faire comme ceci:

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

    // date is correctly formatted and valid, execute some code

}

Cela vérifiera non seulement le format, mais aussi la validité de la date, puisque DateTime ne créera que des dates valides et que cela doit correspondre à la saisie.

1
kasimir

Si vous souhaitez faire correspondre ce type de date, utilisez:

preg_match("~^\d{4}-\d{2}-\d{2}$~", $date)
1
ujovlado

Pour travailler avec des dates en php, vous devez suivre le standard php afin que la regex donnée assure que vous avez une date valide pouvant fonctionner avec PHP.

    preg_match("/^([0-9]{4})-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$/",$date)
0
Pratik Soni

Format 1: $ format1 = "2012-12-31";

Format 2: $ format2 = "31-12-2012";

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

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

À partir de Laravel 5.7 et format de date c'est-à-dire: 31/12/2019

function checkDateFormat(string $date): bool
{
    return preg_match("/^(0[1-9]|1[0-2])\/(0[1-9]|[1-2][0-9]|3[0-1])\/[0-9]{4}$/", $date);
}
0
Mexidense

Tout dépend de la sévérité à laquelle vous souhaitez que cette fonction soit appliquée. Par exemple, si vous ne souhaitez pas autoriser les mois au-dessus de 12 et les jours au-dessus de 31 (cela dépend du mois, cela nécessiterait d'écrire une logique de date), cela pourrait devenir assez compliqué:

function checkDate($date)
{
  $regex = '/^' . 
    '(' .

    // Allows years 0000-9999
    '(?:[0-9]{4})' .
    '\-' .

    // Allows 01-12
    '(?:' .
    '(?:01)|(?:02)|(?:03)|(?:04)|(?:05)|(?:06)|(?:07)|(?:08)|(?:09)|(?:10)|' .
    '(?:11)|(?:12)' .
    ')' .
    '\-' .

    // Allows 01-31
    '(?:' .
    '(?:01)|(?:02)|(?:03)|(?:04)|(?:05)|(?:06)|(?:07)|(?:08)|(?:09)|(?:10)|' .
    '(?:11)|(?:12)|(?:13)|(?:14)|(?:15)|(?:16)|(?:17)|(?:18)|(?:19)|(?:20)|' .
    '(?:21)|(?:22)|(?:23)|(?:24)|(?:25)|(?:26)|(?:27)|(?:28)|(?:29)|(?:30)|' .
    '(?:31)' .
    ')' .

    '$/';

  if ( preg_match($regex, $date) ) {
    return true;
  }

  return false;
}

$result = checkDate('2012-09-12');

Personnellement, je vais juste pour: /^([0-9]{4}\-([0-9]{2}\-[0-9]{2})$/

0
Ariaan

[Si vous utilisez Symfony 4.1.2, essayez ceci] [1]

  $validDate = explode("-",$request->get('date'));
        if (checkdate(filter_var($validDate[1],FILTER_SANITIZE_NUMBER_INT),filter_var($validDate[0],FILTER_SANITIZE_NUMBER_INT),filter_var($validDate[2],FILTER_SANITIZE_NUMBER_INT))){
            $date = date_create(filter_var($request->get('date'),FILTER_SANITIZE_SPECIAL_CHARS));
        }else{
            return $this->redirectToRoute('YOUR_ROUTE');
        }
0
DIDIT B

Cette méthode peut être utile pour valider la date en PHP. La méthode actuelle est pour le format mm/jj/aaaa. Vous devez mettre à jour la séquence de paramètres dans checkdate selon votre format et votre délimiteur dans exploser .

    function isValidDate($dt)
    {
        $dtArr = explode('/', $dt);
        if (!empty($dtArr[0]) && !empty($dtArr[1]) && !empty($dtArr[2])) {
            return checkdate((int) $dtArr[0], (int) $dtArr[1], (int) $dtArr[2]);
        } else {
            return false;
        }
    }
0
Amit Garg