TransWikia.com

Como criar uma função para validar horário em PHP

Stack Overflow em Português Asked by gustavox on January 31, 2021

Estou tentando validar um campo de horário em PHP, e com a ajuda deste tópico num site externo cheguei a este script:

function validaHoras($campo){
    if (preg_match('/^[0-9]{2}:[0-9]{2}$/', $campo)) {
        $horas = substr($campo, 0, 2);
        $minutos = substr($campo, 3, 2);
        if (($horas > "23") OR ($minutos > "59")) {
            $campo = false;
        }
    }
}

O input deve receber o campo no formato ’00:00′, mas não tem validação em JS, e o usuário pode entrar com um horário inválido.

Então o que quero é mudar o valor da variável para false quando vier com um horário errado (tipo 99:99), mas não está funcionando. Testando o script abaixo com o input 99:99, o valor da variável não é alterado…

function validaHoras($campo){
    if (preg_match('/^[0-9]{2}:[0-9]{2}$/', $campo)) {
        $horas = substr($campo, 0, 2);
        $minutos = substr($campo, 3, 2);
        if (($horas > "23") OR ($minutos > "59")) {
            $campo = false;
        }
    }
}

// SEGUNDA

$val1 = isset($_POST["Tsegs"]) && !empty($_POST["Tsegs"]) ? $_POST["Tsegs"] : false;

validaHoras($val1);
echo $val1;

//saida: 99:99
//saida desejada: false

5 Answers

Segue um "protótipo" bem sucinto, usando o mais básico do básico, que é validação com if.

(sem precisar de RegEx, conversões ou mesmo de operações modificando string)

function pareceHora($str)
{
   return strlen($str) == 5
          && $str[2] == ':'
          && $str[0] >= '0' && $str[0] <= '2'
          && $str[1] >= '0' && $str[1] <= ($str[0]=='2'?'3':'9')
          && $str[3] >= '0' && $str[3] <= '5'
          && $str[4] >= '0' && $str[4] <= '9';
}

Veja funcionando: IDEONE

  • A sintaxe $string[n] acessa o byte de numero n da string diretamente

  • na 3ª linha nos certificamos do tamanho ser 5 caracteres

  • na 4ª de ter : no meio

  • na 5ª e 6ª linhas verificamos a hora entre 00 e 23, usando comparação ASCII. É feito um dígito por vez para que uma string '1A' não seja considerada válida (se testássemos '00' a '23' daria estes efeitos colaterais)

  • na 7ª e 8ª linhas é a mesma coisa, só que para os minutos.

Desta forma, validamos os 5 caracteres da string completamente (faixa e forma) com operações básicas e eficientes.

Answered by Bacco on January 31, 2021

Se você estiver considerando horas apenas de 00:00 e 23:59, você pode utilizar simplesmente o método DateTime::createFromFormat ou a função, que é o apelido para esta primeira, date_create_from_format

Assim:

date_create_from_format('!H:i', $valor);

Se retornar DateTime, é um valor válido. Se retornar false, é inválido.

Porém temos um problema aqui. Essa função aceita valores como 24:99 como hora válida, convertendo seus valores para horas corretas quando passados. No caso de 24:99, seria convertido para 1970-01-02 01:39:00.

Para contornar isso, poderíamos usar a função que cria a data a partir do formato e depois, usando o mesmo formato para a saída, comparar com o valor de entrada.

Veja:

function validar_hora($hora) 
{
   $formato = 'H:i';

   $data = date_create_from_format('!'. $formato, $hora);

   return $data && $data->format($formato) === $hora;
}


validar_hora('23:55'); // true
validar_hora('24:00'); // false

Isso ocorre porque:

Ao aplicar 'H:i' em '24:00' é convertido para '1970-01-02 00:00:00', que é convertido para 00:00 ao chamar 'H:i' para formatá-lo.

No caso de '23:55', é convertido para '1970-01-01 23:55:00', que é convertido para '23:55' ao chamar format.

Veja funcionando no IDEONE.

Essa é uma solução alternativa se não quiser utilizar REGEX.

Nota:: O !, conforme a documentação, redefine todos os campos para a "época" Unix (1970-01-01)

Atualização

Ainda pensando numa solução que gaste menos linhas do que a primeira que apresentei, apliquei os "mesmos fundamentos" às funções date e strtotime. A função strtotime converte uma string para timestamp. Já a função date retorna a data formatada a partir do timestamp, quando passado o segundo parâmetro.

Podemos fazer dessa forma:

function validar_hora($hora)
{
    return date('H:i', strtotime($hora)) == $hora;
}

https://ideone.com/4dWLzv

Answered by Wallace Maxters on January 31, 2021

Uma opção com sanitização.

A rotina sanitiza a entrada do usuário, removendo tudo que não for número, porém, preserva o caracter :.

Converte também caracteres numéricos full-width (zenkaku) e aceita hora e minuto com 1 dígito. Exemplo: 1:10, 1:6 (01:10, 01:06)

A sanitização torna a validação mais amigável ao usuário.

function numbers_only($str, $exception = '')
{
    return preg_replace('#[^0-9'.$exception.']#', '', mb_convert_kana($str, 'n'));
}

function valid_hour($n)
{
    $n = str_replace(':', ':', $n);
    $n = numbers_only($n, ':');

    $arr = explode(':', $n);
    if (count($arr) == 2) {
        $h = intval($arr[0]);
        $m = intval($arr[1]);
        if ($h <= 23 && $h >= 0 && $m <= 59 && $m >= 0) {
            return str_pad($h, 2, '0', STR_PAD_LEFT).':'.str_pad($m, 2, '0', STR_PAD_LEFT);
        }
    }
    return false;
}

$str[0] = '0000'; // (inválido pois é necessário o separador : )
$str[1] = '00:09'; // caracteres zenkaku (válido)
$str[2] = '00:00'; // (válido)
$str[3] = '0:06'; // (válido)
$str[4] = '0:0'; // (válido)
$str[5] = '00:0'; // (válido)
$str[6] = '01:60'; // (inválido pois os minutos ultrapassaram o limite)
$str[7] = '01:6.'; // (válido, pois o ponto no final é removido na sanitização)

/*
Quando for inválido, retorna booleano false.
Quando for válido, retorna a string do usuário sanitizada e formatada como hh:mm, mesmo que o usuário insira apenas 1 dígito para hora ou minuto.
*/

var_dump(valid_hour($str[7]));

Answered by Daniel Omine on January 31, 2021

É possível validar o formato e o contéudo somente com expressões regulares caso deseje. ^(0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]).

^(0[0-9]|1[0-9]|2[0-3]) => O primeiro grupo captura a hora no formato (00-23) ele define três tipos 'categorias' a primeira indentifica valores de 00 a 09, o segundo de 10 a 19 e o último de 20 até 23.

([0-5][0-9]) => O segundo grupo cobre os minutos que tem um lógica um pouco diferente das horas. Captura um dígito que pode estar entre zero e cinco e outro que pode estar entre zero e nove, o que cobre 00 a 59

function validaHoras($campo){
    return preg_match('/^(0[0-9]|1[0-9]|2[0-3]):([0-5][0-9])/', $campo) ? $campo : false;
}


$arr = array('00:00', '19:59', '20:34', '24:33', '28:64', '07:59', '000:30', '30:50');

foreach($arr as $item) var_dump(validaHoras($item)) .'<br>';

Exemplo - ideone

Answered by rray on January 31, 2021

Há um equivoco na chamada da função validaHoras, ela está passando o parametro mas não retorna retorna nada após a execução. A variavel que você imprime é a mesma que você passsa como parametro (por valor), ela não vai sofrer alteração. Tente assim:

<?php

function validaHoras($campo){
    if (preg_match('/^[0-9]{2}:[0-9]{2}$/', $campo)) {
        $horas = substr($campo, 0, 2);
        $minutos = substr($campo, 3, 2);
        if (($horas > "23") OR ($minutos > "59")) {
            return false;
        }

        return true;
    }

    return false;
}

// SEGUNDA
$val1 = isset($_POST["Tsegs"]) && !empty($_POST["Tsegs"]) ? $_POST["Tsegs"] : false;

if(validaHoras($val1) === true){
    echo 'hora valida ' . $val1;
}else {
    echo 'hora invalida ' . $val1;
}

Answered by Juven_v on January 31, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP