it-swarm.dev

PHP Regex ke kontrole data je ve formátu RRRR-MM-DD

Snažím se zkontrolovat, zda jsou data zadaná koncovými uživateli v RRRR-MM-DD. Regex nikdy nebyl mou silnou stránkou, stále dostávám falešnou návratovou hodnotu pro preg_match (), kterou mám k dispozici.

Takže předpokládám, že jsem udělal nepořádek z regexu, podrobně popsaného níže.

$date="2012-09-12";

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

Nějaké nápady?

76
cosmicsafari

Zkuste to.

$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

Je to asi lepší použít jiný mechanismus.

Moderní řešení s DateTime :

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

To také potvrzuje vstup: $dt !== false zajišťuje, že datum může být analyzováno se specifikovaným formátem a trik array_sum je přísný způsob, jak zajistit, aby PHP nedělal „měsíční posun“ (např. Za to, že 32. leden je 1. února ). Pro více informací viz DateTime::getLastErrors() .

Řešení staré školy s explode a checkdate :

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

To potvrzuje, že vstup je platným datem. Můžete to udělat s regexem samozřejmě, ale bude to více rozruch - a 29. února nelze validovat s regexem vůbec.

Nevýhoda tohoto přístupu spočívá v tom, že musíte být velmi opatrní, abyste odmítli všechny možné „špatné“ vstupy, aniž byste za jakýchkoli okolností vydávali oznámení. Zde je postup:

  • explode je omezeno na návrat 3 tokeny (takže pokud je vstup "1-2-3-4", $d se stane "3-4")
  • ctype_digit se používá k zajištění, že vstup neobsahuje žádné nečíselné znaky (kromě pomlček)
  • array_pad se používá (s výchozí hodnotou, která způsobí, že checkdate se nezdaří), aby se ujistilo, že je vráceno dostatek prvků, takže pokud je vstup "1-2" list() nevydá oznámení
84
Jon

Kritéria:

Každý rok dělitelný 4 je přestupný rok, s výjimkou případů, kdy je dělitelný 100, pokud není dělitelný 400. Takže:

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

Únor má 29 dnů v přestupném roce a 28, když není přestupný rok

30 dní v dubnu, červnu, září a listopadu

31 dní v lednu, březnu, květnu, červenci, srpnu, říjnu a prosinci

Test:

Následující data by měla projít validací:

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

Následující data by měla selhat ověření:

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

Rozsah:

Budeme testovat data od 1. ledna 1000 do 31. prosince 2999. Technicky je aktuálně používaný gregoriánský kalendář používán pouze v roce 1753 pro britské impérium a v různých letech 1600 pro země v Evropě, ale nejsem bude se toho bát.

Regex testuje rok přestupu:

Roky dělitelné 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

Roky dělitelné 4:

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

Roky dělitelné 100:

[12]\d00

Není dělitelné číslem 100:

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

Roky dělitelné 100, ale ne 400:

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

Dělí se 4, ale ne 100:

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

Přechodné roky:

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

Není dělitelné 4:

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

Není přestupný rok:

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

Platný měsíc a den bez února (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))

Únor s 28 dny:

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

Únor s 29 dny:

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

Platné datum:

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

Takže tam máte regex pro data mezi 1. lednem 1000 a 31. prosincem 2999 ve formátu RRRR-MM-DD.

Domnívám se, že to může být trochu zkráceno, ale nechám to na někom jiném.

To bude odpovídat všem platným datům. Pokud chcete, aby byla platná pouze v případě, že obsahuje pouze jedno datum a nic jiného, ​​zabalte jej do souboru ^( )$ takto:

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

Pokud chcete, aby byla volitelná položka data (tj. Může být prázdná nebo platná), přidejte na začátek ^$|:

^$|^(((((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

rrrr-mm-dd: /^((((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

rrrr/mm/dd: /^((((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-dd-rrrr: /^(((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/dd/rrrr: /^(((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

dd/mm/rrrr: /^(((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

dd-mm-rrrr: /^(((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

29
Shyju KP

Můžete to udělat takto:

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

ale raději byste měli použít tento:

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

v tomto případě se dostanete objekt, který je snadnější použití než jen struny.

12
k102

Můžete použít preg_match s funkcí checkdate php

$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

Vím, že je to stará otázka. Ale myslím, že mám dobré řešení.

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

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

Můžeš to zkusit. Pokud změníte datum na 21.02.2016, echo je nepravdivé. A pokud změníte formát po tom na d.m.Y echo je pravda.

S tímto jednoduchým kódem byste měli být schopni zkontrolovat, který formát data se použije, aniž by jej kontroloval regex.

Možná, že je to člověk, který to bude testovat v každém případě. Ale myslím, že můj nápad je obecně platný. Zdá se mi to logické.

6
Micha93

preg_match potřebuje/nebo jiný znak jako oddělovač.

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

také byste měli zkontrolovat platnost tohoto data, takže byste skončit s něčím jako 9999-19-38

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

můžeš použít 

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

Zkontrolujte a ověřte datum YYYY-MM-DD v jednom řádku

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;
}

Výstupem bude:

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)

Den a měsíc bez úvodní nuly jsou povoleny. Pokud to nechcete povolit, regexp by měl být:

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

To by vám mělo sdělit, zda je formát platný a zda je datum vstupu platné.

    $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

Pokud je to nějaká pomoc, zde je regex pro formát j-n-Y (rok musí být větší než 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

Můžete to také udělat takto:

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

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

}

To nejen zkontroluje formát, ale také platnost data self, protože DateTime vytvoří pouze platná data a to musí odpovídat vstupu.

1
kasimir

Chcete-li tento typ data shodovat, použijte:

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

Chcete-li pracovat s daty v PHP, měli byste se řídit standardem php, aby daný regex zajistil, že máte platné datum, které může fungovat s 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

Vše záleží na tom, jak přísně chcete tuto funkci mít. Pokud například nechcete povolit měsíce nad 12 a více než 31 dní (v závislosti na měsíci, které by vyžadovaly logiku zápisu), mohlo by to být komplikované:

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

Osobně bych šel na: /^([0-9]{4}\-([0-9]{2}\-[0-9]{2})$/

0
Ariaan

[Pokud používáte Symfony 4.1.2, zkuste to] [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

Tato metoda může být užitečná pro ověření data v PHP. Aktuální metoda je pro formát mm/dd/rrrr. Musíte aktualizovat posloupnost parametrů v checkdate podle formátu a oddělovače v explode .

    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