Determine If Business Is Open/Closed Based On Business Hours
Asked Answered
S

8

22

My code works fine if the times are AM to PM (Ex: 11 AM - 10 PM), but if the locations hours of operation are AM to AM (Ex: 9 AM - 1 AM) it breaks. Here is my code:

$datedivide = explode(" - ", $day['hours']); //$day['hours'] Example 11:00 AM - 10:00 PM
$from = ''.$day['days'].' '.$datedivide[0].'';
$to = ''.$day['days'].' '.$datedivide[1].'';
$date = date('l g:i A');
$date = is_int($date) ? $date : strtotime($date);
$from = is_int($from) ? $from : strtotime($from);
$to = is_int($to) ? $to : strtotime($to);
if (($date > $from) && ($date < $to) && ($dateh != 'Closed')) {
    ?>
    <script type="text/javascript">
    $(document).ready(function(){
        $('.entry-title-container').append('<div class="column two"><h2 style="color:green;text-align: left;margin: 0;">OPEN<br /><span style="color:#222;font-size:12px;display: block;">Closes at <?php echo $datedivide[1]; ?></span></h2></div><br clear="all" />');
    });
    </script>
    <?php
}
Scrotum answered 15/2, 2013 at 23:5 Comment(6)
I'm going to play around with this, thanks :)Scrotum
What are your business days and business hours for each day?Narvik
Monday:9:00 AM - 12:00 AM Tuesday:9:00 AM - 12:00 AM Wednesday:9:00 AM - 12:00 AM Thursday:9:00 AM - 12:00 AM Friday:9:00 AM - 1:00 AM Saturday:9:00 AM - 1:00 AM Sunday:9:00 AM - 12:00 AMScrotum
Can you confirm that Sunday and Friday times are indeed correct, I just find it odd that they are list as those times, it looks to me that they should be swapped. Just want to confirm.Narvik
Yes, they are correct. sorry I was busy yesterday (and today lol). And thank you for offering to write the logic. PHP isn't my strongest point, however I'm picking it up pretty fast.Scrotum
There might be some useful bits here too #34921302Estebanesteem
N
51

You would first need to create an array which will hold your days of the week, and their respective close/open time range(s).

/**
 * I setup the hours for each day if they carry-over)
 * everyday is open from 09:00 AM - 12:00 AM
 * Sun/Sat open extra from 12:00 AM - 01:00 AM
 */
$storeSchedule = [
    'Sun' => ['12:00 AM' => '01:00 AM', '09:00 AM' => '12:00 AM'],
    'Mon' => ['09:00 AM' => '12:00 AM'],
    'Tue' => ['09:00 AM' => '12:00 AM'],
    'Wed' => ['09:00 AM' => '12:00 AM'],
    'Thu' => ['09:00 AM' => '12:00 AM'],
    'Fri' => ['09:00 AM' => '12:00 AM'],
    'Sat' => ['12:00 AM' => '01:00 AM', '09:00 AM' => '12:00 AM']
];

You then loop over the current day's time range(s) and check to see if the current time or supplied timestamp is within a range. You do this by using the DateTime class to generate a DateTime object for each time range's start/end time.

The below will do this and allow you to specify a timestamp in case you are wanting to check a supplied timestamp instead of the current time.

// current or user supplied UNIX timestamp
$timestamp = time();

// default status
$status = 'closed';

// 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('h:i A', $startTime);
    $endTime   = DateTime::createFromFormat('h:i A', $endTime);

    // check if current time is within a range
    if (($startTime < $currentTime) && ($currentTime < $endTime)) {
        $status = 'open';
        break;
    }
}

echo "We are currently: $status";

See DEMO of above

Narvik answered 18/2, 2013 at 2:59 Comment(6)
+ Nice but i would have preferred if you used DateTimeHuman
Only downside with this is that the opening hours are hard-coded.Stunk
how to store and fetch data to get this type of array?Rosel
It is recommended to set the current time zone, e.g. date_default_timezone_set('Europe/Berlin'); . Otherwise there is a risk that the time provided by DateTime is not correct, e.g. -2 h.Gilstrap
@Gilstrap you should be passing the timezone to DateTime, not changing the default timezone. It's best to use UTC for all dates/times and only supply a timezone in the instances you need the UTC value converted to a local time.Narvik
How could you make the opening times not hard-coded? I want to be able to change the times on the fly as well as dynamically add exceptions, how would I be able to do this? (I mean in terms of storage, as the array would always go back to the "default" state which is not desirable)Angeliqueangelis
V
6

Modified from the accepted answer for use on a AWS Debian Server (located on the west coast) where our store hours are actually EST... also dropped into a PHP function.

/*
 * decide based upon current EST if the store is open
 *
 * @return bool
 */
function storeIsOpen() {
    $status = FALSE;
    $storeSchedule = [
        'Mon' => ['08:00 AM' => '05:00 PM'],
        'Tue' => ['08:00 AM' => '05:00 PM'],
        'Wed' => ['08:00 AM' => '05:00 PM'],
        'Thu' => ['08:00 AM' => '05:00 PM'],
        'Fri' => ['08:00 AM' => '05:00 PM']
    ];

    //get current East Coast US time
    $timeObject = new DateTime('America/New_York');
    $timestamp = $timeObject->getTimeStamp();
    $currentTime = $timeObject->setTimestamp($timestamp)->format('H:i A');

    // loop through time ranges for current day
    foreach ($storeSchedule[date('D', $timestamp)] as $startTime => $endTime) {

        // create time objects from start/end times and format as string (24hr AM/PM)
        $startTime = DateTime::createFromFormat('h:i A', $startTime)->format('H:i A');
        $endTime = DateTime::createFromFormat('h:i A', $endTime)->format('H:i A');

        // check if current time is within the range
        if (($startTime < $currentTime) && ($currentTime < $endTime)) {
            $status = TRUE;
            break;
        }
    }
    return $status;
}
Vannoy answered 6/12, 2016 at 18:34 Comment(4)
how to fetch data as this kind of array?Rosel
Love this solution, but how do I make this the Europe (24-hours) way?Subsidize
@Loosie94, you would adjust the format in each location to use the format you need for comparison (the 'h:i A') note you will also need to update the store schedule array to 24hr time.... The array times get converted to objects for comparision in the foreach loop so these formats have to match. Then just update the DateTime parameter assigned to $timeObject variable to your timezone. Reference for DateTime->fomat() Hope this helps.Vannoy
@sdforet, I figured the only thing I had to do was (indeed) changing the times to 24-hours, change the lowecase h in $startTime and $endTime to uppercase and remove the A in all H:i A formats. Working good now, thanks!Subsidize
S
3

You should regroup all opening hours in a array since the openings hours of yesterday could be of influence if you stay opened after midnight. Also having the possibility to have several opening hours per day might be handy.

<?php

$times = array(
    'mon' => '9:00 AM - 12:00 AM',
    'tue' => '9:00 AM - 12:00 AM',
    'wed' => '9:00 AM - 12:00 AM',
    'thu' => '9:00 AM - 12:00 AM',
    'fri' => '9:00 AM - 1:00 AM',
    'sat' => '9:00 AM - 1:00 PM, 2:00 PM - 1:00 AM',
    'sun' => 'closed'
);

function compileHours($times, $timestamp) {
    $times = $times[strtolower(date('D',$timestamp))];
    if(!strpos($times, '-')) return array();
    $hours = explode(",", $times);
    $hours = array_map('explode', array_pad(array(),count($hours),'-'), $hours);
    $hours = array_map('array_map', array_pad(array(),count($hours),'strtotime'), $hours, array_pad(array(),count($hours),array_pad(array(),2,$timestamp)));
    end($hours);
    if ($hours[key($hours)][0] > $hours[key($hours)][1]) $hours[key($hours)][1] = strtotime('+1 day', $hours[key($hours)][1]);
    return $hours;
}

function isOpen($now, $times) {
    $open = 0; // time until closing in seconds or 0 if closed
    // merge opening hours of today and the day before
    $hours = array_merge(compileHours($times, strtotime('yesterday',$now)),compileHours($times, $now)); 

    foreach ($hours as $h) {
        if ($now >= $h[0] and $now < $h[1]) {
            $open = $h[1] - $now;
            return $open;
        } 
    }
    return $open;
}

$now = strtotime('7:59pm');
$open = isOpen($now, $times);

if ($open == 0) {
    echo "Is closed";
} else {
    echo "Is open. Will close in ".ceil($open/60)." minutes";
}

?>

On the other hand if you only want to resolve the problem with time like 9am - 5am you should check if $from > $to and add 1 day to $to if necessary.

Stunk answered 24/11, 2013 at 13:11 Comment(0)
K
1

Maybe this? I found it here and tweaked it a bit.

    <?php
    date_default_timezone_set('Europe/Stockholm'); // timezone 
    $today = date(N); // today ex. 6

    //Opening hours
    $opening_hours[1] = "12:00"; $closing_hours[1] = "00:00";
    $opening_hours[2] = "12:00"; $closing_hours[2] = "00:00";
    $opening_hours[3] = "12:00"; $closing_hours[3] = "00:00";
    $opening_hours[4] = "12:00"; $closing_hours[4] = "02:00";
    $opening_hours[5] = "12:00"; $closing_hours[5] = "04:00";
    $opening_hours[6] = "13:00"; $closing_hours[6] = "04:00";
    $opening_hours[7] = "13:00"; $closing_hours[7] = "00:00";

    // correction for opening hours after midnight.
    if(intval($closing_hours[$today - 1]) > 0)
        $today = date(N,strtotime("-".intval($closing_hours[$today - 1])." hours"));

    // now check if the current time is after openingstime AND smaller than closing hours of tomorrow
            if (

    (date("H:i") > $opening_hours[$today] && 
    ((date("Y-m-d H:i") < date("Y-m-d H:i", strtotime('tomorrow +'.intval($closing_hours[$today]).' hours')))) 
    ) ||
    date("H:i") < $closing_hours[$today] && 
    ((date("Y-m-d H:i") < date("Y-m-d H:i", strtotime('today +'.intval($opening_hours[$today]).' hours'))))

    )  {
        $label = "label-success";   
        $label_text = "OPEN";
        $label_time = $closing_hours[$today];
    } else {
        $label = "label-danger";
        $label_text = "GESLOTEN";
        $label_time = $opening_hours[$today];
    }
    ?>
Knisley answered 27/2, 2016 at 17:13 Comment(0)
I
1

A bit late but I have a solution for others, if the one's here don't quite fit their needs. I do like the solution of having multiple time sets for days that close after midnight but then this would add extra data handling to display the hours. You would first have to check if there are multiple time sets available then confirm that they are connected (no time in between).

My solution was instead to write a function that you pass the opening time, closing time, and time-in-question and it will return true for open and false for closed:

function is_open($open, $close, $query_time){
    $open = new DateTime($open);
    $close = new DateTime($close);
    $query_time = new DateTime($query_time);
    $is_open = false;
    //Determine if open time is before close time in a 24 hour period
    if($open < $close){
        //If so, if the query time is between open and closed, it is open
        if($query_time > $open){
            if($query_time < $close){
                $is_open = true;
            }
        }
    }
    elseif($open > $close){
        $is_open = true;
        //If not, if the query time is between close and open, it is closed
        if($query_time < $open){
            if($query_time > $close){
                $is_open = false;
            }
        }
    }
    return $is_open;
}
Incase answered 29/8, 2017 at 17:28 Comment(0)
A
1

Here is my version since the 12hr time format does not work for me. Note that $pancits[] came from my database

$status = 'Closed';
// get current time object
$timestamp = time();
// $currentTime = (new DateTime())->setTimestamp($timestamp);
$currentTime = date('H:i');

$startTime = date('H:i', strtotime($pancits['p_oTime']));
$endTime   = date('H:i', strtotime($pancits['p_cTime']));

if (($startTime <= $currentTime) && ($currentTime <= $endTime)) {
    $status = 'Open';
    //echo $status;
}

echo "<b>Currently $status</b>";

echo '
    <p><b>Time Open: </b>'.date('h:ia', strtotime($pancits['p_oTime'])).'</p>
    <p><b>Time Close: </b>'.date('h:ia', strtotime($pancits['p_cTime'])).'</p>
';
Aldos answered 14/9, 2018 at 7:44 Comment(0)
I
0

I tried this code for a day several different ways and found this to be a solution for me. Posting it in case it might help others. It is important to convert the time from AM/PM format back to UNIX time.

I use times pulled from a database so you would have to define $startTime and $endTime.

//  define each days time like so
$monsta = "07:00 AM";
$monend = "05:00 PM";

$storeSchedule = [
    'Mon' => ["$monsta" => "$monend"],
    'Tue' => ["$tuessta" => "$tuesend"],
    'Wed' => ["$wedssta" => "$wedsend"],
    'Thu' => ["$thurssta" => "$thursend"],
    'Fri' => ["$frista" => "$friend"],
    'Sat' => ["$satsta" => "$satend"],
    'Sun' => ["$sunsta" => "$sunend"]
];

// default time zone
$timestamp = time() - 18000; 

// default status
$status = 'Closed';

// loop through time ranges for current day
foreach ($storeSchedule[date('D', $timestamp)] as $startTime => $endTime) {

// create time objects from start/end times
$startTime = strtotime($startTime);
$endTime = strtotime($endTime);

// check if current time is within a range
if ($startTime <= $timestamp && $timestamp <= $endTime ) {
    $status = 'Open';

    }
}   

echo '<p><b>Today\'s Hours</b><br />'.$days[$d].'<br />';
echo "<b>We are currently: $status </b></p>";
Id answered 29/11, 2016 at 19:44 Comment(4)
Care to elaborate?Dreyer
First define $startTime and $endTime in the arrayId
$monsta = "07:00 AM"Id
Can you edit that into the answer rather than posting it in the comments?Dreyer
K
-1

I would suggest making use of PHP's DateTime class. It was developed to solve all these problems by giving a simple interface for hard to accomplish tasks like this.

Kimberlite answered 16/2, 2013 at 0:15 Comment(1)
Could you give me an example on how I can integrate this to my script?Scrotum

© 2022 - 2024 — McMap. All rights reserved.