skip to Main Content

I’m working on a PHP project where I need to calculate the opening times for a business, which includes both regular weekly schedules and special non-standard times for holidays. I have two arrays: one for the regular schedule and another for the non-standard times.

Regular opening times array:

$opening_times = [
    ['open' => '09:00:00', 'weekday' => 1], // Monday
    ['open' => '09:00:00', 'weekday' => 2], // Tuesday
    ['open' => '09:00:00', 'weekday' => 4], // Thursday, Wednesday is closed
    ['open' => '09:00:00', 'weekday' => 5], // Friday opening time 1
    ['open' => '16:00:00', 'weekday' => 5], // Friday opening time 2
    ['open' => '09:00:00', 'weekday' => 6], // Saturday
    ['open' => '09:00:00', 'weekday' => 7], // Sunday
];

Non-standard opening times array (for holidays etc.):

$special_opening_times = [
    ['date' => '2024-04-24', 'open' => '08:00:00'],
    ['date' => '2024-04-27', 'open' => '08:00:00'],
    ['date' => '2024-04-28', 'open' => '09:00:00'],
    ['date' => '2024-04-30', 'open' => '07:00:00'],
    ['date' => '2024-04-25', 'open' => '11:00:00']
];

Using some inspiration from this question I was able to craft this for the non-standard opening times array:

// Your reference datetime
$reference_datetime = '2024-04-25 10:30:00';

// Array with dates and times
$special_opening_times = [
    ['date' => '2024-04-24', 'open' => '08:00:00'],
    ['date' => '2024-04-27', 'open' => '08:00:00'],
    ['date' => '2024-04-28', 'open' => '09:00:00'],
    ['date' => '2024-04-30', 'open' => '07:00:00'],
    ['date' => '2024-04-25', 'open' => '11:00:00'] // Same day, later time
];

// Function to sort datetimes
function datetime_sort($a, $b) {
    $a_datetime = strtotime($a['date'] . ' ' . $a['open']);
    $b_datetime = strtotime($b['date'] . ' ' . $b['open']);
    return $a_datetime - $b_datetime;
}

// Sort datetimes using the custom function
usort($special_opening_times, 'datetime_sort');

// Variable to hold the next closest datetime
$next_datetime = null;

// Loop through sorted datetimes and find the next closest datetime
foreach ($special_opening_times as $date_array) {
    $current_datetime = strtotime($date_array['date'] . ' ' . $date_array['open']);
    if (strtotime($reference_datetime) < $current_datetime) {
        $next_datetime = $date_array['date'] . ' ' . $date_array['open'];
        break;
    }
}

// Output the next closest datetime
echo $next_datetime;

Which seems to work as expected, however I still need to find the next closest opening date and time from the weekly opening hours, and I’m at a total loss on how to do it. Could anyone shed some light. Thanks.

2

Answers


  1. So given a date and time, you want the date and time of next opening hours.
    I’ll assume that a special date overrides a regular date.

    First we sort by date and time, both input arrays.
    Next given a reference date and time, we find if it falls on a special date, and get the earliest possible time for this day.

    If there is no special date on that date, we will look for same weekday on regular opening hours.

    If still not found (like on Wednesday), we just try the next day.

    
    function getNextOpening($opening_times, $special_opening_times, $reference_datetime)
    {
    
        // day of week of $reference_datetime
        $reference_weekday = date('w', strtotime($reference_datetime));
        if ($reference_weekday === 0) {
            $reference_weekday = 7;
        }
    
        // time of day of $reference_datetime
        $reference_time = date('H:i:s', strtotime($reference_datetime));
    
        // date of $reference_datetime
        $reference_date = date('Y-m-d', strtotime($reference_datetime));
    
    
        // sort $opening_times by weekday and open time
        usort($opening_times, function ($a, $b) {
            if ($a['weekday'] == $b['weekday']) {
                return strcmp($a['open'], $b['open']);
            }
            return $a['weekday'] - $b['weekday'];
        });
    
        // sort $special_opening_times by date and open time
        usort($special_opening_times, function ($a, $b) {
            if (strtotime($a['date']) == strtotime($b['date'])) {
                return strcmp($a['open'], $b['open']);
            }
            return strtotime($a['date']) - strtotime($b['date']);
        });
    
        // find next special date and opening times for SAME date of $reference_datetime
        foreach ($special_opening_times as $special_opening) {
            if ($special_opening['date'] == $reference_date) {
                if (strtotime($special_opening['open']) > strtotime($reference_time)) {
                    return $special_opening['date'] . ' ' . $special_opening['open'];
                }
            }
        }
    
        // no special day, find by regular opening times
        foreach ($opening_times as $opening) {
            if ($opening['weekday'] == $reference_weekday) {
                if ($opening['open'] > $reference_time) {
                    return $reference_date. " ". $opening['open'];
                }
            }
        }
    
        // add 1 day to $reference_date 
        $reference_date = date('Y-m-d', strtotime("+1 day", strtotime($reference_date)));
        return getNextOpening($opening_times, $special_opening_times, $reference_date);
    
    }
    
    
    $opening_times = [
        ['open' => '09:00:00', 'weekday' => 1], // Monday
        ['open' => '09:00:00', 'weekday' => 2], // Tuesday
        ['open' => '09:00:00', 'weekday' => 4], // Thursday, Wednesday is closed
        ['open' => '16:00:00', 'weekday' => 5], // Friday opening time 2
        ['open' => '09:00:00', 'weekday' => 5], // Friday opening time 1
        ['open' => '09:00:00', 'weekday' => 6], // Saturday
        ['open' => '09:00:00', 'weekday' => 7], // Sunday
    ];
    
    $special_opening_times = [
        ['date' => '2024-04-24', 'open' => '08:00:00'],
        ['date' => '2024-04-27', 'open' => '08:00:00'],
        ['date' => '2024-04-28', 'open' => '09:00:00'],
        ['date' => '2024-04-30', 'open' => '07:00:00'],
        ['date' => '2024-04-25', 'open' => '11:00:00']
    ];
    
    echo getNextOpening($opening_times, $special_opening_times, '2024-04-25 10:30:00') . "n";      // 2024-04-25 11:00:00
    echo getNextOpening($opening_times, $special_opening_times, '2024-04-26 10:30:00') . "n";      // 2024-04-26 16:00:00
    echo getNextOpening($opening_times, $special_opening_times, '2024-04-24 07:30:00') . "n";      // 2024-04-24 08:00:00
    echo getNextOpening($opening_times, $special_opening_times, '2024-04-24 08:30:00') . "n";      // 2024-04-25 11:00:00
    
    Login or Signup to reply.
  2. Feel free to modify the script below I’m assuming the following rules here:

    • When the reference Date Time is lower or equal to the matching day opening time it returns the Reference Date opening time.
    • When the Reference Date Time is Greater than the current date opening time, it returns the next Date Opening Time
    • Special Days take precedence over Regular days
    • My script only support one Opening time per day.

    Quick Note on this code: I’ve changed Sunday code to 0 as this is the PHP Default value for Sunday, if you cannot change that in your input, you might need to have a translator from PHP Date numbers to your own numeric map.

    The following script can get you some clues on how you can build this.
    Still, this script Does not support Multiple Opening times in the same date, I’ll let you figure that part 😀

    $regularDaysOpeningTime = [
        ['open' => '09:00:00', 'weekday' => 0], // Sunday
        ['open' => '09:00:00', 'weekday' => 1], // Monday
        ['open' => '09:00:00', 'weekday' => 2], // Tuesday
        ['open' => '09:00:00', 'weekday' => 4], // Thursday, Wednesday is closed
        ['open' => '09:00:00', 'weekday' => 5], // Friday opening time 1
        ['open' => '16:00:00', 'weekday' => 5], // Friday opening time 2
        ['open' => '09:00:00', 'weekday' => 6], // Saturday
    ];
    
    $specialDaysOpeningTime = [
        ['date' => '2024-04-24', 'open' => '08:00:00'],
        ['date' => '2024-04-27', 'open' => '08:00:00'],
        ['date' => '2024-04-28', 'open' => '09:00:00'],
        ['date' => '2024-04-30', 'open' => '07:00:00'],
        ['date' => '2024-04-25', 'open' => '11:00:00']
    ];
    
    $referenceDate = '2024-04-25 12:30:00';
    
    
    function getNextOpeningDate($referenceDate, $regularDaysOpeningTime, $specialDaysOpeningTime) {
        $referenceDateOnly = date('Y-m-d', strtotime($referenceDate));
        $matchingSpecialDay = findSpecialDate($referenceDateOnly, $specialDaysOpeningTime);
        
        // When Input matches a special day
        if ($matchingSpecialDay !== false) {
            return getDateOpeningTimeOrNextDayOpeningTime(
                $referenceDate, 
                $matchingSpecialDay['date'], 
                $matchingSpecialDay['open'],
                $regularDaysOpeningTime,
                $specialDaysOpeningTime
            );
        } 
    
        // Otherwise
        $openingTime = getRegularDayOpeningTimeFromDate($referenceDate, $regularDaysOpeningTime);
        $referenceDateWithoutTime = date('Y-m-d', $timestamp);
    
        return getDateOpeningTimeOrNextDayOpeningTime(
            $referenceDate, 
            $referenceDateWithoutTime, 
            $openingTime,
            $regularDaysOpeningTime,
            $specialDaysOpeningTime
        );
    }
    
    function findSpecialDate($date, $specialDaysOpeningTime) {
        foreach ($specialDaysOpeningTime as $specialDate) {
            if ($specialDate['date'] === $date) {
                return $specialDate;
            }
        }
    }
    
    function getRegularDayOpeningTimeFromDate($date, $regularDaysOpeningTime) {
        $weekdayNumber = date('w', strtotime($date));
        foreach ($regularDaysOpeningTime as $regularDay) {
            if ($regularDay['weekday'] == $weekdayNumber) {
                return $regularDay['open'];
            }
        }
    }
    
    function getDateOpeningTimeOrNextDayOpeningTime(
        $referenceDate, 
        $candidateDate, 
        $candidateDateOpeningTime,
        $regularDaysOpeningTime,
        $specialDaysOpeningTime
    ) {
        $referenceDateTimestamp = strtotime($referenceDate);
        $openingDatCandidate = strtotime($candidateDate . ' ' . $candidateDateOpeningTime);
    
        // When Initial Provided date is not already "late", return opening time. 
        if ($referenceDateTimestamp <= $openingDatCandidate) {
            return $candidateDateOpeningTime;
        }
    
        // Otherwise, Get Next Day Date 
        $nextDateTimestamp = strtotime('+1 day', $referenceDateTimestamp);
        $nextDate = date('Y-m-d', $nextDateTimestamp);
    
        // Them, Find out if the next day is a Special Day
        $specialDay = findSpecialDate($nextDate, $specialDaysOpeningTime);
        if ($specialDay) {
            return $specialDay['open'];
        }
    
        // When not a special Day find the next weekday and return first opening time
        return getRegularDayOpeningTimeFromDate($nextDate, $regularDaysOpeningTime);
    }
    
    
    
    print_r(
        getNextOpeningDate($referenceDate, $regularDaysOpeningTime, $specialDaysOpeningTime)
    )
    
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search