Jak sprawdzić, czy data mieści się w podanym zakresie?

86

Jeśli masz $start_datei $end_date, jak możesz sprawdzić, czy data podana przez użytkownika mieści się w tym zakresie?

na przykład

$start_date = '2009-06-17';

$end_date = '2009-09-05';

$date_from_user = '2009-08-28'; 

Czy w tej chwili daty są łańcuchami, czy warto byłoby je przekonwertować na liczby całkowite ze znacznikiem czasu?

meleyal
źródło
To by pomogło, wtedy miałbyś dostęp do wszelkich dostępnych funkcji manipulacji datami.
ChrisF
3
Po prostu zachowaj się z ograniczeniem datownika, ponieważ znaczniki czasu Unix zaczynają się w epoce, czyli 1 stycznia 1970 (1970-01-01), więc możesz mieć zabawne zachowanie podczas testowania dat sprzed 1970 roku.
Shadi Almosri
1
@Shadi Wiesz, że istnieje coś takiego jak UJEMNE liczby, prawda?
Jeremy Logan
@fiXedd istnieje jednak ten sam podstawowy problem - nawet zezwalając na ujemne znaczniki czasu, 32-bitowe znaczniki czasu nie mogą reprezentować dat sprzed 1902 r.
Frank Farmer

Odpowiedzi:

122

Konwersja ich na znaczniki czasu jest dobrym sposobem, używając strtotime , np

$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));
}
Conroy P.
źródło
Działa świetnie. Dzięki.
GeneCode
47

Użyj klasy DateTime, jeśli masz PHP 5.3+. Łatwiejszy w użyciu, lepsza funkcjonalność.

DateTime wewnętrznie obsługuje strefy czasowe, a inne rozwiązania należy do Ciebie.

<?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);
oldwizard
źródło
Prawdopodobnie przekształciłbyś to w funkcję, która przyjmuje trzy parametry i zwraca wartość logiczną. Czy powinno być bardzo łatwe do przetestowania?
oldwizard
Powiedziałbym, że funkcja powinna zwrócić true dla 2012-02-01is between 2012-02-01 ... 2014-04-30 23:59:59.
Salman A
2
Zawsze wolę DateTimeod pobieżnych funkcji czasu, które PHP oferuje zaraz po wyjęciu z pudełka. Aby uwzględnić datę rozpoczęcia i zakończenia w tym czeku, po prostu zmień wartość zwracaną nareturn $date >= $startDate && $date <= $endDate;
zanderwar
@zanderway mówisz, że domyślnie> jest porównaniem „dniowym”, a nie sekundowym? $ startDate ma określone sekundy, więc zastanawiam się, czy "> =" jest naprawdę konieczne?
PJ Brunet
46

Konwersja do znacznika czasu w celu porównania nie jest konieczna, biorąc pod uwagę, że ciągi są sprawdzane jako daty w formacie kanonicznym „RRRR-MM-DD”.

Ten test zadziała:

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

dany:

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

UWAGA: Porównywanie ciągów w ten sposób pozwala na „niepoprawne” daty, np. (32 grudnia) „2009-13-32” i na dziwnie sformatowane ciągi „2009/3/3”, tak że porównanie ciągów NIE będzie równoważne porównanie daty lub znacznika czasu. Działa to TYLKO wtedy, gdy wartości dat w łańcuchach są w formacie CONSISTENT i CANONICAL .

EDYTUJ, aby dodać tutaj notatkę, omawiając oczywiste.

Przez CONSISTENT rozumiem na przykład, że porównywane ciągi muszą mieć identyczny format: miesiąc musi zawsze składać się z dwóch znaków, dzień musi mieć zawsze dwa znaki, a znak separatora zawsze musi być myślnikiem. Nie możemy wiarygodnie porównywać „łańcuchów”, które nie są czteroznakowe rok, dwa znaki w miesiącu, dwa znaki na dzień. Gdybyśmy mieli na przykład kombinację jednego znaku i dwóch znaków miesięcy w łańcuchach, otrzymalibyśmy nieoczekiwany wynik podczas porównania '2009-9-30'z '2009-10-11'. Po ludzku postrzegamy liczbę „9” jako mniejszą niż „10”, ale porównanie ciągów będzie '2009-9'większe niż '2009-1'. Nie musimy koniecznie mieć znaków rozdzielających myślniki; moglibyśmy równie rzetelnie porównać ciągi znaków w'YYYYMMDD' format; jeśli istnieje znak separatora, musi on zawsze tam być i zawsze być taki sam.

Przez CANONICAL rozumiem format, który będzie skutkował ciągami znaków, które będą sortowane według dat. Oznacza to, że ciąg będzie zawierał najpierw „rok”, potem „miesiąc”, a następnie „dzień”. Nie możemy wiarygodnie porównywać ciągów w 'MM-DD-YYYY'formacie, ponieważ nie jest to kanoniczne. Porównanie ciągów pozwoliłoby porównać MM(miesiąc) przed porównaniem YYYY(rok), ponieważ porównanie ciągów działa od lewej do prawej). Dużą zaletą formatu ciągu „RRRR-MM-DD” jest to, że jest on kanoniczny; daty przedstawione w tym formacie można wiarygodnie porównać jako ciągi.

[UZUPEŁNIENIE]

Jeśli zdecydujesz się na konwersję sygnatury czasowej php, pamiętaj o ograniczeniach.

Na niektórych platformach php nie obsługuje wartości znaczników czasu wcześniejszych niż 1970-01-01 i / lub późniejszych niż 2038-01-19. (Taka jest natura 32-bitowej liczby całkowitej uniksowego znacznika czasu.) Późniejsze wersje pf php (5.3?) Mają to rozwiązać.

Strefa czasowa może również stanowić problem, jeśli nie będziesz ostrożnie używać tej samej strefy czasowej podczas konwersji z ciągu znaków na znacznik czasu iz powrotem ze znacznika czasu na ciąg.

HTH

spencer7593
źródło
1
Ten jest interesujący.
Ionuț G. Stan
1
Chociaż bez przesyłania możesz napotkać problemy, jeśli wartości $ date_from_user, $ start_date lub $ end_date nie są datami ... tj. $ End_date = 'Balh Blah' .. na pewno nie będzie działać poprawnie
Mike Dinescu
@ spencer7593, czy masz jakieś odniesienia do tego zachowania?
Ionuț G. Stan
2
Możesz użyć checkdate (), aby sprawdzić poprawność
meleyal,
1
@Sergio: tak, to samo porównanie ciągów działa dla wartości czasu, o ile ciągi są w standardowym formacie kanonicznym (tj. Zegar 24-godzinny, północ reprezentowana jako „00:00:00”, ostatnia sekunda przed północą reprezentowana jako „ 23:59:59 '. Aby mieć pewność, że porównanie ciągów „zadziała”, należy zagwarantować, że ciągi reprezentujące wartość czasu mają gwarantowany format.
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.
źródło
Nie obejmie to żadnych dokładnych dopasowań do daty rozpoczęcia lub zakończenia
TheTXI
OP nie pytał o mecze włączające.
Jeremy Logan
3

Następnie przekonwertuj obie daty na znaczniki czasu

pseudo kod:

if date_from_user > start_date && date_from_user < end_date
    return true
Dolina górska
źródło
Możesz go edytować tak, aby w zakresie uwzględniać również datę_początkową i datę_końcową. W tej chwili, jeśli data_from_user jest równa albo, nie będzie liczona.
TheTXI
W OP nie określono zakresów obejmujących ani wyłącznych, więc pozostawię im wybór strategii
Glen,
3

W podanym formacie, zakładając, że użytkownik jest wystarczająco inteligentny, aby podać prawidłowe daty, nie musisz najpierw konwertować na datę, możesz je porównać jako ciągi.

richardtallent
źródło
1
W tym miejscu w kodzie daty zostały już zatwierdzone
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
źródło
2

Zamień je na daty lub liczby całkowite znacznika czasu, a następnie sprawdź, czy $ date_from_user to <= $ end_date i> = $ start_date

TheTXI
źródło
3
Czy mógłbyś wyjaśnić „zamień je na daty”? Jak byś to zrobił? Myślałem, że PHP nie ma typu daty?
meleyal
2

Możesz spróbować tego:

//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
źródło
1
uwaga o wersji: iterator_to_array()jest dostępna od PHP 5.1
Raptor
0

Ta metoda jest dla mnie najłatwiejsza:

$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
źródło