I am trying to work out if two time ranges in PHP overlap in same array.If a time range is nested in between the start and end times of another time range, it's not being matched.
Array
(
[1] => Array
(
[start_time] => 01:00
[end_time] => 04:00
)
[2] => Array
(
[start_time] => 05:00
[end_time] => 08:00
)
[3] => Array
(
[start_time] => 06:00
[end_time] => 12:00
)
[4] => Array
(
[start_time] => 13:00
[end_time] => 24:00
)
)
To compare two date/times you have to convert them first into \DateTime objects.
Then you have to do a simple comparison with < and > operators.
But what you are trying to achieve is not to get Date/Times but simply compare hours.
So, as they are simple string, you should first convert them in numbers.
The best thing you can do is to convert all of them into minutes, and then compare them.
So, something like this should work:
function convertFromHoursToMinutes($string)
{
// Separate hours from minutes
$split = explode(':', $string);
// Transform hours into minutes
$hoursToMinutes = $split[0] * 60;
$total = $hoursToMinutes + (int)$split[1];
return $total;
}
$ranges = [
[
'start_time' => '01:00',
'end_time' => '04:00'
],
[
'start_time' => '05:00',
'end_time' => '08:00'
],
[
'start_time' => '06:00',
'end_time' => '12:00'
],
[
'start_time' => '13:00',
'end_time' => '24:00'
]
];
foreach ($ranges as $range)
{
// Convert values into minutes
$start_time = convertFromHoursToMinutes($range['start_time']);
$end_time = convertFromHoursToMinutes($range['end_time']);
// Do the comparison
if ($start_time > $end_time)
{
echo 'Start > end';
} else {
echo 'Start < end';
}
echo '<hr />';
}
Related
I am using below code to find out correct total hours but it is giving wrong results :-
Using getSortedDays to sort my array
Using addTotalAttendedHours to add total and add it into total attended hours ,I have tried all possible methods of time and none of them are working .
Even I tried removing abs and round but nothing is helping
<?php
declare(strict_types=1);
$data = [
'2020-07-14' =>
[
[
'start_time' => '14:15:00',
'end_time' => '17:45:00',
],[
'start_time' => '14:30:00',
'end_time' => '17:30:00',
],[
'start_time' => '14:30:00',
'end_time' => '17:30:00',
],
],
'2020-07-15' => [
[
'start_time' => '13:30:00',
'end_time' => '17:00:00',
],[
'start_time' => '09:00:00',
'end_time' => '14:00:00',
],
],
];
function getSortedDays(array $days): array {
return array_map(function (array $day) {
array_multisort(array_column($day, 'start_time'), SORT_ASC, $day);
return $day;
}, $days);
}
function addTotalAttendedHours(array $days): array {
$sortedDays = getSortedDays($days);
$days = array_map(function (array $day) {
$sum = (new DateTime())->setTimestamp(0);
$previousEnd = null;
foreach ($day as $time) {
$currentStart = new DateTimeImmutable($time['start_time']);
$currentEnd = new DateTimeImmutable($time['end_time']);
$sum->add($currentStart->diff($currentEnd));
if ($previousEnd !== null && $currentStart < $previousEnd) {
$sum->sub($currentStart->diff($previousEnd));
}
$previousEnd = $currentEnd;
}
$attendedSeconds = $sum->getTimestamp();
$day['total_attended_hours'] = sprintf(
'%02u:%02u:%02u',
$attendedSeconds / 60 / 60,
($attendedSeconds / 60) % 60,
$attendedSeconds % 60
);
return $day;
}, $sortedDays);
return $days;
}
echo "<pre>";
print_r(addTotalAttendedHours($data));
?>
Result
Array
(
[2020-07-14] => Array
(
[0] => Array
(
[start_time] => 14:15:00
[end_time] => 17:45:00
)
[1] => Array
(
[start_time] => 14:30:00
[end_time] => 17:30:00
)
[2] => Array
(
[start_time] => 14:30:00
[end_time] => 17:30:00
)
[total_attended_hours] => 03:15:00 //It should be 03:30:00
)
[2020-07-15] => Array
(
[0] => Array
(
[start_time] => 09:00:00
[end_time] => 14:00:00
)
[1] => Array
(
[start_time] => 13:30:00
[end_time] => 17:00:00
)
[total_attended_hours] => 08:00:00
)
)
Using above code I am getting wrong total_attended_hours where for date 2020-07-14 the result should be 03:30:00 but it is giving 03:15:00
For the first day you have these time ranges:
14:15 – 17:45
14:30 – 17:30
14:30 – 17:30
And you are doing this:
calculating the diff for the first range: 3:30
calculating the diff for the second range (3:00) and adding it: 3:30 + 3:00 = 06:30
because the start of the second range is before the previous end, you are subtracting the diff between them. the diff between 14:30 and 17:45 is 3:15, so 6:30 - 3:15 = 3:15
but the second range already ends at 17:30, so you are missing the 15 minutes between 17:30 and 17:45.
You could change the subtracting line to:
$sum->sub($currentStart->diff(min($currentEnd, $previousEnd)));
This way you wouldn't subtract too much anymore, because you would subtract only the diff of the overlapping part until the minimum of current and previous end.
I have an array like below & array would be dynamic.
how to check time is overlap another time of same day
I want to check the time between another time ..days wise
Array
(
[Monday] => Array
(
[0] => Array
(
[start_time] => 02:00 PM
[end_time] => 03:00 PM
)
[1] => Array
(
[start_time] => 04:00 PM
[end_time] => 05:05 PM
)
[2] => Array
(
[start_time] => 03:30 PM
[end_time] => 05:05 PM
)
)
[Sunday] => Array
(
[0] => Array
(
[start_time] => 03:00 PM
[end_time] => 04:00 PM
)
)
)
the single day has multiple time
I want to check time valid for Monday
For EX. Monday
1. 02:00 PM - 03:00 PM
2. 03:00 PM - 04:00 PM
3. 05:00 PM - 06:00 PM
above is valid time but if 3 value is
3:30 PM - 05:00 PM
This is not the valid time
I want to check dynamically
You can process one interval at the time, and check if it overlaps a previous one.
Checking if the start time or the end time is inside any other intervals is enough to find an overlap.
$times = array('Monday' => array(
0 => array('start_time' => '02:00 PM','end_time' => '03:00 PM')
, 1 => array('start_time' => '03:00 PM','end_time' => '05:05 PM')
, 2 => array('start_time' => '03:30 PM','end_time' => '05:05 PM')
)
, 'Sunday' => array(
0 => array('start_time' => '03:00 PM','end_time' => '04:00 PM')
)
);
foreach($times as $dayName => $intervals) // check each day separetely
{
// keep the indices of the valid intervals (we don't want to check if an interval overlaps an invalid one)
$validIntervals = array(0); /* no need to check first interval, always valid */
for($i = 1 ; $i < count($intervals) ; ++$i){
// parse the data of the interval to test
$start = DateTime::createFromFormat('H:i A', $intervals[$i]['start_time']);
$end = DateTime::createFromFormat('H:i A', $intervals[$i]['end_time']);
$isValid = true ;
// check if there is a conflict with a previous valid interval
foreach($validIntervals as $j)
{
$validStart = DateTime::createFromFormat('H:i A', $intervals[$j]['start_time']);
$validEnd = DateTime::createFromFormat('H:i A', $intervals[$j]['end_time']);
if(
($start > $validStart and $start < $validEnd) // the start of the interval is inside a previous one
or
($end > $validStart and $end < $validEnd) // the end of the interval is inside a previous one
)
{
// ** your processing of the incorrect interval here **
echo "In day '$dayName', interval $i [" . $intervals[$i]['start_time'] . ', ' . $intervals[$i]['end_time'] . "] overlaps with Interval $j [" . $intervals[$j]['start_time'] . ', ' . $intervals[$j]['end_time'] . "]" ;
$isValid = false ;
break ; // we found one conflict, no need to check for other one
}
}
// no conflict found, the interval is valid
if($isValid)
$validIntervals[] = $i ;
}
}
I have an input array which contains arrays of day and time data:
$temp = Array
(
[0] => Array
(
[day] => Tuesday
[time] => 07:44 pm - 08:44 pm
)
[1] => Array
(
[day] => Tuesday
[time] => 04:00 am - 04:25 am
)
[2] => Array
(
[day] => Sunday
[time] => 04:00 am - 04:25 am
)
[3] => Array
(
[day] => Sunday
[time] => 04:00 am - 04:25 am
)
[4] => Array
(
[day] => Friday
[time] => 04:00 am - 04:25 am
)
[5] => Array
(
[day] => Friday
[time] => 04:00 am - 04:25 am
)
)
Now I want to group the common times display as one element and if the time is the same for more than one day then it's should display one entry. So what is the best way to achieve the desired result without making this too complex?
Array
(
[0] => Array
(
[day] => Tuesday
[time] => 04:00 am - 04:25 am & 07:44 pm - 08:44 pm
)
[1] => Array
(
[day] => Friday & Sunday
[time] => 04:00 am - 04:25 am
)
)
Here it's what I have done:
$final = [];
foreach ($temp as $val) {
$final[strtolower($val['day'])][] = $val['time'];
}
foreach ($final as $k => $v) {
sort($v);
$v = array_unique($v);
$last = array_pop($v);
$final[$k] = [
'day' => $k,
'time' => count($v) ? implode(", ", $v) . " & " . $last : $last,
];
}
There are 4 basic steps:
Remove duplicate rows.
Sort all rows by day (via lookup) ASC, then time (as a string) ASC.
Store joined time values using day values as temporary keys.
Store joined day values using joined time values as temporary keys.
Code: (Demo)
$array=[
['day'=>'Tuesday','time'=>'07:44 pm - 08:44 pm'],
['day'=>'Tuesday','time'=>'04:00 am - 04:25 am'],
['day'=>'Sunday','time'=>'04:00 am - 04:25 am'],
['day'=>'Sunday','time'=>'04:00 am - 04:25 am'],
['day'=>'Friday','time'=>'04:00 am - 04:25 am'],
['day'=>'Friday','time'=>'04:00 am - 04:25 am']
];
$array=array_unique($array,SORT_REGULAR); // remove exact duplicate rows
$order=array_flip(['Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday']); // lookup array
usort($array,function($a,$b)use($order){ // sort by day name ASC then time ASC
if($order[$a['day']]==$order[$b['day']]){
return $a['time']<=>$b['time']; // 2nd sort criteria: time string
}
return $order[$a['day']]<=>$order[$b['day']]; // 1st sort criteria: day name via lookup array
});
foreach($array as $row){
if(!isset($temp[$row['day']])){
$temp[$row['day']]=$row['time']; // store time for first occurring day
}else{
$temp[$row['day']].=" & {$row['time']}"; // join times (for pre-existing day) as they are found
}
}
foreach($temp as $day=>$times){
if(!isset($result[$times])){
$result[$times]=['day'=>$day,'time'=>$times]; // store first occurring day and time using time as temp key
}else{
$result[$times]['day'].=" & $day"; // join day names as they are found
}
}
var_export(array_values($result)); // optionally remove the temporary keys
Output:
array (
0 =>
array (
'day' => 'Tuesday',
'time' => '04:00 am - 04:25 am & 07:44 pm - 08:44 pm',
),
1 =>
array (
'day' => 'Friday & Sunday',
'time' => '04:00 am - 04:25 am',
),
)
Here is another version that doesn't call array_unique(), but I don't like it as much because it does iterated sort() calls and generates a deeper temporary array.
foreach($array as $row){
$temp[$row['day']][$row['time']]=$row['time']; // remove duplicates and group by day
}
$order=array_flip(['Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday']); // lookup array
uksort($temp,function($a,$b)use($order){
return $order[$a]<=>$order[$b]; // sort by day name via lookup array
});
foreach($temp as $day=>$times){
sort($times); // sort time elements ASC
$time_key=implode(' & ',$times); // join the day's time elements
if(!isset($result[$time_key])){ // if first occurrence of the time string
$result[$time_key]=['day'=>$day,'time'=>$time_key]; // store data using time string as temp key
}else{
$result[$time_key]['day'].=" & {$day}"; // concatenate new day to day element
}
}
var_export(array_values($result));
I have an array like this:
Array
(
[0] => stdClass Object
(
[Start] => 08:00
[dayName] => Tuesday
[dayID] => 2
[courseName] => Math
)
[1] => stdClass Object
(
[Start] => 10:00
[dayName] => Tuesday
[dayID] => 2
[courseName] => Geography
)
[2] => stdClass Object
(
[Start] => 14:00
[dayName] => Tuesday
[dayID] => 2
[courseName] => Science
)
[3] => stdClass Object
(
[Start] => 10:00
[dayName] => Thursday
[dayID] => 4
[courseName] => Math
)
[4] => stdClass Object
(
[Start] => 18:00
[dayName] => Friday
[dayID] => 5
[courseName] => History
)
)
What I want to do is , I want to compare the daya nd time now to the values in the array. For example lets assume that it is 7:00 am and it is Tuesday. Then I want to get the Object[0]. But if it is 11:00 o'clock then i need to get the Object[2] which starts at 14:00 on Tuesday.
It it is Tuesday and 16:00 o'clock then i need Object[3] .
If it is a weekend then i need the beginning of the week which is Tuesday at 08:00 with the Math Course.
I tried to get that using key => value but I mixed up.
How can I compare the Day and then time and in case there is a Course on that combination just pick it up if not just continue checking.
regards
littleblue
function getObject($array){
$timeNow = date('U'); // time now
$oneHour = $timeNow+3600; // time now + 1 hour
foreach($array as $num => $one){ // loop in each $array
$date = strtotime($one->Start); // convert start time to timestamp
if($date >= $timeNow && $date < $oneHour && date('l', $date) == $one->dayName){ // if all criteria met
return $array[$num]; // return that object
}
}
return array('no data'); // if no criteria is met return no data.
}
$course = getObject($yourArray);
echo $course->courseName;
You can mix the use of DateTime and a simple min search :
$myCourse = null;//initialisation
$myCourseDate =null;
$now = new DateTime;
foreach($array as $course){
//get the date from the datas
$date = DateTime::createFromFormat('l h:i',$course->dayName.' '.course->start);
if($now < $date && ($myCourseDate === null || $myCourseDate > $date)){
$myCourse = $course;
$myCourseDate = $date;
}
}
//at the end of the loop, you've got the expected course
I have checked other SO questions but can't seem to find anything related to this - point me in the right direction if I missed something.
I currently have the following array containing a day and time (requested meeting time):
Array (
[0] =>
day => 'Thursday',
start_time => '14:00',
end_time => '15:00'
[1] =>
day => 'Thursday',
start_time => '16:30',
end_time => '17:30'
)
The user is required to attend both of these times. What I am attempting to do is compare against another array containing a users current appointments and detect if there is a clash / time overlap issue
Array (
[0] =>
day => 'Monday',
start_time => '09:00',
end_time => '13:00'
[1] =>
day => 'Wednesday',
start_time => '10:45',
end_time => '11:15'
[2] =>
day => 'Thursday',
start_time => '16:45',
end_time => '17:45'
)
Using the above example I want to identify there is a clash with the second timeslot [1] => of the first array (proposed meetings) which clashes with the third timeslot [2] => of the second array (current meetings).
I have seen examples with timestamps and full dates, but not just days and start / end times.
If they are all in a database, a query for conflicts could look like:
SELECT `plan`.*
FROM `plan`
JOIN `current` ON `plan`.`day`=`current`.`day`
WHERE
`plan`.`start_time` < `current`.`end_time`
AND `plan`.`end_time` > `current`.`start_time`
where current is the table with the current schedule and plan is the table with the user's schedule. I haven't tested this just because I don't have an example, but this should return rows where the day is the same and plan times overlap current times.
edit: You might also want to do stuff like mark current as tentative if they are not required. you can put that into the query where clause.
Also the > and < mean that the start and end minute are not inclusive. so if one ends at the same time as another begins, this will not flag them as a conflict. If you want the start/end minute to be inclusive, change them to >= and <=.
You can do it this way
$slot1 = Array (
0 => array (
'day' => 'Thursday',
'start_time' => '14:00',
'end_time' => '15:00'
),
1 => array (
'day' => 'Thursday',
'start_time' => '16:30',
'end_time' => '17:30'
)
);
$slot2 = Array (
0 => array (
'day' => 'Monday',
'start_time' => '09:00',
'end_time' => '13:00'
),
1 => array (
'day' => 'Wednesday',
'start_time' => '10:45',
'end_time' => '11:15'
),
2 => array (
'day' => 'Thursday',
'start_time' => '16:45',
'end_time' => '17:45'
)
);
echo "<pre>";
$format = "l H:s";
foreach($slot1 as $value)
{
$slot1Start = DateTime::createFromFormat ($format , $value ['day'] . " " . $value['start_time'] );
$slot1End = DateTime::createFromFormat ( $format , $value ['day'] . " " . $value['end_time'] );
foreach($slot2 as $value2)
{
$slot2Start = DateTime::createFromFormat ( $format , $value2 ['day'] . " " .$value2['start_time'] );
$slot2End = DateTime::createFromFormat ( $format , $value2 ['day'] . " " .$value2 ['end_time'] );
if ($slot1Start <= $slot2End && $slot1End >= $slot2Start) {
echo sprintf ( "Even Overlap on %s %s-%s AND %s %s-%s", $value ['day'], $value ['start_time'], $value ['end_time'],
$value2 ['day'], $value2 ['start_time'], $value2 ['end_time'] ), PHP_EOL;
}
}
}
Output
Even Overlap on Thursday 16:30-17:30 AND Thursday 16:45-17:45
There are your a bit modified arrays code
$current_sheduler = Array (
[0] =>
day => 'Thursday',
start_time => '14:00',
end_time => '15:00'
[1] =>
day => 'Thursday',
start_time => '16:30',
end_time => '17:30'
)
$user_plans = Array (
[0] =>
plan_name => 'event1',
day => 'Monday',
start_time => '09:00',
end_time => '13:00'
[1] =>
plan_name => 'event2',
day => 'Wednesday',
start_time => '10:45',
end_time => '11:15'
[2] =>
plan_name => 'event3',
day => 'Thursday',
start_time => '16:45',
end_time => '17:45'
)
Here is way to process your arrays and catches if event planned by user matches time from your first array
foreach($user_plans as $plan){
foreach($current_sheduler as current){
if($plan['day']==$current['day']){
if(
($plan['start_time']>=$current['start_time']) &&
($plan['end_time']<=$current['end_time'])
){
echo "Event {$plan['plan_name']} is working now!";
}
}
}
}
This is general logic, for situation, when match means full period match only.
Another notice: befor compare times, you need to recount them into counted minutes format.