I'd like to define a set of opening hours for a 7 day week. When a visitor visits the web page I'd like to display the current time and day in a specific country (Nevis, Caribbean) and then compare it with a defined set of opening times to show one of two captions 1) Open 2) Closed. Specifically this is what I want to produce:
I'm using this so far to get the current time and set up the array but how do I compare the two?
<?php
function get_timee($country,$city) {
$country = str_replace(' ', '', $country);
$city = str_replace(' ', '', $city);
$geocode_stats = file_get_contents("http://maps.googleapis.com/maps/api/geocode/json?address=$city+$country,&sensor=false");
$output_deals = json_decode($geocode_stats);
$latLng = $output_deals->results[0]->geometry->location;
$lat = $latLng->lat;
$lng = $latLng->lng;
$google_time = file_get_contents("https://maps.googleapis.com/maps/api/timezone/json?location=$lat,$lng×tamp=1331161200&key=xxx");
$timez = json_decode($google_time);
$d = new DateTime("now", new DateTimeZone($timez->timeZoneId));
return $d->format('H:i');
}
$array = array(
"Monday" => "10:00 - 18:00",
"Tuesday" => "10:00 - 18:00",
"Wednesday" => "10:00 - 18:00",
"Thursday" => "10:00 - 18:00",
"Friday" => "18:00 - 23:00",
"Saturday" => "18:00 - 23:00",
"Sunday" => "Closed"
);
?>
Its <?php echo get_timee("Federation of Saint Kitts and Nevis","Nevis"); ?>. We're currently ...
You can do all this without having to use google anything. I assume you were doing that so that you can work out the time at the clients location. Or maybe because you were not sure what the timezone on your server was set to, or you know it was set to the wrong timezone for you location.
But you can do all this using the core PHP DateTime() and DateTimeZone() classes like this.
Note: I changed the Opening/Closing times array to make it easier to process!
<?php
$opening_times = array(
"Monday" => array('open' => '10:00', 'close' => '18:00'),
"Tuesday" => array('open' => '10:00', 'close' => '18:00'),
"Wednesday" => array('open' => '10:00', 'close' => '18:00'),
"Thursday" => array('open' => '10:00', 'close' => '18:00'),
"Friday" => array('open' => '18:00', 'close' => '23:00'),
"Saturday" => array('open' => '18:00', 'close' => '23:00'),
"Sunday" => "Closed"
);
function AreWeOpen($were_open, $date)
{
$htm = '';
if ( ! is_array($were_open[$date->format('l')])
&& strtolower($were_open[$date->format('l')]) == 'closed' )
{
$htm = 'We are closed all day Sunday';
} else {
if ( $date->format('H:i') >= $were_open[$date->format('l')]['open']
&& $date->format('H:i') <= $were_open[$date->format('l')]['close']
)
{
$htm = 'We are open';
} else {
$htm = 'We are closed';
}
}
return $htm;
}
Now to run/test the above all you do is:
// set date time to NOW
$date = new DateTime(null, new DateTimeZone('America/St_Kitts'));
echo 'Are we open now? Now is ' . $date->format('l H:i') . ' >';
echo AreWeOpen($opening_times, $date);
echo PHP_EOL;
echo 'Are we open at 09:59 Monday > ';
$date = new DateTime('2015/08/17 09:59:00', new DateTimeZone('America/St_Kitts'));
echo AreWeOpen($opening_times, $date);
echo PHP_EOL;
echo 'Are we open at 10:00 Monday > ';
$date = new DateTime('2015/08/17 10:00:00', new DateTimeZone('America/St_Kitts'));
echo AreWeOpen($opening_times, $date);
echo PHP_EOL;
echo 'Are we open at 18:00 Monday > ';
$date = new DateTime('2015/08/18 18:00:00', new DateTimeZone('America/St_Kitts'));
echo AreWeOpen($opening_times, $date);
echo PHP_EOL;
echo 'Are we open at 18:01 Monday > ';
$date = new DateTime('2015/08/18 18:01:00', new DateTimeZone('America/St_Kitts'));
echo AreWeOpen($opening_times, $date);
echo PHP_EOL;
echo 'Are we open at 18:01 Friday > ';
$date = new DateTime('2015/08/21 18:01:00', new DateTimeZone('America/St_Kitts'));
echo AreWeOpen($opening_times, $date);
echo PHP_EOL;
echo 'Are we open on SUNDAY > ';
$date = new DateTime('2015/08/16 18:01:00', new DateTimeZone('America/St_Kitts'));
echo AreWeOpen($opening_times, $date);
And that gives these results:
Are we open now? Now is Monday 07:38 >We are closed
Are we open at 09:59 Monday > We are closed
Are we open at 10:00 Monday > We are open
Are we open at 18:00 Monday > We are open
Are we open at 18:01 Monday > We are closed
Are we open at 18:01 Friday > We are open
Are we open on SUNDAY > We are closed all day Sunday
On outline of a solution:
Calculate a numerical version of your opening times array. This array would be a mapping of day of week to opening hours, indexing the days in the same time DateTime->Format('w') would, starting with 0 for Sunday, etc. Each entry could be null for no opening hours, or an array containing the number of seconds into the day at which the store opens.
Change get_timee to return the DateTime object iteself.
Determine whether the calculated DateTime object falls within the range for the current date. In particular, use (int)$dateTime->format('w') to get the index, then look that up in your numerical array. If the entry is not null, perform the "seconds" calculation for the dateTime item, and determine whether it falls within the range.
Related
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
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');
I have a date range and I need it to group by month but I want to keep starting day and ending day. So far I have this:
$interval['from'] = '2017-01-02 00:00:00';
$interval['to'] = '2017-02-06 23:59:59';
$start = Carbon::createFromFormat('Y-m-d H:i:s', $interval['from'])->startOfMonth();
$end = Carbon::createFromFormat('Y-m-d H:i:s', $interval['to'])->startOfMonth()->addMonth();
$separate = CarbonInterval::month();
$period = new \DatePeriod($start, $separate, $end);
foreach ($period as $dt) {
dump($dt);
}
But as result I'm getting:
Carbon\Carbon(3) {
date => "2017-01-01 00:00:00.000000" (26)
timezone_type => 3
timezone => "Europe/Prague" (13)
}
Carbon\Carbon(3) {
date => "2017-02-01 00:00:00.000000" (26)
timezone_type => 3
timezone => "Europe/Prague" (13)
}
It is grouped by month but I need to get whole month period, I mean from
2017-01-02 00:00:00 to 2017-01-31 23:59:59
2017-02-01 00:00:00 to 2017-02-06 23:59:59.
Output:
$array = [
0 => [
'from' => '2017-01-02 00:00:00',
'to' => '2017-01-31 23:59:59'
],
1 => [
'from' => '2017-02-01 00:00:00',
'to' => '2017-02-06 23:59:59'
]
];
What is the easiest way to achive it?
Edit: Here is a little bit modified Carbon version of accepted answer, maybe somebody will need it:
$interval['from'] = '2017-01-02 00:00:00';
$interval['to'] = '2017-04-08 23:59:59';
$interval_from = Carbon::createFromFormat('Y-m-d H:i:s', $interval['from']);
$interval_to = Carbon::createFromFormat('Y-m-d H:i:s', $interval['to']);
$result = [];
foreach (range($interval_from->month, $interval_to->month) as $x) {
$to = $interval_from->copy()->endOfMonth();
if ($x == $interval_to->month) {
$result[] = ["from" => $interval_from, "to" => $interval_to];
} else {
$result[] = ["from" => $interval_from, "to" => $to];
}
$interval_from = $to->copy()->addSecond();
}
PHP code demo
Try this solution you can change from one month to another month(not year) and then check. Lengthy solution but hopefully works correctly, putting explaination.
<?php
ini_set('display_errors', 1);
$from=$interval['from'] = '2017-01-02 00:00:00';
$interval['to'] = '2017-03-07 23:59:59';
$month1=date("m", strtotime($interval['from']));
$month2=date("m", strtotime($interval['to']));
$result=array();
foreach(range($month1, $month2) as $x)
{
$dateTimeObj= new DateTime($from);
$dayDifference=($dateTimeObj->format('d')-1);
$dateTimeObj= new DateTime($from);
$dateTimeObj->add(new DateInterval("P1M"));
$dateTimeObj->sub(new DateInterval("P".$dayDifference."DT1S"));
$to= $dateTimeObj->format("Y-m-d H:i:s");
if($x==$month2)
{
$dateTimeObj= new DateTime($interval['to']);
$noOfDays=$dateTimeObj->format("d");
$dateTimeObj->sub(new DateInterval("P".($noOfDays-1)."D"));
$from=$dateTimeObj->format("Y-m-d H:i:s");
$result[]=array("from"=>$from,"to"=>$interval['to']);
}
else
{
$result[]=array("from"=>$from,"to"=>$to);
}
//adding 1 second to $to for next time to be treated as $from
$dateTimeObj= new DateTime($to);
$dateTimeObj->add(new DateInterval("PT1S"));
$from= $dateTimeObj->format("Y-m-d H:i:s");
}
print_r($result);
I have a program to get the start date and end date of a week when passing year and week number. Following is the code.
function getStartAndEndDate($week, $year) {
$dto = new DateTime();
$dto->setISODate($year, $week,0);
$ret['week_start'] = $dto->format('Y-m-d');
$dto->modify('+6 days');
$ret['week_end'] = $dto->format('Y-m-d');
return $ret;
}
$week_array = getStartAndEndDate(5,2017);
print_r($week_array);
This will output result as
Array ( [week_start] => 2017-01-29 [week_end] => 2017-02-04 )
But I need to get week in two part. For eg the result should be as follows
Array ( [week_start] => 2017-01-29 [week_end] => 2017-01-31)
Array ( [week_start] => 2017-02-01 [week_end] => 2017-02-04)
which means I need separate values for different months. Any help will be appreciated.
You compare two date by month and use 't' format to get the last day of the month later. Follow the description of date() function to learn more about all available formats and the example below.
Also it's better to use DateTimeImmutable instead of DateTime unless you really need the mutable version.
function getStartAndEndDate($week, $year)
{
$dto = (new DateTimeImmutable())->setISODate($year, $week, 0);
$weekStart = $dto;
$weekEnd = $dto->modify('+6 days');
$ret = [];
if ($weekStart->format('m') === $weekEnd->format('m')) {
$ret[] = [
'week_start' => $weekStart,
'week_end' => $weekEnd,
];
} else {
$ret[] = [
'week_start' => $weekStart->format('Y-m-d'),
// 't' = the last day of the month.
'week_end' => $weekStart->format('Y-m-t'),
];
$ret[] = [
// The first day of the month.
'week_start' => $weekEnd->format('Y-m-01'),
'week_end' => $weekEnd->format('Y-m-d'),
];
}
return $ret;
}
I'm looping through an array of days in the current month to generate another array of days that are on or after the current day. I'm also doing the same for the next month (which will always include all days as they are after the current date).
The complexity is when the next month is in a different year to the current month. The format of the final array is like this:
array("year" => array("month" => array(days)));
When both months are in the same year it might look like this:
$allDays = array("2013" => array( "11" => array(28,29,30), "12" => array(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31)));
When the 2 months are in different years (i.e. Dec and Jan) it might look like this:
$allDays = array("2013" => array("12" => array(2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31)), "2014" => array("1" => array(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31) )) ;
Here's my code that generates the list of dates for the current month and the next month:
// Set the default timezone
date_default_timezone_set('Australia/Sydney');
// Get days for current month
$day = date("Y-m-d");
$i = strtotime($day);
array("year" => array("month" => array(days)));
$linked_days = array(
date('Y', $i) => array(
date('m') => range(date('d', $i), intval(date('t'))),
),
);
// Get days for next month
$day2 = date("Y-m-d", strtotime('first day of next month')) ;
$i2 = strtotime($day2);
array("year" => array("month" => array(days)));
$linked_days2 = array(
date('Y', $i2) => array(
date('m') => range(date('d', $i2), intval(date('t'))),
),
);
I'm not sure how to go about combining them into the 1 array with a different sytanx if they are in the same year or not?
You can check if there is already an entry for the year in your array with isset function :
Change this
$day2 = date("Y-m-d", strtotime('first day of next month')) ;
$i2 = strtotime($day2);
array("year" => array("month" => array(days)));
$linked_days2 = array(
date('Y', $i2) => array(
date('m') => range(date('d', $i2), intval(date('t'))),
),
);
To
$day2 = date("Y-m-d", strtotime('first day of next month')) ;
$i2 = strtotime($day2);
array("year" => array("month" => array(days))); //useless line ??
if(!isset($linked_days[date('Y', $i2)])){
//if no entry for this year in array, create new entry
$linked_days[date('Y', $i2)] = array(date('m') => range(date('d', $i), intval(date('t'))));
}
else{
//else, just add the month entry
$linked_days[date('Y', $i2)][date('m')] = range(date('d', $i2), intval(date('t'))) ;
}