Como verificar se uma data está em um determinado intervalo?

86

Se você tem um $start_date e $end_date, como verificar se uma data fornecida pelo usuário está dentro desse intervalo?

por exemplo

$start_date = '2009-06-17';

$end_date = '2009-09-05';

$date_from_user = '2009-08-28'; 

No momento, as datas são strings, ajudaria a convertê-las em inteiros de carimbo de data / hora?

meleyal
fonte
Isso ajudaria, então você teria acesso a todas as funções de manipulação de datas que existem.
ChrisF
3
Tenha cuidado com a limitação do carimbo de data / hora, pois os carimbos de data / hora Unix começam na época, que é 1º de janeiro de 1970 (01/01/1970), para que você possa ter um comportamento engraçado para testar datas anteriores a 1970.
Shadi Almosri
1
@Shadi Você sabe que existem números NEGATIVOS, certo?
Jeremy Logan
@fiXedd o mesmo problema básico existe - mesmo permitindo carimbos de data / hora negativos, carimbos de data / hora de 32 bits não podem representar datas anteriores a 1902.
Frank Farmer

Respostas:

122

Convertê-los em carimbos de data / hora é o caminho certo, usando strtotime , por exemplo

$start_date = '2009-06-17';

$end_date = '2009-09-05';

$date_from_user = '2009-08-28';

check_in_range($start_date, $end_date, $date_from_user);


function check_in_range($start_date, $end_date, $date_from_user)
{
  // Convert to timestamp
  $start_ts = strtotime($start_date);
  $end_ts = strtotime($end_date);
  $user_ts = strtotime($date_from_user);

  // Check that user date is between start & end
  return (($user_ts >= $start_ts) && ($user_ts <= $end_ts));
}
ConroyP
fonte
Funciona bem. Obrigado.
GeneCode de
47

Use a classe DateTime se você tiver PHP 5.3+. Mais fácil de usar, melhor funcionalidade.

DateTime oferece suporte interno a fusos horários, com as outras soluções que você decide.

<?php    
/**
 * @param DateTime $date Date that is to be checked if it falls between $startDate and $endDate
 * @param DateTime $startDate Date should be after this date to return true
 * @param DateTime $endDate Date should be before this date to return true
 * return bool
 */
function isDateBetweenDates(DateTime $date, DateTime $startDate, DateTime $endDate) {
    return $date > $startDate && $date < $endDate;
}

$fromUser = new DateTime("2012-03-01");
$startDate = new DateTime("2012-02-01 00:00:00");
$endDate = new DateTime("2012-04-30 23:59:59");

echo isDateBetweenDates($fromUser, $startDate, $endDate);
velho feiticeiro
fonte
Você provavelmente transformaria isso em uma função que usa três parâmetros e retornaria um booleano. Deve ser muito fácil de testar?
oldwizard
Eu diria que a função deve retornar true para 2012-02-01está entre 2012-02-01 ... 2014-04-30 23:59:59.
Salman A
2
Eu sempre DateTimeprefiro as funções de tempo incompletas que o PHP oferece fora da caixa. Para incluir as datas de início e término nesta verificação, basta alterar o valor de retorno parareturn $date >= $startDate && $date <= $endDate;
zanderwar
@zanderway, você está dizendo que o padrão é uma comparação de "dia" e não uma comparação de segundos? $ startDate tem segundos especificados, então estou me perguntando se "> =" é realmente necessário?
PJ Brunet
46

Não é necessário converter para carimbo de data / hora para fazer a comparação, visto que as strings são validadas como datas no formato canônico 'AAAA-MM-DD'.

Este teste funcionará:

( ( $date_from_user >= $start_date ) && ( $date_from_user <= $end_date ) )

dado:

$start_date     = '2009-06-17';
$end_date       = '2009-09-05';
$date_from_user = '2009-08-28';

NOTA: Comparar strings como este permite datas "não válidas", por exemplo (32 de dezembro) '2009-13-32' e para strings com formatação estranha '2009/3/3', de modo que uma comparação de string NÃO será equivalente a uma comparação de data ou carimbo de data / hora. Isso funciona somente se os valores de data nas cordas estão em um CONSISTENTE e CANÔNICA formato.

EDIT para adicionar uma nota aqui, elaborando o óbvio.

Por CONSISTENTE , quero dizer, por exemplo, que as strings que estão sendo comparadas devem estar em formato idêntico: o mês deve sempre ter dois caracteres, o dia deve sempre ter dois caracteres e o caractere separador deve ser sempre um travessão. Não podemos comparar de forma confiável "strings" que não sejam ano de quatro caracteres, mês de dois caracteres, dia de dois caracteres. Se tivéssemos uma mistura de um caractere e dois meses de caractere nas strings, por exemplo, obteríamos resultados inesperados quando comparamos '2009-9-30'com '2009-10-11'. Humanamente vemos "9" como sendo menor que "10", mas uma comparação de strings verá '2009-9'como maior que '2009-1'. Não precisamos necessariamente ter caracteres separadores de traço; poderíamos comparar strings de forma confiável em'YYYYMMDD' formato; se houver um caractere separador, ele deve estar sempre lá e ser sempre o mesmo.

Por CANONICAL , quero dizer um formato que resultará em strings que serão classificadas em ordem de data. Ou seja, a string terá uma representação de "ano" primeiro, depois "mês" e depois "dia". Não podemos comparar strings no 'MM-DD-YYYY'formato de forma confiável , porque isso não é canônico. Uma comparação de string compararia o MM(mês) antes de comparar YYYY(ano), pois a comparação de string funciona da esquerda para a direita.) Um grande benefício do formato de string 'AAAA-MM-DD' é que ele é canônico; as datas representadas neste formato podem ser comparadas de forma confiável como strings.

[TERMO ADITIVO]

Se você for para a conversão de carimbo de data / hora php, esteja ciente das limitações.

Em algumas plataformas, o php não suporta valores de carimbo de data / hora anteriores a 1970-01-01 e / ou posteriores a 2038-01-19. (Essa é a natureza do inteiro de 32 bits do carimbo de data / hora unix.) As versões posteriores pf php (5.3?) Devem resolver isso.

O fuso horário também pode ser um problema, se você não tiver o cuidado de usar o mesmo fuso horário ao converter de string para timestamp e de timestamp de volta para string.

HTH

spencer7593
fonte
1
Este é interessante.
Ionuț G. Stan
1
Embora sem lançar você poderá ter problemas se os valores de $ date_from_user, ou $ start_date, ou $ end_date não forem datas .. ex: $ end_date = 'Balh Blah' .. definitivamente não funcionará corretamente
Mike Dinescu
@ spencer7593, você tem algumas referências para esse comportamento?
Ionuț G. Stan
2
Você pode usar checkdate () para validar
meleyal
1
@Sergio: sim, a mesma comparação de strings funciona para valores de hora, desde que as strings estejam em um formato canônico padrão (ou seja, relógio de 24 horas, meia-noite representada como '00: 00: 00 ', o último segundo antes da meia-noite representado como' 23:59:59 '. Para ter certeza de que a comparação de strings "funcionará", você precisa garantir que as representações de string do valor de tempo estejam em um formato garantido.
spencer7593
4
$startDatedt = strtotime($start_date)
$endDatedt = strtotime($end_date)
$usrDatedt = strtotime($date_from_user)

if( $usrDatedt >= $startDatedt && $usrDatedt <= $endDatedt)
{
   //..falls within range
}
Stan R.
fonte
Isso não incluirá correspondências exatas com a data de início ou de término
TheTXI
OP não pediu correspondências inclusivas.
Jeremy Logan
3

Converta ambas as datas em carimbos de data / hora e faça

pseudo-código:

if date_from_user > start_date && date_from_user < end_date
    return true
Glen
fonte
Você pode querer editá-lo para incluir start_date e end_date no intervalo. No momento, se date_from_user for igual a qualquer um, não será contado.
TheTXI
O OP não especificou intervalos inclusivos ou exclusivos, então vou deixar que eles escolham a estratégia a ser usada
Glen
3

No formato que você forneceu, supondo que o usuário seja inteligente o suficiente para fornecer datas válidas, você não precisa converter para uma data primeiro, você pode compará-los como strings.

richardtallent
fonte
1
Neste ponto no código as datas já foram validadas
meleyal
3
$start_date="17/02/2012";
$end_date="21/02/2012";
$date_from_user="19/02/2012";

function geraTimestamp($data)
{
    $partes = explode('/', $data); 
    return mktime(0, 0, 0, $partes[1], $partes[0], $partes[2]);
}


$startDatedt = geraTimestamp($start_date); 
$endDatedt = geraTimestamp($end_date);
$usrDatedt = geraTimestamp($date_from_user);

if (($usrDatedt >= $startDatedt) && ($usrDatedt <= $endDatedt))
{ 
    echo "Dentro";
}    
else
{
    echo "Fora";
}
Paulo henrique
fonte
2

Converta-os em datas ou inteiros de carimbo de data / hora e, em seguida, verifique se $ date_from_user é <= $ end_date e> = $ start_date

TheTXI
fonte
3
Você poderia esclarecer 'convertê-los em datas'? Como você faria isso? Achei que o PHP não tem tipo de data?
meleyal
2

Você pode tentar isto:

//custom date for example
$d1 = new DateTime("2012-07-08");
$d2 = new DateTime("2012-07-11");
$d3 = new DateTime("2012-07-08");
$d4 = new DateTime("2012-07-15");

//create a date period object
$interval = new DateInterval('P1D');
$daterange = iterator_to_array(new DatePeriod($d1, $interval, $d2));
$daterange1 = iterator_to_array(new DatePeriod($d3, $interval, $d4));
array_map(function($v) use ($daterange1) { if(in_array($v, $daterange1)) print "Bingo!";}, $daterange);
Ilya
fonte
1
nota de versão: iterator_to_array()está disponível desde PHP 5.1
Raptor
0

Achei este método o mais fácil:

$start_date = '2009-06-17';
$end_date = '2009-09-05';
$date_from_user = '2009-08-28';

$start_date = date_create($start_date);
$date_from_user = date_create($date_from_user);
$end_date = date_create($end_date);

$interval1 = date_diff($start_date, $date_from_user);
$interval2 = date_diff($end_date, $date_from_user);

if($interval1->invert == 0){
  if($interval2->invert == 1){

     // if it lies between start date and end date execute this code

  }
}
Iniravpatel
fonte