Poprawnie określ, czy ciąg daty jest prawidłową datą w tym formacie

184

Otrzymuję ciąg daty od interfejsu API, który jest sformatowany jako yyyy-mm-dd.

Obecnie używam wyrażenia regularnego do sprawdzania poprawności formatu ciągu, co działa dobrze, ale widzę przypadki, w których może to być poprawny format zgodny z ciągiem, ale w rzeczywistości niepoprawna data. tj. 2013-13-01na przykład.

Czy jest lepszy sposób na PHP, aby wziąć taki ciąg znaków 2013-13-01i powiedzieć, czy jest to poprawna data dla formatu yyyy-mm-dd?

Marty Wallace
źródło
Możliwy duplikat weryfikacji daty php
Vivek Athalye
2
Tutaj odpowiedź jest zdecydowanie lepsza.
Sebastien

Odpowiedzi:

454

Możesz użyć DateTimeklasy w tym celu:

function validateDate($date, $format = 'Y-m-d')
{
    $d = DateTime::createFromFormat($format, $date);
    // The Y ( 4 digits year ) returns TRUE for any integer with any number of digits so changing the comparison from == to === fixes the issue.
    return $d && $d->format($format) === $date;
}

[ Funkcja zaczerpnięta z tej odpowiedzi . Również na php.net . Pierwotnie napisany przez Glavić . ]


Przypadki testowe:

var_dump(validateDate('2013-13-01'));  // false
var_dump(validateDate('20132-13-01')); // false
var_dump(validateDate('2013-11-32'));  // false
var_dump(validateDate('2012-2-25'));   // false
var_dump(validateDate('2013-12-01'));  // true
var_dump(validateDate('1970-12-01'));  // true
var_dump(validateDate('2012-02-29'));  // true
var_dump(validateDate('2012', 'Y'));   // true
var_dump(validateDate('12012', 'Y'));  // false

Próbny!

Amal Murali
źródło
14
Jeśli używasz PHP 5.2.x, powinieneś użyć, strtotimeaby uzyskać uniksowy znacznik czasu, a następnie date('Y-m-d', $t)uzyskać datę ciągu. Następnie porównujesz je tak jak w tej odpowiedzi.
pedromanoel
2
@pedromanoel: do standardowego wprowadzania daty i godziny możesz użyć strtotime, ale w przypadku niestandardowych formatów, które strtotimenie rozpoznają, będziesz potrzebować innego rozwiązania. A dla wersji PHP 5.2 wsparcie zatrzymane w styczniu 2011, dla 5.3 wsparcie zatrzymane w sierpniu 2014.
Glavić
2
rozważ tę datęvar_dump( validateDate('2012-2-9'));
króluje
4
Funkcja działa poprawnie. Zwrócił wartość false, ponieważ określony format thr był niepoprawny. Jeśli chcesz używać dnia i miesiąca bez zer wiodących, format powinien mieć 'Y-n-j'postać @reignsly.
Amal Murali,
1
@ AntonyD'Andrea: nie, nie będzie działać bez tej części. Ponieważ $dnie będzie to fałszywe, jeśli podasz datę, która ma przepełnione części, na przykład 13 miesiąc (2013-13-01). Ale to naprawdę zależy od tego, czego chcesz. Jeśli chcesz na przykład validateDate('2015-55-66')być poprawny, to tak, musisz tylko sprawdzić, czy $djest obiektem, czy nie.
Glavić,
81

Sprawdź, czy jakikolwiek ciąg jest datą

function checkIsAValidDate($myDateString){
    return (bool)strtotime($myDateString);
}
arsh
źródło
2
To potwierdza cały zakres prawidłowych formatów dat, nie tylko yyyy-mm-dd.
EWit
10
@MichelAyres dzieje się tak, ponieważ php uważa 2015-02-30za prawidłową datę, ponieważ gdy podany dzień jest większy niż liczba dni w danym miesiącu (lub ujemna) php przechodzi na następny miesiąc. Ponieważ gwarantowana jest data w formacie, yyyy-mm-ddmożna to naprawić, zmieniając wartość powrotu na return (bool)strtotime($myDateString) && date("Y-m-d", strtotime($myDateString)) == $myDateString;.
elitechief21
2
Dlaczego (bool)strtotime('s')okazuje się to prawdą?
Peon
zwraca to również 1 checkIsAValidDate („F”);
Vaibhav Bhanushali
możemy użyć $myDateString = str_replace("/", '-', $myDateString);przed powrotem, jeśli ciąg daty zawiera ukośniki (/), takie jak: - dd / mm / rrrr
Yashrajsinh Jadeja
37

Używaj w prosty sposób z wbudowaną funkcją php:

function checkmydate($date) {
  $tempDate = explode('-', $date);
  // checkdate(month, day, year)
  return checkdate($tempDate[1], $tempDate[2], $tempDate[0]);
}

Test

   checkmydate('2015-12-01'); //true
   checkmydate('2015-14-04'); //false
winorośl
źródło
1
Ładne proste rozwiązanie, zadziałało po raz pierwszy, dzięki :)
David Bell
Ponownie, gdy używasz testu, w ifcelu zwrócenia truelub po prostu falsezwróć test.
Victor Schröder,
4
Zakłada się, że w tablicy $ tempDate są co najmniej 3 elementy.
osoba 27
2
@ person27:return sizeof($tmpDate) == 3 && checkdate($tmpDate[1]...
neurino
@vineet - to się nie udaje. Jeśli rok jest 2, 20, 202, 2020lub nawet jeśli rok jest 20201- to prawda powraca za każdym razem.
rolinger
16

Sprawdź, czy ciąg znaków jest datą, nawet jeśli ciąg znaków jest niestandardowy

(strtotime nie akceptuje żadnego niestandardowego formatu)

<?php
function validateDateTime($dateStr, $format)
{
    date_default_timezone_set('UTC');
    $date = DateTime::createFromFormat($format, $dateStr);
    return $date && ($date->format($format) === $dateStr);
}

// These return true
validateDateTime('2001-03-10 17:16:18', 'Y-m-d H:i:s');
validateDateTime('2001-03-10', 'Y-m-d');
validateDateTime('2001', 'Y');
validateDateTime('Mon', 'D');
validateDateTime('March 10, 2001, 5:16 pm', 'F j, Y, g:i a');
validateDateTime('March 10, 2001, 5:16 pm', 'F j, Y, g:i a');
validateDateTime('03.10.01', 'm.d.y');
validateDateTime('10, 3, 2001', 'j, n, Y');
validateDateTime('20010310', 'Ymd');
validateDateTime('05-16-18, 10-03-01', 'h-i-s, j-m-y');
validateDateTime('Monday 8th of August 2005 03:12:46 PM', 'l jS \of F Y h:i:s A');
validateDateTime('Wed, 25 Sep 2013 15:28:57', 'D, d M Y H:i:s');
validateDateTime('17:03:18 is the time', 'H:m:s \i\s \t\h\e \t\i\m\e');
validateDateTime('17:16:18', 'H:i:s');

// These return false
validateDateTime('2001-03-10 17:16:18', 'Y-m-D H:i:s');
validateDateTime('2001', 'm');
validateDateTime('Mon', 'D-m-y');
validateDateTime('Mon', 'D-m-y');
validateDateTime('2001-13-04', 'Y-m-d');
migli
źródło
Podczas korzystania z testu w ifcelu zwrócenia truelub po prostu falsezwróć test.
Victor Schröder,
Ale 2018-3-24 zwraca false, metoda odbiera 2018-3-24, gdy format ma zastosowanie, return 2018-03-24; jak mogę zwrócić prawdę na dwa sposoby?
Aquiles Perez
12

Ta opcja jest nie tylko prosta, ale także akceptuje prawie każdy format, chociaż w niestandardowych formatach może być wadliwa.

$timestamp = strtotime($date);
return $timestamp ? $date : null;
galki
źródło
To powinna być zaakceptowana odpowiedź! Znacznie prostsze.
Webmaster G
2
Należy pamiętać, że nie będzie to działać w formacie brytyjskim (d / m / Y), ponieważ zakłada, że ​​konwertuje on format amerykański (m / d / Y). Wydaje się, że działa tylko wtedy, gdy dzień jest krótszy niż 12!
Sylvester Saracevas
@galki - przetestowałem to i zawiodło niektóre wartości. Jeśli $ date = '202-03-31', zwraca true. Ale to nie jest poprawna data. Odkryłem, że jeśli zmienisz rok na nieważny, zawsze zwraca on wartość true.
rolinger
@rolinger dziwne ... może widzi 202AD? Jaką datą roku jest „strtotime”?
galki
@galki - nie jestem pewien, ale 202zwraca liczbę ujemną - która nadal przechodzi test.
rolinger
10

Możesz także przeanalizować datę dla daty miesiąca i roku, a następnie możesz użyć funkcji PHP, o checkdate()której możesz przeczytać tutaj: http://php.net/manual/en/function.checkdate.php

Możesz także wypróbować ten:

$date="2013-13-01";

if (preg_match("/^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$/",$date))
    {
        echo 'Date is valid';
    }else{
        echo 'Date is invalid';
    }
Suvash sarker
źródło
w przypadku lutego (skomentowane przez elitechief21) funkcja isValidDate ($ date) {return preg_match ("/ ^ [0-9] {4} - (0 [1-9] | 1 [0-2]) - (0 [1-9] | [1-2] [0-9] | 3 [0-1]) $ / ", $ date) && date (" Ymd ", strtotime ($ date)) == $ date; }
abdulwadood,
4
To rozwiązanie jest bardzo słabe, ponieważ w żaden sposób nie sprawdza poprawności daty. Każdy miesiąc może mieć 31 dni, w tym luty. Każdy rok może mieć 29 lutego. Sprawdzanie poprawności dat za pomocą RegExp wymagałoby czegoś znacznie bardziej złożonego, z referencjami wstecz i negatywnymi prognozami.
Victor Schröder,
9

Najłatwiejszym sposobem sprawdzenia, czy podana data jest poprawna, jest prawdopodobnie konwersja jej na unikstime przy użyciu strtotime, sformatowanie jej do formatu podanej daty, a następnie porównanie:

function isValidDate($date) { return date('Y-m-d', strtotime($date)) === $date; }

Oczywiście możesz użyć wyrażenia regularnego, aby sprawdzić poprawność, ale będzie on ograniczony do danego formatu, za każdym razem będziesz musiał go edytować, aby spełnić inne formaty, a także będzie więcej niż wymagany. Wbudowane funkcje to najlepszy sposób (w większości przypadków) na uzyskanie pracy.


źródło
1
Wydaje mi się, że ta odpowiedź jest dość niskiej jakości, zwłaszcza biorąc pod uwagę, że istnieją już odpowiedzi StrTotime.
GrumpyCrouton
4
To jest najkrótsza odpowiedź i działa. Trudno powiedzieć, że jest niskiej jakości.
Tim Rogers,
@ user4600953 - jest to najłatwiejszy, który działa. Wiele innych mówi, aby użyć checkdate()- ale okazuje się, że data checkdate kończy się niepowodzeniem, jeśli rok ma DOWOLNĄ wartość: 2, 20, 202, 2020, 20201- wszystkie zwracają wartość true. Idę z twoim rozwiązaniem!
rolinger
7

Zgodnie z odpowiedzią cl-sah, ale brzmi to lepiej, krócej ...

function checkmydate($date) {
  $tempDate = explode('-', $date);
  return checkdate($tempDate[1], $tempDate[2], $tempDate[0]);
}

Test

checkmydate('2015-12-01');//true
checkmydate('2015-14-04');//false
sdotbertoli
źródło
Musisz sprawdzić, czy count($tempDate) === 3chociaż
VDarricau
@VDarricau nie nie, checkdate zbombarduje, jeśli ich nie ma, możesz napisać isset () dla każdego, ale po prostu
zignoruję
7

Mam tę rzecz, że nawet w PHP lubię znaleźć funkcjonalne rozwiązania. Na przykład odpowiedź udzielona przez @migli jest naprawdę dobra, bardzo elastyczna i elegancka.

Ale ma problem: co zrobić, jeśli trzeba zweryfikować wiele ciągów DateTime w tym samym formacie? Będziesz musiał powtórzyć format w dowolnym miejscu, co jest sprzeczne z zasadą SUCHOŚCI . Moglibyśmy ustawić format na stały, ale nadal musielibyśmy przekazać stałą jako argument do każdego wywołania funkcji.

Ale nie bój się więcej! Możemy użyć curry na ratunek! PHP nie sprawia, że ​​to zadanie jest przyjemne, ale nadal można wdrożyć curry z PHP:

<?php
function validateDateTime($format)
{
    return function($dateStr) use ($format) {
        $date = DateTime::createFromFormat($format, $dateStr);
        return $date && $date->format($format) === $dateStr;
    };
}

Więc co właśnie zrobiliśmy? Zasadniczo zawinęliśmy ciało funkcji w anonimowy i zamiast tego zwróciliśmy taką funkcję. Możemy wywołać funkcję sprawdzania poprawności w następujący sposób:

validateDateTime('Y-m-d H:i:s')('2017-02-06 17:07:11'); // true

Tak, niezbyt duża różnica ... ale prawdziwa moc pochodzi z częściowo zastosowanej funkcji , możliwej dzięki curry:

// Get a partially applied function
$validate = validateDateTime('Y-m-d H:i:s');

// Now you can use it everywhere, without repeating the format!
$validate('2017-02-06 17:09:31'); // true
$validate('1999-03-31 07:07:07'); // true
$validate('13-2-4 3:2:45'); // false

Programowanie funkcjonalne FTW!

Victor Schröder
źródło
2
Najlepsza odpowiedź od osuwiska IMHO (rozwiązuje specyficzny problem OP z większą elastycznością i prawie taką samą ilością kodu, jak pozostałe odpowiedzi)
StubbornShowaGuy
IMHO to naprawdę brzydkie programowanie, po prostu umieść wartości w tablicy i przeprowadź je przez pętlę, aby sprawdzić poprawność, ale nie rób tego!
Tim
Jestem ciekawy @Tim, czy możesz podać nam przykład walidacji tablicy / pętli?
Victor Schröder
5

Obawiam się, że większość głosów ( https://stackoverflow.com/a/19271434/3283279 ) nie działa poprawnie. Czwarty przypadek testowy (var_dump (validateDate ('2012-2-25')); // false) jest niepoprawny. Data jest poprawna, ponieważ odpowiada formatowi - m pozwala na miesiąc z zerowym początkiem lub bez (patrz: http://php.net/manual/en/datetime.createfromformat.php ). Dlatego data 2012-2-25 ma format Ymd, a przypadek testowy musi być prawdziwy, a nie fałszywy.

Uważam, że lepszym rozwiązaniem jest przetestowanie możliwego błędu w następujący sposób:

function validateDate($date, $format = 'Y-m-d') {
    DateTime::createFromFormat($format, $date);
    $errors = DateTime::getLastErrors();

    return $errors['warning_count'] === 0 && $errors['error_count'] === 0;
}
Barvajz
źródło
3

Co powiesz na ten?

Po prostu używamy bloku try-catch.

$dateTime = 'an invalid datetime';

try {
    $dateTimeObject = new DateTime($dateTime);
} catch (Exception $exc) {
    echo 'Do something with an invalid DateTime';
}

To podejście nie ogranicza się tylko do jednego formatu daty / godziny i nie trzeba definiować żadnej funkcji.

juliański
źródło
to nie zadziała dla wartości datetime 0000-00-00 00:00:00
Naseeruddin VN
@NaseeruddinVN '0000-00-00 00:00:00'jest prawidłową wartością daty i godziny . To tylko pierwsza wartość. Jednak właściwość date obiektu datetime będzie mieć wartość '-0001-11-30 00:00:00'.
Julian
1

Testowane rozwiązanie Regex:

    function isValidDate($date)
    {
            if (preg_match("/^(((((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])))))$/", $date)) {
                    return $date;
            }
            return null;
    }

Zwróci wartość null, jeśli data jest niepoprawna lub nie ma formatu rrrr-mm-dd, w przeciwnym razie zwróci datę.

Akumaburn
źródło
1

Sprawdź poprawność za pomocą funkcji checkdate :

$date = '2019-02-30';

$date_parts = explode( '-', $date );

if(checkdate( $date_parts[1], $date_parts[2], $date_parts[0] )){
    //date is valid
}else{
    //date is invalid
}
Książę Ahmed
źródło
Dokumentacja do checkdate
Prince Ahmed
0
/*********************************************************************************
Returns TRUE if the input parameter is a valid date string in "YYYY-MM-DD" format (aka "MySQL date format")
The date separator can be only the '-' character.
*********************************************************************************/
function isMysqlDate($yyyymmdd)
{
    return checkdate(substr($yyyymmdd, 5, 2), substr($yyyymmdd, 8), substr($yyyymmdd, 0, 4)) 
        && (substr($yyyymmdd, 4, 1) === '-') 
        && (substr($yyyymmdd, 7, 1) === '-');
}
Marco Demaio
źródło
-1
    /**** date check is a recursive function. it's need 3 argument 
    MONTH,DAY,YEAR. ******/

    $always_valid_date = $this->date_check($month,$day,$year);

    private function date_check($month,$day,$year){

        /** checkdate() is a php function that check a date is valid 
        or not. if valid date it's return true else false.   **/

        $status = checkdate($month,$day,$year);

        if($status == true){

            $always_valid_date = $year . '-' . $month . '-' . $day;

            return $always_valid_date;

        }else{
            $day = ($day - 1);

            /**recursive call**/

            return $this->date_check($month,$day,$year);
        }

    }
Subham Ghorui
źródło
1
Kod bez żadnego wyjaśnienia nie jest zbyt przydatny.
Gert Arnold,
-2

Wypróbuj to:

$date = "2017-10-01";


function date_checker($input,$devider){
  $output = false;

  $input = explode($devider, $input);
  $year = $input[0];
  $month = $input[1];
  $day = $input[2];

  if (is_numeric($year) && is_numeric($month) && is_numeric($day)) {
    if (strlen($year) == 4 && strlen($month) == 2 && strlen($day) == 2) {
      $output = true;
    }
  }
  return $output;
}

if (date_checker($date, '-')) {
  echo "The function is working";
}else {
  echo "The function isNOT working";
}
Youssef Gamra
źródło