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 ;
}
}
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.
Am trying to get hours from midnight from the current time
that is suppose now it's 02:34 then I expect to get
02:34 - 02:00,
01:00 - 02:00,
00:00 - 01:00
as an array
so I have tried
function getFromMidnight(){
$durations = [];
$currentTime = strtotime("now");
$iStartOfHour = $currentTime - ($currentTime % 3600);
$midnight = strtotime('today midnight');
$hours = floor(($iStartOfHour - $midnight)/3600);
array_push($durations,["from"=>$currentTime, "to"=>$iStartOfHour]);
if(floor(($iStartOfHour - $midnight)/3600) > 0){
for ($val = 1;$val <= $hours;$val++){
$newval = $val +=1;
array_push($durations, ["from"=>strtotime('- '.$val.' hours',$iStartOfHour),"to"=>strtotime('- '.$newval.' hours',$iStartOfHour)]);
}
}
return $durations;
}
For the first array has the correct durations e.g. from the above example 02:34-02:00 but the next arrays are messed up giving me wrong values with constant timestamps eg: 01:00 - 01:00
I suspect its my for loop with an error, what could be wrong?
I would not use that code, instead of working things out just use DateInterval inverted and work backwards. Then sub() the hour in the loop to get the offset.
<?php
date_default_timezone_set('UTC');
$begin = new DateTime('today midnight');
$end = new DateTime();
$interval = new DateInterval('PT60M');
$interval->invert = 1;
$daterange = new DatePeriod($begin, $interval, $end);
$range = [];
foreach ($daterange as $date){
$range[] = [
'from' => $date->format("H:i"),
'to' => $date->sub($interval)->format("H:i")
];
}
print_r($range);
https://3v4l.org/BMSbI
Result:
Array
(
[0] => Array
(
[from] => 00:00
[to] => 01:00
)
[1] => Array
(
[from] => 01:00
[to] => 02:00
)
[2] => Array
(
[from] => 02:00
[to] => 03:00
)
[3] => Array
(
[from] => 03:00
[to] => 04:00
)
)
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 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 />';
}
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