Seeking logic to scan array for next active time slot - php

I'm trying to define the logic that would return a value of the next live show time per day for a media station.
The array
$liveshows = [
'mon' => [
['start' => '0600', 'end' => '0900', 'host' => 'Joe'],
['start' => '1300', 'end' => '1500', 'host' => 'Carol'],
['start' => '1500', 'end' => '1600', 'host' => 'Cortez'],
['start' => '1700', 'end' => '2100', 'host' => 'Boy George']
],
];
Process
date_default_timezone_set('America/New_York');
$day = strtolower(date('D'));
$current_time = date('Hi');
$html=''; $start=[];
if( $day == 'mon' && !empty( $liveshows['mon'] ) )
{
foreach( $liveshows['mon'] as $showtime ) {
if( $current_time >= $showtime['start'] && $current_time <= $showtime['end'] )
$html .= '<h3>' . $showtime['host'] . ' <span>is on air</span></h3>';
// create an array of start times to use in below 'no live show' notice.
$start[] = $showtime['start'];
}
}
// print it
echo $html;
All works fine to that point.
Now I want to show a notice when there is no live host on air so I've used current(), next() and end() to cycle the $start array. It works to a point but fails because the array count() is not consistent per day.
if( empty( $html ) )
{
$nextshow='';
if( $current_time < current($start) ) {
$nextshow = current($start);
}
elseif( $current_time < next($start) ) {
$nextshow = next($start);
}
echo '<h3>No live show is on</h3>';
echo '<p>The next live show is at ' . date('g:iA', strtotime( $nextshow )) . '</p>';
}
The output notice when retrieved is:
From midnight
The next live show is at 6:00AM
Between first show and next
The next live show is at 1:00PM
After the 1600 (4PM) show end time, the process fails and the output is without the 5PM value for the next in array and just...
The next live show is
How do I correctly scan the array and compare to current time to check if a show is on, and if not, show the time of the next show in line?

I think it's quite cumbersome to use current/next in your case. You can simplify your code greatly by keeping track on whether any show is on air $isOnAir = true/false (in below code) and if not, simply reiterating the shows array with a different condition.
Example:
<?php
declare(strict_types=1);
date_default_timezone_set('America/New_York');
$day = strtolower(date('D'));
$current_time = date('Hi');
// test
$day = 'mon';
$current_time = '1630';
$liveshows = [
'mon' => [
['start' => '0600', 'end' => '0900', 'host' => 'Joe'],
['start' => '1300', 'end' => '1500', 'host' => 'Carol'],
['start' => '1500', 'end' => '1600', 'host' => 'Cortez'],
['start' => '1700', 'end' => '2100', 'host' => 'Boy George'],
],
];
if ($day === 'mon' && !empty($liveshows['mon'])) {
// assume no show is on air
$isOnAir = false;
foreach ($liveshows['mon'] as $showtime) {
if ($showtime['start'] <= $current_time && $current_time <= $showtime['end']) {
// we have a show on air
$isOnAir = true;
// output
echo '<h3>', $showtime['host'], ' <span>is on air</span></h3>';
// break loop
break;
}
}
// without a show on air (see above)
if (!$isOnAir) {
echo '<h3>No live show is on</h3>';
foreach ($liveshows['mon'] as $showtime) {
// find the first where start > current
if ($current_time < $showtime['start']) {
// output
echo '<p>The next live show is at ', date('g:iA', strtotime($showtime['start'])), '</p>';
// and break loop
break;
}
}
}
}
https://3v4l.org/ZKcjo

Related

PHP returning an element from an array

So I have some code that returns all the time in an array like this Open hours today: 9:00- 9:45, 9:55 - 10:20, 10:30 - 11:00 . If we used $formatted_ranges[array_key_first($formatted_ranges)] instead of join, it would return a single element as like this, "Open hours today: 9:00 - 9:45". However we need to return like this,
Open hours today: 9:00 - 11:00.
$start = DateTime::createFromFormat( 'H:i', $range['from'] );
$end = DateTime::createFromFormat( 'H:i', $range['to'] );
$formatted_ranges = array_map( function( $range ) {
return $this->format_time( $range['from'] ).' - '.$this->format_time($range['to'] );
}, $ranges );
return sprintf(
__( 'Open hours today:', 'example' ) . ' <span>%s</span>',
join( ', ', $formatted_ranges )
);
If I understand the input data correctly, you don't need to iterate and reformat every element in the multidimensional array.
Just access the from from the first row and the to from the last row and you're done. Format just those two values if necessary.
Code: (Demo)
$ranges = [
['from' => '9:00', 'to' => '9:45'],
['from' => '9:55', 'to' => '10:20'],
['from' => '10:30', 'to' => '11:00'],
];
if (!isset($ranges[0]['from'], $ranges[0]['to'])) {
throw new Exception('insufficient business hours data');
}
printf(
'Open hours today: %s - %s',
$ranges[0]['from'],
$ranges[array_key_last($ranges)]['to']
);
Output:
Open hours today: 9:00 - 11:00
The originally shared code was not runnable. From the question, I think you want to reformat a time range array to find the beginning and the end of all the ranges.
As long as all the time are represented as 24-hour clock format, this is an example of how it can be done.
<?php
/**
* Convert multiple ranges into a single range.
*
* #param array $ranges
* #return array
*/
function overallRanges(array $ranges): array {
if (sizeof($ranges) === 0) {
throw new \Exception('The provided ranges array is empty');
}
$range = array_reduce($ranges, function ($carry, $current) {
$from = DateTime::createFromFormat('H:i', $current['from']);
$to = DateTime::createFromFormat('H:i', $current['to']);
$carry['from'] = ($from < $carry['from']) ? $from : $carry['from'];
$carry['to'] = ($to > $carry['to']) ? $to : $carry['to'];
return $carry;
},
[
'from' => DateTime::createFromFormat('H:i', $ranges[0]['from']),
'to' => DateTime::createFromFormat('H:i', $ranges[0]['to']),
]
);
return [
'from' => $range['from']->format('G:i'),
'to' => $range['to']->format('G:i'),
];
}
// example use
$ranges = [
['from' => '9:00', 'to' => '9:45'],
['from' => '9:55', 'to' => '10:20'],
['from' => '10:30', 'to' => '11:00'],
];
var_dump(overallRanges($ranges));
The output:
array(2) {
["from"]=>
string(5) "9:00"
["to"]=>
string(5) "11:00"
}
Should be a good enough start for you to reformat into anything.

Differentiate output message depending on time in array

I am trying to write a script which will display either an open or closed message depending on a businesses operating hours. I tried using the solution found here and adding to this by setting the timezone. However when I try to run this the value of $status is always 'closed' even if the time of day falls within the values in the array.
Here is my code, any advice would be appreciated. Thanks
//setting default timezone
date_default_timezone_set('Europe/Dublin');
//creating array of opening hours
$openingHours = [
'Sun' => ['12:00' => '20:30'],
'Wed' => ['16:00' => '21:00'],
'Thu' => ['16:00' => '21:00'],
'Fri' => ['16:00' => '23:00'],
'Sat' => ['16:00' => '21:00']
];
//current timestamp
$timestamp = time();
//default status
$status = 'closed';
//get time object from timestamp
$currentTime = (new DateTime())->setTimestamp($timestamp);
//loop through time range for current day
foreach ($openingHours[date('D', $timestamp)] as $startTime => $endTime) {
//create time objects from start and end times
$startTime = DateTime::createFromFormat('h:i A', $startTime);
$endTime = DateTime::createFromFormat('h:i A', $endTime);
//check if current time is within range
if (($startTime < $currentTime) && ($currentTime < $endTime)) {
$status = 'open';
break;
}//end off if
}//end off foreach
echo "we are currently :$status";
In your code the currentTime variable is an object so when you try comparing it things aren't going to match. Try this instead which simplifies things a little.
date_default_timezone_set('Europe/Dublin');
//creating array of opening hours
$openingHours = [
'Sun' => ['12:00' => '20:30'],
'Wed' => ['16:00' => '21:00'],
'Thu' => ['16:00' => '21:00'],
'Fri' => ['16:00' => '23:00'],
'Sat' => ['16:00' => '21:00']
];
//default status
$status = 'closed';
$timeNow = date('H:m',time());
//loop through time range for current day
foreach ($openingHours[date('D', time())] as $startTime => $endTime) {
//check if current time is within range
if (($startTime < $timeNow) && ($timeNow < $endTime)) {
$status = 'open';
break;
} //end off if
} //end off foreach
echo "we are currently :$status";
Edit on July 7th:
If you can change you the opening hours array slightly you may avoid multiple calls to date and the foreach loop using the code below.
date_default_timezone_set('Europe/Dublin');
//creating array of opening hours
$openingHours = [
'Sun' => ['Open'=>'12:00','Close' => '20:30'],
'Wed' => ['Open'=>'16:00','Close' => '21:00'],
'Thu' => ['Open'=>'16:00','Close' => '21:00'],
'Fri' => ['Open'=>'16:00','Close' => '23:00'],
'Sat' => ['Open'=>'16:00','Close' => '21:00']
];
$timeNow = date('H:m',time());
$dow = date('D',time());
echo 'We are currently :' . ((($openingHours[$dow]['Open'] < $timeNow) AND
($timeNow < $openingHours[$dow]['Close'])) ? 'open' : 'closed');

Reduce the code length of a work hours display

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.

Getting endless loop in PHP

Whenever i try to run this code, it seems like it gets into an endless loop. But, i cant figure out whats causing this problem. Maybe an extra eye on the thing would be able do point out the problem?
The problem only accours when there is a different year zone, example 2012-2018
Example: $this->budget_model->set_monthly_budget('1','2012', '8','2014','1');
function set_monthly_budget($start_month, $start_year, $end_month, $end_year, $budget_group_id)
{
$user_id = 2;
// Current date
$current_month = $start_month;
$current_year = $start_year;
$days_in_current_month = cal_days_in_month(CAL_GREGORIAN, $current_month, $current_year);
$company_id = 1;
$month_goal = 100;
// Check if it is the current month
if($start_year == $end_year)
{
for($x=$current_month;$x<=$end_month;$x++)
{
$data = array(
'user_id' => $user_id,
'budget_group_id' => $budget_group_id,
'month' => $x,
'year' => $start_year,
'company_id' => $company_id,
'month_goal' => $month_goal
);
// Inserting information into the database
$this->db->insert('budget_month',$data);
}
return true; // Return true if the task was completed
}
if($start_year !== $end_year)
{
$temp_start_year = $start_year;
while($temp_start_year !== $end_year)
{
// Check if we are in the same year as we started
if($temp_start_year == $current_year)
{
// Insert remaining months for this year
for($x=$current_month;$x<=12;$x++)
{
$data = array(
'user_id' => $user_id,
'budget_group_id' => $budget_group_id,
'month' => $x,
'year' => $temp_start_year,
'company_id' => $company_id,
'month_goal' => $month_goal
);
// Inserting information into the database
$this->db->insert('budget_month',$data);
}
}
// Check if the temp and end year is the same
if($temp_start_year < $end_year)
{
// Insert remaining months for this year
for($x=1;$x<=12;$x++)
{
$data = array(
'user_id' => $user_id,
'budget_group_id' => $budget_group_id,
'month' => $x,
'year' => $temp_start_year,
'company_id' => $company_id,
'month_goal' => $month_goal
);
// Inserting information into the database
$this->db->insert('budget_month',$data);
}
}
// Check if we are in the same year as we started
if($temp_start_year == $end_year)
{
// Insert remaining months for this year
for($x=1;$x<=$end_month;$x++)
{
$data = array(
'user_id' => $user_id,
'budget_group_id' => $budget_group_id,
'month' => $x,
'year' => $temp_start_year,
'company_id' => $company_id,
'month_goal' => $month_goal
);
// Inserting information into the database
$this->db->insert('budget_month',$data);
}
}
$temp_start_year++;
}
}
}
in your code
while($temp_start_year !== $end_year)
you used !== which also check if the type of the 2 variables are the same.
but this line
$temp_start_year++;
will implicitly cast the variable to integer.
Therefore the !== will be comparing integer to string, which will always evaluate to true.
The solution is as simple as using != instead of !==, or feed an integer instead of string when you call your function (remove the single quotes).

Logo change with date script

I just wondered if anybody can point me in the right direction: I'm looking to make a script whereby the logo on my site changes depending on the date; so for instance a haloween style one soon.
I started off by having 2 arrays, 1 of start dates and 1 of end dates(not sure even if this is the best way!):
<?php
$start_dates = array('01/01' => 'New Years',
'14/02' => 'Valentine Day',
'16/02/2010' => 'Pancake Day',
'17/03' => 'St Patricks Day',
'01/04' => 'April Fools',
'02/04/2010' => 'Easter',
'23/04' => 'St Georges Day',
'11/06/2010' => 'World Cup',
'31/10' => 'Halloween',
'05/11' => 'Guy Fawkes',
'11/11' => 'Armistice Day',
'16/10' => 'Today',
'15/12' => 'Christmas');
$end_dates = array( '08/01' => 'New Years',
'15/02' => 'Valentine Day',
'17/02/2010' => 'Pancake Day',
'18/03' => 'St Patricks Day',
'02/04' => 'April Fools',
'06/04/2010' => 'Easter',
'24/04' => 'St Georges Day',
'12/07/2010' => 'World Cup',
'01/11' => 'Halloween',
'06/11' => 'Guy Fawkes',
'12/11' => 'Armistice Day',
'17/10' => 'Today',
'01/01' => 'Christmas');
?>
Easy so far...the problemis that I need a way of working out if todays date falls between the start date and end date, then changing the image file name.
Its a long shot but I hope someone would be kind enough to help.
Thanks,
B.
like this
$events = array(
'New Year' => '01/01 01/08',
'Pancake Day' => '16/02/2010 17/02/2010',
//etc
);
echo find_event($events, '16/02');
where find_event() is
function mdy2time($date) {
$e = explode('/', $date);
if(count($e) < 3)
$e[] = '2010';
return strtotime("$e[1]-$e[0]-$e[2]");
}
function find_event($events, $date = null) {
$date = is_null($date) ? time() : mdy2time($date);
foreach($events as $name => $range) {
list($start, $end) = explode(' ', $range);
if($date >= mdy2time($start) && $date <= mdy2time($end))
return $name;
}
return null;
}
you should use an array more like this:
$dates = array();
$dates[] = array(
'name' => 'New Years'
'start' = '01/14',
'end' => '01/20',
'style' => 'haloween',
);
$dates[] = array(
//...
);
then you can get the style as follows:
$style='default';
// date as number e.g. 130 (january 30th)
$currDate = date('md',time()) * 1;
foreach ($dates AS $k => $v) {
$tmp = explode("/",$v['start'];
$start = ($tmp[1].$tmp[0])*1;
$tmp = explode("/",$v['end'];
$stop = ($tmp[1].$tmp[0])*1;
if ($start <= $currDate && $currDate < $stop) {
$style=$v['style'];
break;
}
}
echo 'style: '.$style;
Didn't check the code yet, so feel free to correct me if iam wrong.

Categories