如何计算两段日期的业务时长并排除对应实体的暂停时间?
实现扣除暂停时段的有效业务时长计算
需求说明
- 业务时间范围:周一至周五 09:00-17:00,需排除公共节假日
- 核心计算逻辑:先算出实体时段内的基础业务时长,再扣除暂停时段与「业务时段+实体自身时段」的重叠时长
- 示例(entity_id=1):
- 实体时段:2023-12-04 10:00:00 至 2023-12-05 12:01:01,基础业务时长为10小时1分1秒
- 暂停时段1:2023-12-04 09:00:00至12:00:00,重叠有效时长为2小时
- 暂停时段2:2023-12-05 12:00:00至14:00:00,重叠有效时长为1分1秒
- 最终有效时长:8小时
实现思路
要计算单段暂停的有效重叠时长,需同时取三个范围的交集:
- 暂停时段本身
- 实体自身的业务时段
- 符合规则的业务时间(工作日09-17、非节假日)
最终计算该交集的时长,累加所有暂停的有效重叠时长后从基础业务时长中扣除。
代码实现
假设已存在以下变量:
$openingHours:spatie/opening-hours定义的工作时间规则(已配置节假日排除)$entityStart/$entityEnd:实体时段的起始、结束Carbon对象$pauses:实体的所有暂停数据数组,每个元素包含start和end时间字符串$baseBusinessDuration:已算出的基础业务时长(单位:秒)
1. 单段暂停重叠时长计算函数
use Carbon\Carbon; use Spatie\OpeningHours\OpeningHours; function calculatePauseOverlap(Carbon $pauseStart, Carbon $pauseEnd, Carbon $entityStart, Carbon $entityEnd, OpeningHours $openingHours): int { // 第一步:取暂停时段与实体时段的交集 $overlapStart = max($pauseStart, $entityStart); $overlapEnd = min($pauseEnd, $entityEnd); // 无交集则返回0 if ($overlapStart->greaterThanOrEqualTo($overlapEnd)) { return 0; } // 第二步:计算该交集内的有效业务时长(自动过滤非工作时间/节假日) return $openingHours->diffInSeconds($overlapStart, $overlapEnd); }
2. 总重叠时长计算与最终有效时长得出
$totalPauseOverlap = 0; foreach ($pauses as $pause) { $pauseStart = Carbon::parse($pause['start']); $pauseEnd = Carbon::parse($pause['end']); $totalPauseOverlap += calculatePauseOverlap( $pauseStart, $pauseEnd, $entityStart, $entityEnd, $openingHours ); } // 最终有效时长(单位:秒) $effectiveDuration = $baseBusinessDuration - $totalPauseOverlap; // 转换为可读格式输出 echo \Carbon\CarbonInterval::seconds($effectiveDuration)->cascade()->forHumans();
关键细节
- 暂停时段若在周末、节假日或实体时段外,会通过交集计算自动排除,无需额外判断
- 统一使用Carbon对象处理时间,确保时区一致(建议业务全流程用同一时区,如UTC或本地时区)
- spatie/opening-hours的
diffInSeconds方法已封装了工作时间、节假日的判断逻辑,无需重复造轮子
内容的提问来源于stack exchange,提问作者executable
相关产品推荐
相关产品推荐

