I have written some code to retrieve the various work hours for my different restaurants from a database and display whether they are open of not right now.
The work hours are defined like this: 10:30to13:30/18:30to22:30.
The problem is that I couldn't find a short way to write all the following code. How can I reduce its length?
if ($result > 0) {
while($row = $stmt->fetch()) {
//SETUP HOURS FOR EACH DAY
$timesSun = $row['sunday'];
$timesMon = $row['monday'];
$timesTue = $row['tuesday'];
$timesWed = $row['wednesday'];
$timesThu = $row['thursday'];
$timesFri = $row['friday'];
$timesSat = $row['saturday'];
//SEPARATE DAY & NIGHT
$sepDNsun = explode('/',$timesSun);
$sepDNmon = explode('/',$timesMon);
$sepDNtue = explode('/',$timesTue);
$sepDNwed = explode('/',$timesWed);
$sepDNthu = explode('/',$timesThu);
$sepDNfri = explode('/',$timesFri);
$sepDNsat = explode('/',$timesSat);
//SEPARATE OPEN AND CLOSE
$daySun = explode('to',$sepDNsun[0]);
$nightSun = explode('to',$sepDNsun[1]);
$dayMon = explode('to',$sepDNmon[0]);
$nightMon = explode('to',$sepDNmon[1]);
$dayTue = explode('to',$sepDNtue[0]);
$nightTue = explode('to',$sepDNtue[1]);
$dayWed = explode('to',$sepDNwed[0]);
$nightWed = explode('to',$sepDNwed[1]);
$dayThu = explode('to',$sepDNthu[0]);
$nightThu = explode('to',$sepDNthu[1]);
$dayFri = explode('to',$sepDNfri[0]);
$nightFri = explode('to',$sepDNfri[1]);
$daySat = explode('to',$sepDNsat[0]);
$nightSat = explode('to',$sepDNsat[1]);
//SET OPEN & CLOSE
$dayOpenSun = $daySun[0];
$dayCloseSun = $daySun[1];
$nightOpenSun = $nightSun[0];
$nightCloseSun = $nightSun[1];
$dayOpenMon = $dayMon[0];
$dayCloseMon = $dayMon[1];
$nightOpenMon = $nightMon[0];
$nightCloseMon = $nightMon[1];
$dayOpenTue = $dayTue[0];
$dayCloseTue = $dayTue[1];
$nightOpenTue = $nightTue[0];
$nightCloseTue = $nightTue[1];
$dayOpenWed = $dayWed[0];
$dayCloseWed = $dayWed[1];
$nightOpenWed = $nightWed[0];
$nightCloseWed = $nightWed[1];
$dayOpenThu = $dayThu[0];
$dayCloseThu = $dayThu[1];
$nightOpenThu = $nightThu[0];
$nightCloseThu = $nightThu[1];
$dayOpenFri = $dayFri[0];
$dayCloseFri = $dayFri[1];
$nightOpenFri = $nightFri[0];
$nightCloseFri = $nightFri[1];
$dayOpenSat = $daySat[0];
$dayCloseSat = $daySat[1];
$nightOpenSat = $nightSat[0];
$nightCloseSat = $nightSat[1];
//SET STORE OPENING HOURS
$storeSchedule = [
'Sun' => [$dayOpenSun => $dayCloseSun, $nightOpenSun => $nightCloseSun],
'Mon' => [$dayOpenMon => $dayCloseMon, $nightOpenMon => $nightCloseMon],
'Tue' => [$dayOpenTue => $dayCloseTue, $nightOpenTue => $nightCloseTue],
'Wed' => [$dayOpenWed => $dayCloseWed, $nightOpenWed => $nightCloseWed],
'Thu' => [$dayOpenThu => $dayCloseThu, $nightOpenThu => $nightCloseThu],
'Fri' => [$dayOpenFri => $dayCloseFri, $nightOpenFri => $nightCloseFri],
'Sat' => [$dayOpenSat => $dayCloseSat, $nightOpenSat => $nightCloseSat]
];
// current or user supplied UNIX timestamp
$timestamp = time();
// default status
$status = $lang["NO-READY"];
// get current time object
$currentTime = (new DateTime())->setTimestamp($timestamp);
// loop through time ranges for current day
foreach ($storeSchedule[date('D', $timestamp)] as $startTime => $endTime) {
// create time objects from start/end times
$startTime = DateTime::createFromFormat('G:i', $startTime);
$endTime = DateTime::createFromFormat('G:i', $endTime);
// check if current time is within a range
if (($startTime < $currentTime) && ($currentTime < $endTime)) {
$status = $lang["READY"];
break;
}
}
//OUTPUT CONTENT
echo '<li>
<div class="rest-list-content">
<a href="'. $location .'/restaurants/'. $row["rest_url"] .'">
<img src="images/all_rest/'. $row["rest_logo"] .'" alt="'. $row["rest_name"] .'">
<h1>'. $row["rest_name"] .'</h1>
<p>Cuisine: <span>'. $row["cuisine_name"] .'</span></p>
<p>Minimun Order: <span>$'. $row["rest_min_order"] .'</span></p>
<p class="availability">'. $status .'</p>
</a>
</div>
</li>';
}
} else {
echo "0 results";
}
Preamble
I might be wrong, but looking at this code, it seems that what you are doing is showing whether the restaurants are opened right now or not.
There are quite a few things to optimize in there:
You keep redeclaring $timestamp = time(); and the other variables associated to it. This is rather inefficient as the time won't change much during the execution of the script. Even if the script took more than a second to run, this is really negligible. This must be declared once, before the loop.
You process all 7 days of the week while you only need the one that corresponds to today, we can cut this workload down by 85%.
So many unique variables. This can be reduced significantly. Arrays are our friends.
Let's minimize
$currentTime = new DateTime('now');
$currentDay = strtolower($currentTime->format('l'));
if($result > 0) {
while($row = $stmt->fetch()) {
$schedule = explode('/',
str_replace('to', '/', $row[$currentDay])
);
foreach($schedule as $time) {
$schedule['time'][] = DateTime::createFromFormat('G:i', $time);
}
$status =
($schedule['time'][0] <= $currentTime && $currentTime <= $schedule['time'][1])
||
($schedule['time'][2] <= $currentTime && $currentTime <= $schedule['time'][3])
? $lang["READY"]
: $lang["NO-READY"];
/*
HTML GOES HERE
*/
}
}
else {
echo '0 results';
}
What happened
Since the date and time are unlikely to change during the execution of the script, we moved that part at the beginning.
This returns a DateTime object for "now", no need to call time():
$currentTime = new DateTime('now');
This returns the full textual day of today, which gets converted to lowercase to match the database records (ex: tuesday):
$currentDay = strtolower($currentTime->format('l'));
Then for each record iterated:
We use $row[$currentDay], which is today's data.
We replace to with /, this results in xx:xx/xx:xx/xx:xx/xx:xx.
We explode using /.
$schedule = explode('/',
str_replace('to', '/', $row[$currentDay])
);
We now have an array containing 4 values:
Array
(
[0] => 9:30
[1] => 13:30
[2] => 17:30
[3] => 20:30
)
We create a DateTime object from each of these values, that we store in that same array:
foreach($schedule as $time) {
$schedule['time'][] = DateTime::createFromFormat('G:i', $time);
}
We now have this array:
Array
(
[0] => 9:30
[1] => 13:30
[2] => 17:30
[3] => 20:30
[time] => Array
(
[0] => DateTime Object
(
[date] => 2015-09-29 09:30:00.000000
[timezone_type] => 3
[timezone] => Europe/Paris
)
[1] => DateTime Object
(
[date] => 2015-09-29 13:30:00.000000
[timezone_type] => 3
[timezone] => Europe/Paris
)
[2] => DateTime Object
(
[date] => 2015-09-29 17:30:00.000000
[timezone_type] => 3
[timezone] => Europe/Paris
)
[3] => DateTime Object
(
[date] => 2015-09-29 20:30:00.000000
[timezone_type] => 3
[timezone] => Europe/Paris
)
)
)
We use a ternary operator to set the status (true ? true : false).
This checks if "now" is either between morning hours or evening hours at the same time.
$status =
($schedule['time'][0] <= $currentTime && $currentTime <= $schedule['time'][1])
||
($schedule['time'][2] <= $currentTime && $currentTime <= $schedule['time'][3])
? $lang["READY"]
: $lang["NO-READY"];
$status is now available, the html can be built.
Repeat until there is no record left.
We exit the loop.
We're done.
Related
There is a large array with timestamps, e.g.:
$timestamps = array();
for ($i = 0; $i < 5000; $i++) {
$timestamps[] = mt_rand(strtotime('1900-01-01 00:00:00 am'), strtotime('2100-12-31 11:59:59 pm'));
}
Now I need to return the timestamps with the earliest (min) and latest (max) clock time.
My approach:
echo date('Y-m-d h:i:s a', min(array_map('callback', $timestamps)));
echo "\n";
echo date('Y-m-d h:i:s a', max(array_map('callback', $timestamps)));
function callback($timestamp) {
return strtotime(date('h:i:s a', $timestamp));
}
This actually provides the earliest and latest clock time, but of course along with the current date (today).
How to return the original timestamps with the earliest and latest clock time?
You can use next code:
//run array_reduce over array
$res = array_reduce(
$timestamps, // timestaps array
function($res, $t) {
// get time from timestamp
$time = date('H:i:s', $t);
// if result min not exists
// or more then $time store new value to $res['min']
if (is_null($res['min'][0]) || $time<$res['min'][0])
$res['min'] = [$time, date('Y-m-d h:i:s a', $t)];
// if result max not exists
// or less then $time store new value to $res['max']
if (is_null($res['max'][0]) || $time>$res['max'][0])
$res['max'] = [$time, date('Y-m-d h:i:s a', $t)];
// return updated result
return $res;
},
// define initial $res with null values
['min'=>[null, null], 'max'=>[null, null]]
);
Share PHP online
Result:
Array
(
[min] => Array
(
[0] => 00:00:30
[1] => 1997-05-03 12:00:30 am
)
[max] => Array
(
[0] => 23:59:36
[1] => 1983-07-21 11:59:36 pm
)
)
I'm trying to separate the dates for example:
$arr=array(
"2018-06-27 20:30:20",
"2018-06-27 20:31:20",
"2018-06-27 20:37:20",
"2018-06-27 20:45:20",
"2018-06-27 20:48:20"
);
As you can see there are minutes with difference only of 1 minute or even seconds.
What I'm trying to accomplish is to force the dates to be 5 mins interval.
example output
2018-06-27 20:30:00
2018-06-27 20:35:00
2018-06-27 20:40:00
2018-06-27 20:45:00
2018-06-27 20:50:00
Here's my code
function roundToNearestMinuteInterval($time)
{
$time = (round(strtotime($time) / 300)) * 300;
return date('Y-m-d H:i:s', $time);
}
$temp="";
$wave=1;
foreach($arr as $a) {
if(empty($temp)) {
$temp= roundToNearestMinuteInterval($a);
}
$date= roundToNearestMinuteInterval($a);
if($temp==$date && $wave!=1){
$new=date('Y-m-d H:i:s',strtotime('+3 minutes',strtotime($a)));
$date= roundToNearestMinuteInterval($date);
$temp= $date;
}
$wave++;
echo $date."<br/>";
}
If you want to have an output array with all the 5 minute (or other interval) times between the earliest and latest times in the input array, you can just iterate between them, adding the interval in each loop:
$arr=array("2018-06-27 20:30:20","2018-06-27 20:31:20","2018-06-27 20:37:20","2018-06-27 20:45:20","2018-06-27 20:48:20");
function roundToNearestMinuteInterval($time, $interval) {
$timestamp = strtotime($time);
$rounded = round($timestamp / ($interval * 60), 0) * $interval * 60;
return $rounded;
}
$interval = 5; // minutes
$start = roundToNearestMinuteInterval(min($arr), $interval);
$end = roundToNearestMinuteInterval(max($arr), $interval);
for (; $start <= $end; $start += $interval * 60) {
$results[] = date('Y-m-d H:i:s', $start);
}
print_r($results);
Output:
Array
(
[0] => 2018-06-27 20:30:00
[1] => 2018-06-27 20:35:00
[2] => 2018-06-27 20:40:00
[3] => 2018-06-27 20:45:00
[4] => 2018-06-27 20:50:00
)
Demo on 3v4l.org
Solution with a DateTime extension dt (https://github.com/jspit-de/dt) returns an array with the date as a key. The value supplied is the number of rounded values from the input array. The algorithm can be implemented even without class extension with a few more commands.
$inputArr = array(
"2018-06-27 20:30:20",
"2018-06-27 20:31:20",
"2018-06-27 20:37:20",
"2018-06-27 20:45:20",
"2018-06-27 20:48:20"
);
$interval = "5 Minutes";
//create basis
$resArr = [];
$dt = dt::create(min($inputArr))->round($interval); //start
$endDate = dt::create(max($inputArr))->round($interval);
for(;$dt <= $endDate; $dt->modify($interval)){
$key = $dt->format("Y-m-d H:i:s");
$resArr[$key] = 0;
}
foreach($inputArr as $strDate){
$key = $dt::create($strDate)
->round($interval)
->format("Y-m-d H:i:s");
$resArr[$key]++;
}
The result $resArr
array (
'2018-06-27 20:30:00' => 2,
'2018-06-27 20:35:00' => 1,
'2018-06-27 20:40:00' => 0,
'2018-06-27 20:45:00' => 1,
'2018-06-27 20:50:00' => 1,
)
Situation:
I have arrays with information of the used appointments and the calculated days for the new appointment series based on PHP.
For example, the exist appointment array:
Array
(
[0] => Array
(
[date] => 2019-05-02
[time_start] => 14:00:00
[time_end] => 15:30:00
)
[1] => Array
(
[date] => 2019-05-06
[time_start] => 14:00:00
[time_end] => 15:30:00
)
)
Now, i will check have the calculated series (same array format) collisions with the exist appointments.
My Question:
How can i check if a collision exist beetween the start and end time and if yes how i can give the array a new value with a time windows after or before the exist appointment. This within a time windows from 08:00 am to 4:00 pm.
What i have is the calculation of the appointment days.
private function calculate_dates($data, $measure)
{
$this->load->model('calendar/calendar_model');
$holiday_dates = $this->calendar_model->get_holidays();
foreach ($holiday_dates as $key => $value) {
$holidays[] = $value['holiday_date'];
}
$begin = new DateTime($data->measure_begin);
$end = new DateTime($data->measure_end);
$oneday = new DateInterval("P1D");
$days = json_decode($data->measure_dates);
$wdays = array();
$ue_minutes = 0;
$minutes = ($data->measure_ue * $measure->ue_minutes/2);
$daterange = new DatePeriod( $begin, DateInterval::createFromDateString('+1 weekdays') ,$end );
foreach(new DatePeriod($begin, $oneday, $end->add($oneday)) as $day) {
$day_num = $day->format("N"); /* 'N' number days 1 (mon) to 7 (sun) */
if($day_num < 6 ) { /* weekday */
$wdays[] = $day;
}
}
$count = 1;
foreach($wdays as $date){
foreach ($days as $key => $value) {
if(mb_strtolower($date->format('l')) == $value){
if(($data->measure_ue/2)+1 != $count){
if(in_array($date->format('Y-m-d'), $holidays)) {
$dates[] = $this->close_days($date, $days, true)->format('l, d.m.Y');
} else {
$dates[] = $date->format('l, d.m.Y');
}
$count++;
}
}
}
}
return array(
'dates' => $dates,
'minutes' => round($minutes/count($dates))
);
}
private function close_days($date, $days, $init = false)
{
if($init){
$days[] = 'saturday';
$days[] = 'sunday';
}
if( in_array(mb_strtolower($date->format('l')), $days) ) {
$this->close_days($date->modify('next day'), $days);
}
return $date;
}
Any Ideas for a solution or maybe a code for a better way?
I have a script that queries an API and finds records within the last day.
I'd like to then loop through these results and solely get those within the last hour.
Can someone explain how I do this?
This is my array of daily results:
array(2) {
[0]=>
array(36) {
["CallRef"]=> string(10) "1234567891"
["CallStartTime"]=> string(8) "08:18:30"
}
[1]=>
array(36) {
["CallRef"]=> string(10) "1234567892"
["CallStartTime"]=> string(8) "14:04:20"
}
}
It's 14:40 here in the UK so my script should just grab the 2nd item from the array.
How about this?
$apiElements = [
['CallRef' => '1234567891', 'CallStartTime' => '08:18:30'],
['CallRef' => '1234567892', 'CallStartTime' => '14:04:20'],
];
$currentFormatted = (new DateTime())->format('H');
$startOfHour = DateTime::createFromFormat('H:i:s', $currentFormatted . ':00:00');
$endOfHour = DateTime::createFromFormat('H:i:s', $currentFormatted . ':59:59');
$callsInHour = array_filter($apiElements, function($element) use ($startOfHour, $endOfHour) {
$dt = DateTime::createFromFormat('H:i:s', $element['CallStartTime']);
return $dt >= $startOfHour && $dt <= $endOfHour;
});
Totally untested, but give it a try.
Next time, please post what code you tried...
Theres multiple ways to do this. You can get a Unix timestmap from a hour ago: strtotime('-1 hour'). You can explode the time on every : in the time and take the 2nd result from the explode result.
foreach ($array as $value){
$exploded=explode(":",$value['CallStartTime']);
if(date('H')-1>$exploded[1]){
//This means it's an hour ago.
}
}
Replace $allResults and try this:
date_default_timezone_set('UTC');
$hourAgo = strtotime(date('H:i:s')) - 3600;
foreach ($allResults as $result){
if(strtotime($result["CallStartTime"]) > $hourAgo){
var_dump($result);
}
}
More flexible solution:
First you need a function to convert time to seconds
function convertTimeToSeconds($hhmmss)
{
//correct format if needed
list($hours, $minutes, $seconds) = explode(':', $hhmmss);
return $hours * 3600 + $minutes * 60 + $seconds;
}
And then you just can use array_filter function
$now = date('H:i:s');
$nowSeconds = convertTimeToSeconds($now);
$filterFunction = function ($value) use ($nowSeconds) {
return ($nowSeconds - convertTimeToSeconds($value['CallStartTime'])) < 3600; //3600 seconds = hour
};
$filteredList = array_filter($array, $filterFunction);
All together https://3v4l.org/icAmp
I suppose you have this array
Array
(
[0] => Array
(
[CallRef] => 1234567892
[CallStartTime] => 21:04:20
)
[1] => Array
(
[CallRef] => 1234567892
[CallStartTime] => 09:08:08
)
[2] => Array
(
[CallRef] => 1234567892
[CallStartTime] => 08:11:08
)
[3] => Array
(
[CallRef] => 1234567892
[CallStartTime] => 20:59:08
)
)
And so i tried this
<?php
$arr = array(
"0" => array (
"CallRef" => 1234567892,
"CallStartTime" => "21:04:20"
),
"1" => array (
"CallRef" => 1234567892,
"CallStartTime" => "09:08:08"
),
"2" => array (
"CallRef" => 1234567892,
"CallStartTime" => "08:11:08"
),
"3" => array (
"CallRef" => 1234567892,
"CallStartTime" => "20:59:08"
)
);
//echo "<pre>";print_r($arr);die;
date_default_timezone_set('Asia/kolkata');//set your timezone
$currentTime = date('H:i:s');//getting current time
$lastHourTime = date('H:i:s', strtotime('-1 hour'));//getting last hour time
$result = array();
foreach ($arr as $singlearr){
if(strtotime($singlearr['CallStartTime']) >= strtotime($lastHourTime) && strtotime($singlearr['CallStartTime']) <= strtotime($currentTime)){
$result[] = $singlearr;
}else{
}
}
echo "<pre>";print_r($result);die;//this gives me last hour records only
Check Demo When you check demo All records which have time > last hour will return otherwise it will return empty array
May be it can help!
I am trying to Write Program for Calculating Next 20 dates After Specifying Start date, then from 20 dates i have Exclude Weekends & Holidays(Array holidays('2016-12-13',2016-12-24)) And Result Array which includes only Working Days Excluding Saturday & Sunday, from this Result Array after Passing Holiday array(Eg:- holidays('2016-12-13',2016-12-24))), it must be Excluded from result array. i:e;
I want Expected Output Below mentioned
.
<?php
$Date=array('2016-12-01');
echo "\n <br />Start Date:-" . $Date[0] . "";
/*Code For Generating Next 20 Dates Starts*/
//$start = strtotime($s_row['schedule_start_date']);
$start = strtotime('2016-12-01');
$dates=array();
for($i = 0; $i<20; $i++)
{
array_push($dates,date('Y-m-d', strtotime("+$i day", $start)));
}
echo "\n <br /> Array Of next 20 Days/dates of Given:-";
print_r($dates);
$start=array();
$start=$dates; /*Code For Generating Next 20 Dates Ends*/
$result=array();
$start = strtotime(array_values($Date)[0]);
//$end = strtotime(array_values($Date)[30]);
$result = array();
$begin = new DateTime( '2016-12-01' );
$end = new DateTime( '' );
//$end = $end->modify( '+1 day' );
$interval = new DateInterval('P1D');
$daterange = new DatePeriod($begin, $interval ,$end);
foreach($daterange as $date)
{
//echo $date->format("Y-m-d") . "<br>";
if (date('N', $start) <= 5) /* 'N' number days 1 (mon) to 7 (sun) */
/*5 weekday */
{
$current = date('Y-m-d', $start); //m/d/Y
$result[$current] = '';
}
$start += 86400;
//echo "Days Without Sat Sun".$result[date($date->format("Y-m-d"))];
//echo "Days Without Sat Sun".$result2[date($current->format("Y-m-d"))];
}
echo " \n <br /> Dates Without Weekends LIKE (Excluding Saturday & Sunday):-";
print_r($result);
/*For Holiday*/
$FinalArray = array();
$holidays = array(
'2016-12-13',
'2016-12-24',
);
echo " \n <br /> Given Holiday Dates Are:-";
print_r($holidays);
$a1 = $result;
$a2 = $holidays;
$array = array_diff(array_merge($a1,$a2),array_intersect($a1,$a2));
echo "\n <br /> Output:-";
print_r($array);
?>
it Gives Output as :- Array ( [2016-12-01] => [2016-12-02] => [2016-12-05] => [2016-12-06] => [2016-12-07] => [2016-12-08] => [2016-12-09] => [2016-12-12] => [2016-12-13] => [2016-12-14] => [2016-12-15] => [2016-12-16] => [2016-12-19] => [2016-12-20] => [2016-12-21] => [2016-12-22] => [2016-12-23] => [0] => 2016-12-13 [1] => 2016-12-24 )
> But I Want Expected Output:-
Array ( [2016-12-01] => [2016-12-02] => [2016-12-05] => [2016-12-06] => [2016-12-07] => [2016-12-08] => [2016-12-09] => [2016-12-12] => [2016-12-14] => [2016-12-15] => [2016-12-16] => [2016-12-19] => [2016-12-20] => [2016-12-21] => [2016-12-22] => [2016-12-23]
You Can Notice That 2016-12-13 is Not There in Above Expected Output as in '2016-12-13', 2016-12-24 is passed as Holiday via holiday array ($holidays = array( '2016-12-13', '2016-12-24', );) i:e; if i pass any date through holidays array it should not be included in result Array(). i:e 2016-12-13 is Available in Result array as well as holiday array So While while printing Final OUTPUT:- 13th date(2016-12-13) Should not be Included in final Output. Anybody Solve this will be Appreciated Thanks in Advance.
When I have to remove duplicates from a array the function that I keep going back to is
array array_unique ( array $array [, int $sort_flags = SORT_STRING ] )
you can find the documentation Here
<?php
$input = array("a" => "green", "red", "b" => "green", "blue", "red");
$result = array_unique($input);
print_r($result);
?>
the output
Array
(
[a] => green
[0] => red
[1] => blue
)
I hope that this was able to help
I prefer to calculate all dates just in one pass. (You may skip filling $dates and $dates_mon_fri arrays if they doesn't used in output also.) There is yet another approach to avoid array_diff() and array_unique() functions. I've used an array_flip() to exchange keys with values in $holdidays array to use fast array_key_exists() function.
<?php
$start = strtotime('2016-12-01');
$holidays = [
'2016-12-13',
'2016-12-24',
];
$dates = [];
$dates_mon_fri = [];
$dates_working = [];
$flip_holidays = array_flip($holidays);
for ($i = 0; $i < 20; $i++) {
$timestamp = strtotime("+$i day", $start);
$date = date('Y-m-d', $timestamp);
$dates[] = $date;
$mon_fri = false;
if (date('N', $timestamp) <= 5) {
$dates_mon_fri[] = $date;
$mon_fri = true;
}
if ($mon_fri && !array_key_exists($date, $flip_holidays)) {
$dates_working[] = $date;
}
}
var_dump($dates);
var_dump($dates_mon_fri);
var_dump($dates_working);
You can avoid using explicit looping:
$begin = new DateTimeImmutable('2016-12-01');
$end = $begin->modify('+20 days');
$interval = new DateInterval('P1D');
$daterange = new DatePeriod($begin, $interval, $end);
$allDates = iterator_to_array($daterange);
$datesExcludingWeekends = array_filter($allDates, function ($date) {
return (int) $date->format("N") < 6;
});
$datesExcludingWeekends = array_map(
'date_format',
$datesExcludingWeekends,
array_fill(1, count($datesExcludingWeekends), 'Y-m-d')
);
$holidays = [
'2016-12-13',
'2016-12-24',
];
$datesExcludingWeekendsIncludingHolidays = array_flip(array_merge(
$datesExcludingWeekends,
array_diff($holidays, $datesExcludingWeekends)
));
Here is working demo.
Also, take a look at the Carbon library. If you need some exhaustive working with dates this library can really ease your life.