Algorithm for available times in agenda - php

I'm really stuck into this situation.
I have these 2 tables:
employee_working_schedule (stores the start and end time an employee works in a specific date)
employee_appointments
Let's suppose we have those rows stored
employee_working_schedule:
start | end
10:00 | 18:00
employee_appointments:
start | end
10:10 | 11:00
11:20 | 12:00
14:30 | 15:20
in this case I want to show that available times are:
10:00 | 10:10
11:00 | 11:20
12:00 | 14:30
15:20 | 18:00
Is there a way of doing this by SQL? I have tried to achieve with php, but no success so far.
Any help will be appreciated.

Here is a way to do it in pure PHP:
class TimeSpan {
function __construct($start, $end) {
$this->start = $start;
$this->end = $end;
}
function starttime() {
list($hour, $minute) = explode(":", $this->start);
return (int)$hour * 60 + (int)$minute;
}
function endtime() {
list($hour, $minute) = explode(":", $this->end);
return (int)$hour * 60 + (int)$minute;
}
}
function convert_to_time($minutes) {
$hour = (int) ($minutes / 60);
$minutes = $minutes % 60;
return str_pad($hour, 2, '0', STR_PAD_LEFT) . ':' . str_pad($minutes, 2, '0', STR_PAD_LEFT);
}
function open_times($shift, $appointments) {
$alltimes = array_fill_keys(range($shift->starttime(), $shift->endtime()), 1);
foreach ($appointments as $appt) {
$alltimes = array_diff_key($alltimes, array_fill_keys(range($appt->starttime() + 1, $appt->endtime() - 1), 1));
}
$groups = array();
$active_group = 0;
$output = array();
$output_counter = 0;
$nums = array_keys($alltimes);
foreach( $nums as $k => $num ) {
if( $k !== 0 && $nums[$k] !== $nums[$k-1]+1 ) $active_group ++;
$groups[ $active_group ][] = $num;
}
foreach( $groups as $group ) {
$first = array_shift( array_values($group) );
$output[$output_counter][] = $first;
$last = array_pop( array_values($group) );
if( $first !== $last )
$output[$output_counter][] = $last;
$output_counter++;
}
foreach ($output as &$span) {
$span[0] = convert_to_time($span[0]);
$span[1] = convert_to_time($span[1]);
}
return $output;
}
$shift = new TimeSpan("10:00", "18:00");
$appointments = array(
new TimeSpan("10:10", "11:00"),
new TimeSpan("11:20", "12:00"),
new TimeSpan("14:30", "15:20"),
);
print_r(open_times($shift, $appointments));
OUTPUT
Array
(
[0] => Array
(
[0] => 10:00
[1] => 10:10
)
[1] => Array
(
[0] => 11:00
[1] => 11:20
)
[2] => Array
(
[0] => 12:00
[1] => 14:30
)
[3] => Array
(
[0] => 15:20
[1] => 18:00
)
)

I can give you a solution in PHP but it would be, of course, faster if you could find something in SQL
//given you selected $employee_working_schedule
$dayStart = DateTime::createFromFormat('h:i',$employee_working_schedule['start']);
$dayEnd = DateTime::createFromFormat('h:i',$employee_working_schedule['end']);
//then assuming your query statement is named $timeQuery and use PDO
$nextFreeStart = $dayStart;
$newFreeEnd = null;
$freeTime = array();//here we will store the free time intervals
while($times = $timeQuery->fetch(PDO::FETCH_ASSOC)){
$nextFreeEnd = DateTime::createFromFormat('h:i',$times['start']);
$freeTime[] = $nextFreeEnd->diff($nextFreeStart);
$nextFreeStart = DateTime::createFromFormat('h:i',$times['end']);
}
$freeTime[] = $dayEnd->diff($nextFreeStart); //close the day

Strategy: Collect all startpoints, find its respective endpoint for each, then throw away rows that don't comply.
This the idea written in MySQL (untested):
SELECT eas.s AS s_start, MIN(eas.e) AS e_end
FROM ((
SELECT end AS s, start AS e
FROM employee_appointments
) UNION (
SELECT start AS s,end AS e
FROM employee_working_schedule
)) eas
WHERE end > start -- or >= if you want zero-time slots as well
GROUP BY eas.s
HAVING NOT EXISTS ( -- if appointments are disjoint, this is likely redundant
SELECT 0
FROM employee_appointments kill
WHERE s_start > kill.end AND kill.start > e_end
)

Related

How to I get a range of times from a start time and end times with an hour difference using PHP?

$course_hour = 1; $starttime = '07:00'; $endtime = '16:00';
I wish if it could return an array of values like below:
If
$course_hour = 1;
$return_array = array('07:00 - 08:00', '08:00 - 09:00', '09:00 - 10:00', '10:00 - 11:00', '11:00 - 12:00', '12:00 - 13:00', '13:00 - 14:00', '14:00 - 15:00', '15:00 - 16:00');
If
$course_hour = 1.5;
$return_array = array('07:00 - 08:30', '8:30 - 10:00', '10:00 - 11:30', '11:30 - 13:00', '13:00 - 14:30', '14:30 - 16:00');
If
$course_hour = 2;
$return_array = array('07:00 - 09:00', '09:00 - 11:00', '11:00 - 13:00', '13:00 - 15:00');
function get_session_times($course_hour, $start_time, $end_time){
if(strlen($start_time) < 5) $start_time = '0'.$start_time;
if(strlen($end_time) < 5) $end_time = '0'.$end_time;
$current_time = date("Y-m-d H:i");
$session_starttime = date("Y-m-d H:i", strtotime(date("Y-m-d ").$start_time));
$session_endtime = date("Y-m-d H:i", strtotime(date("Y-m-d ").$end_time));
$session_starttime_ms = strtotime($session_starttime);
$session_endtime_ms = strtotime($session_endtime);
$session_times_array = array();
for ($i = $session_starttime_ms; $i < $session_endtime_ms; ) {
$session_times_array[] = date("H:i", $i)."<br/>";
$i = $i + ($course_hour * 60 * 60);
}
return $session_times_array;
}
/**
* Get session times from start to end.
*
* #link https://stackoverflow.com/a/27497314/128761 Convert decimal to hour and minute.
* #link https://stackoverflow.com/a/62064286/128761 Original source code for get session times.
* #param int|float|double|decimal $course_hour Course hour.
* #param string $start_time Start time. Format is Hour:minute HH:MM.
* #param string $end_time End time.
* #return array Return times in array.
*/
function getSessionTimes($course_hour, $start_time, $end_time): array
{
if (!is_numeric($course_hour)) {
return [];
}
if (empty($start_time) || empty($end_time)) {
return [];
}
$courseHour = (int) $course_hour;
$courseMinute = (fmod($course_hour, 1) * 60);
$beginDate = new DateTime(date('Y-m-d') . ' ' . $start_time);
$endDate = new DateTime(date('Y-m-d') . ' ' . $end_time);
$result = [];
while ($beginDate < $endDate) {
$output = $beginDate->format('H:i') . ' - ';
$beginDate->modify('+' . $courseHour . 'hour ' . $courseMinute . 'minute');
if ($beginDate > $endDate) {
break;
}
$output .= $beginDate->format('H:i');
$result[] = $output;
unset($output);
}
unset($beginDate, $endDate);
return $result;
}
You have to convert from decimal (for example 1, 1.5) to be hour and minute based on code from this answer.
And then I use code from this answer mentioned by mickmackusa to get times array.
Here is testing.
/**
* This function is for test the result only. It is no need in your real project.
*/
function testResult($result, $expect)
{
if (!is_array($result) || !is_array($expect)) {
throw new Exception('The result and expect must be array.');
}
$totalResult = count($result);
$totalExpect = count($expect);
$totalTested = ($totalResult === $totalExpect);
unset($totalResult, $totalExpect);
foreach ($result as $index => $eachResult) {
if (isset($expect[$index]) && $expect[$index] === $eachResult) {
// matched.
} else {
throw new Exception('The result and expect at index ' . $index . ' does not matched. (' . $eachResult . ')');
}
}
return $totalTested;
}
// run tests
$result = getSessionTimes(1, '07:00', '16:00');
$expect = ['07:00 - 08:00', '08:00 - 09:00', '09:00 - 10:00', '10:00 - 11:00', '11:00 - 12:00', '12:00 - 13:00', '13:00 - 14:00', '14:00 - 15:00', '15:00 - 16:00'];
print_r($result);
var_dump(testResult($result, $expect));
$result = getSessionTimes(1.5, '07:00', '16:00');
$expect = ['07:00 - 08:30', '08:30 - 10:00', '10:00 - 11:30', '11:30 - 13:00', '13:00 - 14:30', '14:30 - 16:00'];
print_r($result);
var_dump(testResult($result, $expect));
$result = getSessionTimes(2, '07:00', '16:00');
$expect = ['07:00 - 09:00', '09:00 - 11:00', '11:00 - 13:00', '13:00 - 15:00'];
print_r($result);
var_dump(testResult($result, $expect));
The result will be...
Array ( [0] => 07:00 - 08:00 [1] => 08:00 - 09:00 [2] => 09:00 - 10:00 [3] => 10:00 - 11:00 [4] => 11:00 - 12:00 [5] => 12:00 - 13:00 [6] => 13:00 - 14:00 [7] => 14:00 - 15:00 [8] => 15:00 - 16:00 )
boolean true
Array ( [0] => 07:00 - 08:30 [1] => 08:30 - 10:00 [2] => 10:00 - 11:30 [3] => 11:30 - 13:00 [4] => 13:00 - 14:30 [5] => 14:30 - 16:00 )
boolean true
Array ( [0] => 07:00 - 09:00 [1] => 09:00 - 11:00 [2] => 11:00 - 13:00 [3] => 13:00 - 15:00 )
boolean true
<?php
$course_hour = 1.5; // 1 || 1.5 || 2
$starttime = '07:00';
$endtime = '16:00';
print_r(get_time_range($course_hour, $starttime, $endtime));
function get_time_range($course_hour, $starttime, $endtime){
$to_add = '';
switch($course_hour):
case 1:
$to_add = " +1 hours";
break;
case 1.5:
$to_add = " +1 hours 30 minutes";
break;
case 2:
$to_add = " +2 hours";
break;
default:
$to_add = " +1 hours";
break;
endswitch;
// get the total hours
$from_time = strtotime($starttime);
$to_time = strtotime($endtime);
$diff_mins = round(abs($from_time - $to_time)/60);
$diff_hours = $diff_mins/60;
$hours_arr =[];
for($i=1; $i<=$diff_hours; $i+= $course_hour):
$new_end_strtime = strtotime($starttime . $to_add);
$endtime = date('H:i', $new_end_strtime);
if($new_end_strtime <= $to_time): // checking the last end time is not greater than defined $endtime
$str = $starttime . ' - '. $endtime;
$tmp = strtotime($starttime . $to_add);
$starttime = date('H:i', $tmp);
array_push($hours_arr, $str);
endif;
endfor;
return $hours_arr;
}
?>

Add Days from Input Type Date and Input Type Duration PHP Excluding Weekends + MYSQL National Holiday

I need to add days (input type number + input type date) but the result must be an array so I can INSERT one after another into the Database.
Here's the code (After HTML Form submitted):
<?php
$start_date = '2017-12-22';
$duration = '3';
$d = new DateTime($start_date);
$t = $d->getTimestamp();
// loop for X days
for($i=0; $i <= $duration; $i++){
// add 1 day to timestamp-
$addDay = 86400;
// get what day it is next day
$nextDay = date('w', ($t + $addDay));
// if it's Saturday or Sunday get $i-1
if($nextDay === 6 || $nextDay === 7) {
$i --;
}
// modify timestamp, add 1 day
$t = $t + $addDay;
$d->setTimestamp($t);
$day_off = $d->format( 'Y-m-d' ). "<br />";
echo $day_off;
$query = "INSERT SQL";
}
?>
From echo $day_off result I get:
2017-12-23
2017-12-24
2017-12-25
2017-12-26
Instead of 23, 24, 25, 26. I need to get the result below:
2017-12-22
2017-12-25
2017-12-26
2017-12-27
22 is the input date, start from 25 because 23 and 24 are Sat and Sun and weekends need to be excluded.
How can I achieve this result? I've been searching on the net but unfortunately, I couldn't find what I needed.
#C. Geek answer made it to works, but I have a more complex question here, since my account are not eligible to ask more question so I'll ask here.
So here's what I've tried so far (with #C. Geek answer) :
<?php
// loop for X days
for($i=0; $i < $duration; $i++){
$d = strtotime("$start_date +$i weekdays");
$t = strftime("%Y-%m-%d",$d);
$day_off[] = $t;
foreach($day_off as $dayoff) {
$data_holiday = mysqli_fetch_array(mysqli_query($con, "SELECT * FROM `holiday_master_data` WHERE `date` = '$dayoff' "));
}
$holiday[] = $data_holiday['date'];
$date = array_diff($day_off, $holiday);
$dayoff_ = $holiday;
?>
Start date : 2017-12-29
Duration : 5 days
From print_r($day_off); I'm getting this result :
Array ( [0] => 2017-12-29 ) Array ( [0] => 2017-12-29 [1] => 2018-01-01 ) Array ( [0] => 2017-12-29 [1] => 2018-01-01 [2] => 2018-01-02 )
And from print_r($holiday); I'm getting this result :
Array ( [0] => ) Array ( [0] => [1] => 2018-01-01 ) Array ( [0] => [1] => 2018-01-01 [2] => ) Array ( [0] => [1] => 2018-01-01 [2] => [3] => ) Array ( [0] => [1] => 2018-01-01 [2] => [3] => [4] => )
The national date fetched from database is 2018-01-01 with 5 looping result, the final date result I need to make are 29 Dec, 02 Jan 03 Jan and 04 Jan, 05 Jan.
Any help will be much appreciated.
Thanks.
https://stackoverflow.com/a/4261223/6288442
If you are limiting to weekdays use the string weekdays.
echo date ( 'Y-m-j' , strtotime ( '3 weekdays' ) );
This should jump you ahead by 3 weekdays, so if it is Thursday it will
add the additional weekend time.
Source: http://www.php.net/manual/en/datetime.formats.relative.php
As for formatting:
http://php.net/manual/en/function.strftime.php
string strftime ( string $format [, int $timestamp = time() ] )
If you need more help with writing the code than these, please do tell in a comment
Here is my full answer:
$start_date = '2017-12-22';
$duration = 3;
$arr=null;
for($i=0; $i <= $duration; $i++){
$d = strtotime("$start_date +$i weekdays");
$t = strftime("%Y-%m-%d",$d);
$arr[]=$t;
}
Get the holidays before the looping, then in the loop, check if date is in_array before adding it to $arr.
e.g.
$start_date = '2017-12-22';
$data_holiday = mysqli_fetch_array(mysqli_query($con, "SELECT * FROM `holiday_master_data` WHERE YEAR(`date`) BETWEEN YEAR('$start_date') AND YEAR('$start_date')+1 "));
$holidays =
$duration = 3;
$arr=null;
for($i=0; $i <= $duration; $i++){
$d = strtotime("$start_date +$i weekdays");
$t = strftime("%Y-%m-%d",$d);
if(!in_array($t,$data_holiday))
$arr[]=$t;
}
FINALLY!! After several hours I fixed everything. Here's the code how I manage to skip (Sun and Monday) and also Skip the Holiday's fetched from the database (based on #C.Geek answers + several tweaking):
<?php
include 'conn.php';
$start_date = mysqli_real_escape_string($con, $_POST['start_date']);
$duration = mysqli_real_escape_string($con, $_POST['duration']);
// loop for X days
for($i=0; $i <= $duration; $i++){
$d = strtotime("$start_date +$i weekdays");
$t = explode(", ", strftime("%Y-%m-%d", $d));
foreach ($t as $date) {
$to_encode = array("date" => $date);
$date_where = $to_encode['date'];
$data_holiday = mysqli_fetch_array(mysqli_query($con, "SELECT `date` AS '0' FROM `holiday_master_data` WHERE DATE(`date`) BETWEEN DATE('$date_where') AND DATE('$date_where') + 1 GROUP BY `id` "));
$encode_holiday = array("date" => $data_holiday[0]);
break;
}
$holiday = array_unique($encode_holiday);
$dayoff = array_diff($t, $holiday);
foreach($dayoff as $date) {
$query = mysqli_query($con, "INSERT INTO ");
if ($query) {
echo "<script>alert('Absence Saved'); window.location ='document.php' </script>";
} else {
echo "<script>alert('Gagal'); window.location ='document.php' </script>";
}
}
}
?>
Hope this helps anyone seeking the same problem I had.
Cheers.

PHP How To Calculate Yearly Increase by Given Start and End Year

By given 2 range of year, current population and target population, how to get total population every year by using PHP?
Example:
Year | Population
2014 | 100000
2018 | 132000
On paper calculation like this:
132000 / 100000 = 1.0718
so we will get result every year (On paper):
2014 = 100000
2015 = 107187 (100000 * 1.0718)
2016 = 114890 (107187 * 1.0718)
2017 = 123147 (114890 * 1.0718)
2018 = 132000
How to hold previous year variable to get result as above?
This is my PHP code:
for($i > $start; $i < $end; $i++) {
$this->ProjectPopulation->create();
$increase = array(
'project_id' => $project_id,
'year' => $i,
'percent_increase' => $this->percentage_increase($current_population, $target_population, $year),
'population' => ??? // Problem here
);
$this->ProjectPopulation->save($increase);
}
Thanks
There are two resources I'd recommend you taking a look at:
http://php.net/manual/en/ref.math.php
and
http://www.w3schools.com/php/php_ref_math.asp
You can solve this by doing the calculation each time, but it would be faster to use a loop in order to iterate through them all.
So something like this:
$population = 100000;
$year = 2014;
//I'd print the first one out prior to the loop, or you could put an if($year == 2014) in the loop, your choice really.
for(i= 0; i < 4; i++)
{
//do calculation in here.
echo "The current years is" . $year;
$yearsCalculation = $population * 1.0718;
//then save that value for the next time around the loop.
$population = $yearsCalculation ;
echo $yearsCalculation ;
//increase years each time round loop
$year++;
}
If you then want to store it in a MySQL database you might want to have a look at this tutorial:
http://www.w3schools.com/php/php_mysql_insert.asp
Hope that helped!
Solve
private function power($current_population, $target_population, $year) {
$devide = $target_population / $current_population;
$pwrdevider = 1 / $year;
$power = pow($devide, $pwrdevider);
return $power;
}
private function percentage_increase($current_population, $target_population, $year) {
$devide = $target_population / $current_population;
$power = pow($devide, 1 / $year);
$increase = ($power - 1) * 100;
return $increase;
}
$start = 2014;
$end = 2018;
$diff = $end - $start;
for($i = 1, $data = 100000; $i <= $diff; $i++) {
$data *= $this->power(100000, 132000, $diff);
$increase = array(
'project_id' => 1,
'year' => $start += 1,
'percent_increase' => $this->percentage_increase(100000, 132000, $diff),
'population' => $data
);
print_r($increase);
}
This is my result
Array
(
[project_id] => 1
[year] => 2015
[percent_increase] => 7.1873373728262
[population] => 107187.33737283
)
Array
(
[project_id] => 1
[year] => 2016
[percent_increase] => 7.1873373728262
[population] => 114891.25293076
)
Array
(
[project_id] => 1
[year] => 2017
[percent_increase] => 7.1873373728262
[population] => 123148.87489076
)
Array
(
[project_id] => 1
[year] => 2018
[percent_increase] => 7.1873373728262
[population] => 132000
)
Thanks all

Check for overlapping times on the same date in an array?

I have an array that is produced from people wanting to reserve a time block to volunteer with our organization. I want to check to see if they chose time blocks on the same day that overlap. In my example array below the first and third elements are overlapping and I need to detect that. Any recommendation would be much appreciated:
Array
(
[0] => Array
(
[id_pc_time_blocks] => 3
[id_pc] => 2
[pc_date] => 2012-11-21
[pc_time_block] => 9:00 AM-1:00 PM
[pc_time_block_max] => 25
[pc_time_block_count] => 0
[pc_name] => Atlanta
)
[1] => Array
(
[id_pc_time_blocks] => 4
[id_pc] => 2
[pc_date] => 2012-11-21
[pc_time_block] => 1:00 PM-5:00 PM
[pc_time_block_max] => 25
[pc_time_block_count] => 10
[pc_name] => Atlanta
)
[2] => Array
(
[id_pc_time_blocks] => 6
[id_pc] => 2
[pc_date] => 2012-11-21
[pc_time_block] => 10:00 AM-2:00 PM
[pc_time_block_max] => 25
[pc_time_block_count] => 0
[pc_name] => Atlanta
)
[3] => Array
(
[id_pc_time_blocks] => 6
[id_pc] => 2
[pc_date] => 2012-11-23
[pc_time_block] => 10:00 AM-2:00 PM
[pc_time_block_max] => 25
[pc_time_block_count] => 0
[pc_name] => Atlanta
)
[4] => Array
(
[id_pc_time_blocks] => 6
[id_pc] => 2
[pc_date] => 2012-11-23
[pc_time_block] => 3:00 AM-6:00 PM
[pc_time_block_max] => 25
[pc_time_block_count] => 0
[pc_name] => Atlanta
)
)
Not recommended for HUGE arrays, but here's a quick solution. You need to break hte times into unix time stamps for comparisson
// Run down each element of the array. (I've called it MyStartArray)
$numElements = count($MyStartArray);
for ($i=0; $i<$numElements ; $i++) {
// Calculate "Start Time" and "End Time" as Unix time stamps (use mktime function) and store as another items in the array
// You can use preg_match or substr to get the values to pump into mktime() below - not writing hte whole thing for you ;)
$MyStartArray[$i]['start_time'] = mktime( ... );
$MyStartArray[$i]['end_time'] = mktime( ... );
// Now run through all the previous elements to see if a start time is before the end time, or an end time is after the start time.
if ($i > 0) {
for ($j=0; $j<$i;$j++) {
if ($MyStartArray[$i]['start_time'] < $MyStartArray[$j]['end_time'] ||
$MyStartArray[$j]['end_time'] > $MyStartArray[$j]['start_time'] ) {
echo 'CLASH';
}
}
}
}
Here is my solution for checking each date for overlapping times. The only thing is, this is for my particular scenario and does not account for overlapping years as I dont have that need for this application.
Here is my working example:
$dateIdx = 0;
foreach($timeblocks_array as $obj) {
$timeblocks_array[$dateIdx]["intDay"] = idate("z",strtotime($obj["pc_date"]));
$timeblocks_array[$dateIdx]["intStart"] = intval($obj["start_time"]);
$timeblocks_array[$dateIdx]["intEnd"] = intval($obj["end_time"]);
$mindates[] = idate("z",strtotime($obj["pc_date"]));
$dateIdx++;
}
$minDateSingle = min($mindates);
$maxDateSingle = max($mindates);
$currentDate = $minDateSingle;
$dateIdx = 0;
while ($currentDate <= $maxDateSingle) {
$hrIndex = 0;
while ($hrIndex < 24) {
$matrixArray[$dateIdx][$hrIndex]["count"] = 0;
$matrixArray[$dateIdx][$hrIndex]["intDay"] = $currentDate;
$hrIndex++;
}
// calculate counts:
$hourIdx = 0;
foreach($matrixArray[$dateIdx] as $hour){
foreach($timeblocks_array as $block) {
if ($hour["intDay"] == $block["intDay"]) {
if ($hourIdx >= $block["intStart"] && $hourIdx < $block["intEnd"]) {
$matrixArray[$dateIdx][$hourIdx]["count"] = $matrixArray[$dateIdx][$hourIdx]["count"] + 1;
$matrixArray[$dateIdx][$hourIdx]["requests"][] = $block;
}
}
}
$hourIdx++;
}
$dateIdx++;
$currentDate = $currentDate + 1;
}
//loop through the matrix array and timeblocks array to see if they intersect
foreach($matrixArray as $day) {
$hourIdx = 0;
foreach($day as $hour) {
if ($hour["count"] > 1) {
//echo $hour["intDay"]." - Overlap on Hour $hourIdx\n";
$smarty->assign('overlappingError', 1);
$error = 1;
foreach($hour["requests"] as $overlapblock) {
//echo " --> conflict: ". $overlapblock["pc_date"]." ".$overlapblock["pc_time_block"]." (".$overlapblock["intStart"]." to ".$overlapblock["intEnd"].")\n";
}
} else if ($hour["count"] == 1) {
// these are valid hours
}
$hourIdx++;
}
}
Robbie's answer did not work for me. I found the rules that needed to be in place going off his example for just dates were:
$i[start] needs to be less than and not equal to $i[stop]
$i[start] needs to greater than and not equal to $j[stop]
$j[start] needs to be less than and not equal to $j[stop]
Therefore my solution was:
$numElements = count($dates);
for ($i=0; $i<$numElements; $i++) {
$dates[$i]['start_time'] = strtotime($dates[$i]['start']);
$dates[$i]['end_time'] = strtotime($dates[$i]['end']);
if ($i > 0) {
for ($j=0; $j<$i;$j++) {
if($dates[$i]['start_time'] >= $dates[$i]['end_time'] || $dates[$i]['start_time'] <= $dates[$j]['end_time'] || $dates[$j]['start_time'] >= $dates[$j]['end_time']) {
$this->set_error(['dates_overlap']);
$this->dates_overlap = true;
break;
}
}
}
if(isset($this->dates_overlap))
break;
}

PHP dates & day counting

I have a scenario where a user submits a start and stop date for a reservation in a hotel. The database has different (daily) prices for periods of the year. Given the user's date range I want to start by counting the number of days the user's range has in each range from the database and then multiplying that by the daily price for that date range.
User's dates:
03 Jun 2012 - 03 Jul 2012
Relevant date ranges from the DB
Array
(
[0] => stdClass Object
(
[date_from] => 2012-04-09
[date_to] => 2012-06-04
[price] => 44
)
[1] => stdClass Object
(
[date_from] => 2012-06-04
[date_to] => 2012-07-02
[price] => 52
)
[2] => stdClass Object
(
[date_from] => 2012-07-02
[date_to] => 2012-07-16
[price] => 61
)
)
As you can see the reservation begins in the first range, spans the second range and ends in the third range.
foreach ($dates as $key => $d)
{
$days = 0;
$user_start = strtotime($range['start']);
$user_stop = strtotime($range['stop']);
$db_start = strtotime($d->date_from);
$db_stop = strtotime($d->date_to);
if( $user_start >= $db_start && $user_stop < $db_stop )
{
$start = $range['start'];
$stop = $range['stop'];
}
elseif( $user_start <= $db_start && $user_stop > $db_start )
{
$start = $d->date_from;
$stop = $d->date_to;
}
elseif( $user_start <= $db_stop && $user_stop > $db_stop )
{
$start = $range['start'];
$stop = $d->date_to;
}
$days = calculate_nbr_days($start, $stop);
$price += $days * $d->price;
}
This code almost works, except for the fact it takes the end of the reservation ($stop) as being the last date of the DB range instead of the user's range and I can't figure out why!
Have you considered doing it all in SQL? Something this should work:
SELECT
SUM(
datediff(
IF($dateTo>date_to,date_to,$dateTo),
IF($dateFrom<date_from,date_from,$dateFrom)
) * price_per_day
) as total_cost
FROM ranges
WHERE '$dateFrom'<=date_to
AND '$dateTo'>=date_from

Categories