Determinar corretamente se a sequência de datas é uma data válida nesse formato

184

Estou recebendo uma sequência de datas de uma API e está formatada como yyyy-mm-dd.

No momento, estou usando um regex para validar o formato da string, que funciona bem, mas posso ver alguns casos em que poderia ser um formato correto de acordo com a string, mas na verdade uma data inválida. ou seja 2013-13-01, por exemplo.

Existe uma maneira melhor no PHP de pegar uma string como 2013-13-01e dizer se é uma data válida ou não para o formato yyyy-mm-dd?

Marty Wallace
fonte
Possível duplicado de validação de data php
Vivek Athalye
2
A resposta aqui é muito melhor de qualquer maneira.
Sebastien

Respostas:

454

Você pode usar DateTimeclasse para essa finalidade:

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

[ Função retirada desta resposta . Também no php.net . Originalmente escrito por Glavić . ]


Casos de teste:

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

Demo!

Amal Murali
fonte
14
Se você estiver usando o PHP 5.2.x, você deve strtotimeobter o timestamp unix e date('Y-m-d', $t)obter a data da string. Então você as compara exatamente como esta resposta.
pedromanoel
2
@pedromanoel: para entrada padrão de data e hora que você pode usar strtotime, mas para formatos fora do padrão que strtotimenão reconhecem, você precisará de outra solução. E para o suporte à versão 5.2 do php parado em janeiro de 2011, o suporte para 5.3 parou em agosto de 2014.
Glavić
2
considere esta datavar_dump( validateDate('2012-2-9'));
reignsly
4
A função funciona corretamente. Ele retornou falso porque o formato thr especificado estava incorreto. Se você deseja usar dia e mês sem zeros à esquerda, o formato deve ser 'Y-n-j'@reignsly.
Amal Murali
1
@ AntonyD'Andrea: não, não vai funcionar sem essa parte. Porque $dnão será falso se você der uma data que tenha transbordado partes, como no 13º mês (13 2013/2013). Mas isso realmente depende do que você quer. Se você precisa, por exemplo, validateDate('2015-55-66')ser válido, sim, você só precisa verificar se $dé objeto ou não.
Glavić
81

Determinar se alguma sequência é uma data

function checkIsAValidDate($myDateString){
    return (bool)strtotime($myDateString);
}
arsh
fonte
2
Isso valida todo um intervalo de formatos de data válidos, não apenas yyyy-mm-dd.
EWit
10
@MichelAyres, isso ocorre porque o php vê 2015-02-30como uma data válida, porque quando o dia indicado é maior que o número de dias no mês especificado (ou negativo), o php passa para o próximo mês. Como a data é garantida no formato, yyyy-mm-ddisso pode ser corrigido alterando o retorno para return (bool)strtotime($myDateString) && date("Y-m-d", strtotime($myDateString)) == $myDateString;.
elitechief21
2
Por que parece (bool)strtotime('s')verdade?
Peon
isso também retorna 1 checkIsAValidDate ("F");
Vaibhav Bhanushali
podemos usar $myDateString = str_replace("/", '-', $myDateString);antes do retorno, se a seqüência de data contém barras (/) como: - dd / mm / aaaa
Yashrajsinh Jadeja
37

Use de maneira simples com a função pré-construída php:

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

Teste

   checkmydate('2015-12-01'); //true
   checkmydate('2015-14-04'); //false
vinha
fonte
1
Boa solução simples, trabalhou primeira vez, graças :)
David Bell
Novamente, ao usar um teste, dentro de um ifpara retornar simplesmente trueou false, retorne o teste em si.
Victor Schröder
4
Isso pressupõe que haja pelo menos três elementos na matriz $ tempDate.
precisa saber é
2
@ person27:return sizeof($tmpDate) == 3 && checkdate($tmpDate[1]...
neurino 15/06
@ vinineet - isso falha. Se o ano é 2, 20, 202, 2020ou mesmo se o ano é 20201- ele retorna true de cada vez.
rolinger
16

Determinar se a string é uma data, mesmo que a string seja um formato não padrão

(strtotime não aceita nenhum formato personalizado)

<?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
fonte
Ao usar um teste, dentro de um ifpara retornar simplesmente trueou false, retorne o teste em si.
Victor Schröder
Mas 2018-3-24 está retornando false, o método recebe 2018-3-24, quando o formato é aplicado return 2018-03-24; como posso retornar verdadeiro de duas maneiras?
Aquiles Perez
12

Essa opção não é apenas simples, mas também aceita quase todos os formatos, embora com formatos fora do padrão possa ser buggy.

$timestamp = strtotime($date);
return $timestamp ? $date : null;
Galki
fonte
Esta deveria ter sido a resposta aceita! Muito, muito mais simples.
Webmaster G
2
É importante observar que isso não funcionará com o formato britânico (d / m / A), pois pressupõe que está convertendo o americano (m / d / a). Parece funcionar apenas se o dia for inferior a 12!
Sylvester Saracevas
@ galki - eu testei isso e ele falha em certos valores. Se $ date = '202-03-31', ele retornará verdadeiro. Mas isso não é uma data válida. Descobri que se você alterar o ano para um ano inválido, ele sempre retornará verdadeiro.
rolinger
@rolinger estranho ... talvez esteja vendo 202AD? Qual carimbo de data / hora do ano 'strtotime' fornece?
galki
@ galki - não tenho certeza, mas 202retorna um número negativo - que ainda passa no teste.
rolinger
10

Você também pode Analisar a data da data do mês e do ano e, em seguida, pode usar a função PHP checkdate()que você pode ler aqui: http://php.net/manual/en/function.checkdate.php

Você também pode tentar este:

$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
fonte
no caso de fevereiro (como comentado por elitechief21), a função 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 12/08/16
4
Essa solução é muito ruim, pois não verifica a validade da data em nenhum sentido. Qualquer mês pode ter 31 dias, incluindo fevereiro. Qualquer ano pode ter 29 de fevereiro. Validar datas usando o RegExp exigiria algo muito mais complexo, com referências anteriores e perspectivas negativas.
Victor Schröder
9

A maneira mais fácil de verificar se a data especificada é válida provavelmente a converte em unixtime usando strtotime, formatando para o formato da data especificada e comparando-a:

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

É claro que você pode usar expressões regulares para verificar a validade, mas será limitado a um determinado formato, sempre que precisar editá-lo para satisfazer outros formatos, além de ser mais do que necessário. As funções integradas são a melhor maneira (na maioria dos casos) de realizar trabalhos.


fonte
1
Eu sinto que essa resposta é de qualidade muito baixa, especialmente considerando que existem respostas em tempo integral.
GrumpyCrouton
4
Esta é a resposta mais curta e funciona. É um pouco duro dizer que é de baixa qualidade.
Tim Rogers
@ user4600953 - este é o mais fácil que funciona. Muitos outros estão dizendo para usar checkdate()- mas estou descobrindo que a data da verificação falha se o ano tiver QUALQUER valor: 2, 20, 202, 2020, 20201- todos retornam verdadeiros. Eu estou indo com sua solução!
rolinger
7

De acordo com a resposta de cl-sah, mas isso soa melhor, mais curto ...

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

Teste

checkmydate('2015-12-01');//true
checkmydate('2015-14-04');//false
sdotbertoli
fonte
Você precisará testar se count($tempDate) === 3embora
VDarricau
@VDarricau não, não, a data de verificação bombardeará se estiverem faltando, você poderia escrever isset () para cada uma delas, mas eu simplesmente suprimiria os avisos
Sr. Heelis
7

Eu tenho uma coisa que, mesmo com PHP, gosto de encontrar soluções funcionais . Então, por exemplo, a resposta dada por @migli é realmente boa, altamente flexível e elegante.

Mas há um problema: e se você precisar validar várias seqüências de caracteres DateTime com o mesmo formato? Você teria que repetir o formato em todo o lugar, o que vai contra o princípio DRY . Poderíamos colocar o formato em uma constante, mas ainda assim, teríamos que passar a constante como argumento para cada chamada de função.

Mas não tema mais! Podemos usar curry em nosso socorro! O PHP não torna essa tarefa agradável, mas ainda é possível implementar o currying com o PHP:

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

Então, o que acabamos de fazer? Basicamente, envolvemos o corpo da função em um anônimo e retornamos essa função. Podemos chamar a função de validação assim:

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

Sim, não é uma grande diferença ... mas o verdadeiro poder vem do função parcialmente aplicada , possibilitada pelo currying:

// 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

Programação funcional FTW!

Victor Schröder
fonte
2
A melhor resposta por um deslizamento de terra IMHO (resolve o problema específico do OP com maior flexibilidade e praticamente a mesma quantidade de código como o resto das respostas)
StubbornShowaGuy
IMHO esta é uma programação realmente feia, basta colocar seus valores em uma matriz e fazer um loop através deles para validar, mas por favor não faça isso!
Tim
Estou curioso @ Tim, você poderia nos dar um exemplo de sua validação de matriz / loop?
Victor Schröder
5

Receio que a solução mais votada ( https://stackoverflow.com/a/19271434/3283279 ) não esteja funcionando corretamente. O quarto caso de teste (var_dump (validateDate ('2012-2-25')); // false) está errado. A data está correta, porque corresponde ao formato - o m permite um mês com ou sem zero à esquerda (consulte: http://php.net/manual/en/datetime.createfromformat.php ). Portanto, uma data 2012-2-25 está no formato Ymd e o caso de teste deve ser verdadeiro, não falso.

Acredito que a melhor solução é testar o possível erro da seguinte maneira:

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

    return $errors['warning_count'] === 0 && $errors['error_count'] === 0;
}
Barvajz
fonte
3

Que tal este?

Simplesmente usamos um bloco try-catch.

$dateTime = 'an invalid datetime';

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

Essa abordagem não se limita a apenas um formato de data / hora e você não precisa definir nenhuma função.

Julian
fonte
isso não funcionará para o valor de data e hora 0000-00-00 00:00:00
Naseeruddin VN
@NaseeruddinVN '0000-00-00 00:00:00'é um valor válido de data e hora. É apenas o primeiro o valor. No entanto, a propriedade date do objeto datetime será '-0001-11-30 00:00:00'.
Julian
1

Solução Regex testada:

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

Isso retornará nulo se a data for inválida ou não estiver no formato aaaa-mm-dd, caso contrário, retornará a data.

Akumaburn
fonte
1

Valide com a função 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
}
Prince Ahmed
fonte
Documentação para checkdate: w3schools.com/php/func_date_checkdate.asp
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
fonte
-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
fonte
1
Código sem qualquer explicação não é muito útil.
Gert Arnold
-2

Faça uma tentativa:

$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
fonte