0

For a reservation system, I want to see if the start and end date of a user input intersect with the start and end date stored in two arrays. One array holds all the start dates and one all the end dates. The values from the arrays have been fetched from the database before.

$startArray = [0 => "1.1.2021", 1 => "5.1.2021", 2 => "10.1.2021"]; 
$endArray = [0 => "6.1.2021", 1 => "8.1.2021", 2 => "13.1.2021"];
$startUser = "3.1.2021";
$endUser = "5.1.2021";

My goal is the see if the selection the user has made intersects with the selection from the array. I have tried this

foreach ($startArray as $key => $value) {
            if (($startUser <= $endArray[$key]) && ($endUser >= $value)) {
                //action;
            }

That isn't working though. How would I best go about this?

EDIT: Date format is actually 2021-01-01. Both in the array as well as the user input.

  • A little unclear to me. Are "start dates" and "end dates" fixed? Meaning, if I started on 1.1.2021 I must finish on 6.1.2021 or earlier (both are the first limb in the array) – Eden Moshe Jan 14 '21 at 15:28
  • Intersection would imply any sort of overlap. From a logical standpoint, that means user start is before db end and user end is after db start. Is this what you're aiming at? – El_Vanja Jan 14 '21 at 15:31
  • Also, it might make your comparison easier if you organize your intervals into a single array where each element will be an array with the start and end dates. – El_Vanja Jan 14 '21 at 15:41
  • @El_Vanja Not quite. The database simply contains a list of all bookings for the reservation system. The start and end date from the arrays belong together by index. The startUser and enduser are the values the user inputs where they want to add a reservation to the database. All I want to check is if the dates given by the user conflict with another already existing booking from the database. I just used arrays to store all the results in. –  Jan 14 '21 at 15:42
  • @EdenMoshe The values in the array correlate to each other. Index 0 from startArray is the start and Index 0 from endArray is the end of that duration. Same with index 1, 2 etc. Those dates are all fixed and cannot be changed so if it starts at the 1.1.2021 it must end at the 6.1.2021. I want to find out if the duration of startUser and endUser overlaps with any of the timespans from the arrays. –  Jan 14 '21 at 15:46
  • Your timespans overlap themselves (first one ends 6.1., second one starts 5.1.), which makes it all the more confusing. From what you've described in your comment, you're looking for the exact logic from my first comment. If user start is before db end and user end is after db start - they intersect. Though you can't just compare strings like that. How are your dates stored in the database, `date` or `varchar`? – El_Vanja Jan 14 '21 at 15:50
  • This could probably be solved with a db query instead of having to fetch all the data and iterate it. You'd just have to share your db structure. – El_Vanja Jan 14 '21 at 16:14
  • @El_Vanja I have one table called rooms that contains the room number and their price class. Multiple rooms can share one price class. The user selects the price class he wants to book at and then the duration (start and end). The other table called bookings is a list of all bookings ever made. I want to go through it and see if all the rooms at that price class are booked for the duration the user selected. The bookings table just has room number start and end date along with some other information that is not necessary for. this (I think at least). I hope that makes sense. –  Jan 14 '21 at 16:49
  • And all dates are saved as dates on the database –  Jan 14 '21 at 16:49
  • Are you certain that's all you need, just a boolean information if there's an available room or not? You don't need a specific room id? – El_Vanja Jan 14 '21 at 19:26
  • No I'd also would need the room ID for the selected room –  Jan 14 '21 at 19:41

3 Answers3

-1

It's hard comparing strings like that. What I like to do when checking dates, is to use DateTime objects which can be compared far more easily.

$d1 = new DateTime('2008-08-03 14:52:10');
$d2 = new DateTime('2008-01-03 11:11:10');
var_dump($d1 == $d2);
var_dump($d1 > $d2);
var_dump($d1 < $d2);
?>
bool(false)
bool(true)
bool(false)
  • How would I use this to check if the timespan of 3.1.2021 to 5.1.2021 overlaps with another timespan? –  Jan 14 '21 at 15:54
-1

The problem with your code is that you did not check that every date is between the 2 dates. Start date should be greater than start date and smaller than end date. End date should be greater than start date and smaller than end date

<?php
$startArray = [0 => "1.1.2021", 1 => "5.1.2021", 2 => "10.1.2021"];
$endArray = [0 => "6.1.2021", 1 => "8.1.2021", 2 => "13.1.2021"];
$startUser = DateTime::createFromFormat('d.m.Y', "3.1.2021");
$endUser =  DateTime::createFromFormat('d.m.Y', "5.1.2021");

if ($startUser->diff($endUser)->format("%R%d") <= 0){
    return false;
}
foreach ($startArray as $key => $value) {
    $start = DateTime::createFromFormat('d.m.Y', $value);
    $end = DateTime::createFromFormat('d.m.Y', $endArray[$key]);
    if ($startUser->diff($start)->format("%R%d") <= 0 && $startUser->diff($end)->format("%R%d") >=0 && $endUser->diff($start)->format("%R%d") <= 0 && $endUser->diff($end)->format("%R%d") >=0 ) {
        return true;
    }
}
return false;

For the sake of order and convenience of maintenance I would slightly change the structure of the data. I would build such an array

$dates = [['start' => "1.1.2021", 'end'= >"6.1.2021"] , ['start' => "5.1.2021", 'end'= >"8.1.2021"]
Eden Moshe
  • 1,097
  • 1
  • 6
  • 16
  • This date format is unreliable for comparison. Try `var_dump("5.2.2021" <= "6.1.2021")` for example. – El_Vanja Jan 14 '21 at 16:11
  • Fixed. Thank you. – Eden Moshe Jan 14 '21 at 16:35
  • Though, you could simplify that condition considerably. See [general date range overlap logic](https://stackoverflow.com/questions/325933/determine-whether-two-date-ranges-overlap/). `if ($startUser <= $end && $endUser >= $start)` should completely cover it. – El_Vanja Jan 14 '21 at 16:42
  • And not only is it overly complicated, it is also wrong. See [fiddle](https://3v4l.org/P5DQQ) where I modified dates. I set them to overlap with the last interval in the arrays, but it doesn't enter the `if`. – El_Vanja Jan 14 '21 at 16:48
  • `23.1.2021 < 25.1.2021` – Eden Moshe Jan 14 '21 at 16:53
  • ...and? Somehow that doesn't constitute an overlap? Everything from 25.1. to 1.2. overlaps. – El_Vanja Jan 14 '21 at 16:55
  • To the best of my knowledge both the start date and the end date should be between the dates. This is exactly the reason for the relatively complex "if" – Eden Moshe Jan 14 '21 at 16:58
  • That is not the definition of an intersection. – El_Vanja Jan 14 '21 at 17:05
-1

Your issue can be solved at the database level, with a single query. Let's assume the following simplified database setup:

  • rooms table
number price_class
1 Economy
2 Economy
3 Luxury
4 Luxury
  • bookings table
room_number start_date end_date
1 2021-06-11 2021-06-15
2 2021-06-11 2021-06-15
3 2021-06-11 2021-06-15

Now let's also assume that the user searches for the term 2021-06-09 - 2021-06-12. That would mean the only unoccupied room for that period would be room 4. If we search for Economy rooms, there should be no results and if we search for Luxury, only room 4 should be in the result. The following query does it:

SELECT number FROM rooms
WHERE price_class = :priceClass AND number NOT IN (
  SELECT DISTINCT room_number FROM bookings b
  INNER JOIN rooms r ON r.number = b.room_number AND r.price_class = :priceClass
  WHERE start_date <= :userEndDate AND end_date >= :userStartDate
)

You can test it online in this fiddle.

How it works:

  • The subquery selects all the rooms of given price class that have an overlap in the dates. The overlap logic is well explained in this post. We use distinct because one overlap is sufficient (theoretically, the user input can overlap with multiple intervals in the same room, but that doesn't matter - as soon as one overlaps, we know it's not available).
  • Main query then selects all the rooms of given price class whose number was not found in the overlaps query (meaning they're available).

This will return all the rooms that are available. If you only need n rooms, just add LIMIT n to the main query. If you want to return random available rooms, you can add ORDER BY RAND() to the main query.

El_Vanja
  • 3,660
  • 4
  • 18
  • 21