You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用PHP简便列出早8点至次日凌晨4点的可用预订时间区间

如何简便计算跨天营业时间内的可用预订区间?

问题背景

我的店铺营业时间为早8点($shopOpenTime = 08)到次日凌晨4点($shopCloseTime = 04),目前已有客户预订的时间段为:09-11、12-14、23-01。需要列出该营业时间范围内所有可用的预订时间区间,预期结果为:01-04 / 08-09 / 11-12 / 14-23。

我之前的尝试

我曾用简单的循环拼接区间,但后续为处理时间转换(如24转为00、25转为01)和边界判断(比如判断上一个预订结束时间是否小于闭店时间、判断下一个预订开始时间是否大于开店时间),写了超过100行的if else语句,代码变得冗长混乱。以下是最初的代码片段:

foreach($manyOldBook as $eachOldBook) { 
    $availableTime = $availableTime . $eachOldBook['book_end'] . " - " . $eachOldBook['book_start'] . " / "; 
} 
echo $availableTime;

现在想寻求更简便的实现方式,比如是否可以用PHP的fmod()函数?或者将已预订时间存入数组,输出数组中不存在的时间区间?还有其他创意且简便的实现方法吗?


解决方案

方法1:将时间转换为「当日分钟数」统一计算(最直观)

由于营业时间跨天,我们可以把所有时间都转换成从当日0点开始的分钟数,彻底规避跨天格式的困扰:

  • 早8点 = 8*60 = 480分钟
  • 次日凌晨4点 = 2460 + 460 = 1680分钟
  • 预订时间也做同样转换,比如23-01就是2360=1380分钟 到 2460+1*60=1500分钟

步骤如下:

  1. 把店铺营业时间、所有已预订区间都转换成分钟数格式,形成统一的时间轴
  2. 将已预订区间按开始时间排序,合并重叠/相邻的区间(避免重复处理)
  3. 遍历合并后的预订区间,对比店铺营业时间,找出空白区间,最后再把分钟数转回到时间格式

示例代码:

<?php
// 店铺营业时间:转成当日分钟数(次日4点按次日的分钟数算)
$shopOpen = 8 * 60; // 480
$shopClose = 24 * 60 + 4 * 60; // 1680

// 已预订的时间段:转成分钟数数组
$bookedIntervals = [
    [9*60, 11*60],
    [12*60, 14*60],
    [23*60, 24*60 + 1*60]
];

// 1. 按开始时间排序预订区间
usort($bookedIntervals, function($a, $b) {
    return $a[0] - $b[0];
});

// 2. 合并重叠/相邻的预订区间
$merged = [];
foreach ($bookedIntervals as $interval) {
    if (empty($merged)) {
        $merged[] = $interval;
        continue;
    }
    $last = &$merged[count($merged)-1];
    if ($interval[0] <= $last[1]) {
        // 重叠或相邻,合并区间
        $last[1] = max($last[1], $interval[1]);
    } else {
        $merged[] = $interval;
    }
}

// 3. 计算可用区间
$available = [];
$currentStart = $shopOpen;

foreach ($merged as $interval) {
    $bookedStart = $interval[0];
    $bookedEnd = $interval[1];
    
    // 当前可用区间开始到预订区间开始
    if ($currentStart < $bookedStart) {
        $available[] = [$currentStart, $bookedStart];
    }
    $currentStart = $bookedEnd;
}

// 处理最后一段:最后一个预订结束到店铺闭店
if ($currentStart < $shopClose) {
    $available[] = [$currentStart, $shopClose];
}

// 4. 将分钟数转成时间格式(处理跨天的情况)
function formatMinutesToTime($minutes) {
    $hours = floor($minutes / 60);
    $hours = $hours % 24; // 转成0-23的格式
    return sprintf("%02d", $hours);
}

// 拼接结果
$result = [];
foreach ($available as $interval) {
    $start = formatMinutesToTime($interval[0]);
    $end = formatMinutesToTime($interval[1]);
    $result[] = "$start-$end";
}

echo implode(" / ", $result);
// 输出:01-04 / 08-09 / 11-12 / 14-23
?>

方法2:用「循环时间轴」标记已占用时段

如果时间精度按小时计算,我们可以创建一个包含店铺营业时间内所有小时的数组,标记已预订的小时,然后遍历数组找出连续的可用小时区间:

<?php
$shopOpen = 8;
$shopClose = 4; // 次日凌晨4点,所以我们把时间轴从8点到次日4点,共20个小时

// 生成店铺营业时间内的所有小时(转成0-47的格式,方便处理跨天)
$timeAxis = [];
for ($h = 8; $h < 24; $h++) {
    $timeAxis[] = $h;
}
for ($h = 0; $h <= 4; $h++) {
    $timeAxis[] = 24 + $h;
}

// 标记已预订的小时
$bookedHours = [];
// 处理09-11
for ($h = 9; $h < 11; $h++) $bookedHours[] = $h;
// 处理12-14
for ($h = 12; $h < 14; $h++) $bookedHours[] = $h;
// 处理23-01
for ($h = 23; $h < 24; $h++) $bookedHours[] = $h;
for ($h = 0; $h < 1; $h++) $bookedHours[] = 24 + $h;

// 找出可用的小时
$availableHours = array_diff($timeAxis, $bookedHours);
sort($availableHours);

// 合并连续的小时成区间
$availableIntervals = [];
$currentStart = null;
foreach ($availableHours as $h) {
    if ($currentStart === null) {
        $currentStart = $h;
        continue;
    }
    // 判断是否连续
    if ($h != $currentStart + count(($availableIntervals[count($availableIntervals)-1]['hours'] ?? []))) {
        // 不连续,结束当前区间
        $availableIntervals[] = ['start' => $currentStart, 'end' => $h-1];
        $currentStart = $h;
    }
}
// 处理最后一个区间
$availableIntervals[] = ['start' => $currentStart, 'end' => end($availableHours)];

// 格式转换
$result = [];
foreach ($availableIntervals as $interval) {
    $start = $interval['start'] % 24;
    $end = ($interval['end'] + 1) % 24; // 因为区间是左闭右开,比如01-04是指1点到4点,包含1、2、3点
    $result[] = sprintf("%02d-%02d", $start, $end);
}

echo implode(" / ", $result);
// 输出:01-04 / 08-09 / 11-12 / 14-23
?>

关于fmod()的用法

fmod()主要用于计算浮点数取模,在这个场景下不是必需的,但如果要快速处理跨天的小时数转换(比如25点转成1点),可以用fmod($hour, 24)来简化:

echo fmod(25, 24); // 输出1
echo fmod(0, 24); // 输出0

这个可以用来替代$hour % 24,但核心还是要先把时间统一到一个可比较的维度(比如分钟数、0-47的小时数)。


内容的提问来源于stack exchange,提问作者Eng Keat Ang

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 18:02:35