讲师中心 微信公众号
AI工具推荐 视频效率加速

PHP 实现时间区间差集运算:从初始时间段中排除多个重叠排除区间

秋杰大大_2910

秋杰大大_2910

发布时间:2026-08-07 10:43:26

|

912人浏览过

|

来源于php中文网

原创

PHP 实现时间区间差集运算:从初始时间段中排除多个重叠排除区间

本文介绍一种健壮的 php 时间区间差集算法,用于从一组初始时间区间中精确减去多个排除区间,支持任意重叠关系,并避免递归死循环,最终返回合并后无重叠的剩余时间片段。

本文介绍一种健壮的 php 时间区间差集算法,用于从一组初始时间区间中精确减去多个排除区间,支持任意重叠关系,并避免递归死循环,最终返回合并后无重叠的剩余时间片段。

在构建日程调度、资源占用计算、权限时段控制等系统时,常需解决「从一个或多个时间区间中,扣除若干排除时段,得到剩余有效时段」的问题。这本质上是时间区间的集合差运算(Set Difference),但 PHP 原生不提供此类功能,需手动实现逻辑严谨的区间切割与合并。

原始代码虽能处理单个排除区间,但在多个排除区间存在时陷入无限递归——根本原因在于:每次递归调用未对 $exclusions 进行状态隔离或进度推进,导致同一排除项反复作用于新生成的子区间,且缺乏去重与合并机制,造成逻辑震荡。

✅ 正确解法应遵循以下核心原则:

  • 非递归优先:避免深度递归,改用迭代+队列方式逐个处理待切分区间;
  • 区间标准化:预先合并重叠的排除区间,减少冗余计算;
  • 四类交集关系全覆盖:对任一 set 与 exclusion,仅存在四种几何关系(完全包含、左交、右交、内含),需分别处理;
  • 结果自动归并:切割后可能存在相邻/重叠区间,需排序后合并。

以下是优化后的生产就绪版本(已移除递归,增强鲁棒性与可读性):

btpanel phpsite 宝塔面板PHP网站
btpanel phpsite 宝塔面板PHP网站

宝塔面板 PHP 网站管理:站点创建、删除、启停、PHP 版本切换、域名管理、SSL证书管理、伪静态管理、数据库管理

下载

立即学习“PHP免费学习笔记(深入)”;

/**
 * 计算时间区间集合差:$sets 减去 $exclusions
 * @param array $sets   [['start' => DateTime, 'end' => DateTime], ...]
 * @param array $exclusions [['start' => DateTime, 'end' => DateTime], ...]
 * @return array 剩余不重叠区间列表(已按起始时间升序排列并合并)
 */
function dateIntervalDiff(array $sets, array $exclusions): array
{
    // Step 1: 预处理 — 合并重叠的排除区间,提升效率并避免重复切割
    $mergedExclusions = mergeDateTimeIntervals($exclusions);

    // Step 2: 迭代处理每个原始区间
    $result = [];
    foreach ($sets as $set) {
        $currentSegments = [$set]; // 当前待处理的子区间队列

        // 对每个排除区间,切分所有现存子段
        foreach ($mergedExclusions as $excl) {
            $nextSegments = [];
            foreach ($currentSegments as $seg) {
                $intersection = getIntersection($seg, $excl);
                if (!$intersection) {
                    // 无交集:保留原段
                    $nextSegments[] = $seg;
                } else {
                    // 有交集:按四类关系切分
                    $parts = splitByExclusion($seg, $excl);
                    $nextSegments = array_merge($nextSegments, $parts);
                }
            }
            $currentSegments = $nextSegments;
        }

        $result = array_merge($result, $currentSegments);
    }

    // Step 3: 合并相邻/重叠结果区间
    return mergeDateTimeIntervals($result);
}

/**
 * 合并重叠或相邻的时间区间(关键辅助函数)
 */
function mergeDateTimeIntervals(array $intervals): array
{
    if (empty($intervals)) return [];

    // 按 start 排序
    usort($intervals, function($a, $b) {
        return $a['start']->getTimestamp() <=> $b['start']->getTimestamp();
    });

    $merged = [$intervals[0]];
    for ($i = 1; $i < count($intervals); $i++) {
        $last = end($merged);
        $curr = $intervals[$i];

        // 若当前区间起点 ≤ 上一段终点 → 可合并(含相邻:如 [09:00,12:00] 和 [12:00,15:00])
        if ($curr['start'] <= $last['end']) {
            $last['end'] = max($last['end'], $curr['end']);
            $merged[key($merged)] = $last;
        } else {
            $merged[] = $curr;
        }
    }

    return $merged;
}

/**
 * 获取两区间交集(返回 null 表示无交集)
 */
function getIntersection(array $a, array $b): ?array
{
    $start = max($a['start'], $b['start']);
    $end   = min($a['end'], $b['end']);
    return ($start <= $end) ? ['start' => $start, 'end' => $end] : null;
}

/**
 * 将区间 $seg 按 $excl 切分为 0–2 个新区间(不含递归)
 */
function splitByExclusion(array $seg, array $excl): array
{
    $sStart = $seg['start']->getTimestamp();
    $sEnd   = $seg['end']->getTimestamp();
    $eStart = $excl['start']->getTimestamp();
    $eEnd   = $excl['end']->getTimestamp();

    // Case 1: exclusion 完全覆盖 segment → 返回空
    if ($eStart <= $sStart && $eEnd >= $sEnd) {
        return [];
    }

    // Case 2: exclusion 在 segment 左侧相交 → 保留右侧
    if ($eStart <= $sStart && $eEnd > $sStart && $eEnd < $sEnd) {
        return [[
            'start' => (clone $seg['start'])->setTimestamp($eEnd),
            'end'   => $seg['end']
        ]];
    }

    // Case 3: exclusion 在 segment 右侧相交 → 保留左侧
    if ($eStart > $sStart && $eStart < $sEnd && $eEnd >= $sEnd) {
        return [[
            'start' => $seg['start'],
            'end'   => (clone $seg['end'])->setTimestamp($eStart)
        ]];
    }

    // Case 4: exclusion 完全在 segment 内部 → 拆为左右两段
    if ($eStart > $sStart && $eEnd < $sEnd) {
        return [
            [
                'start' => $seg['start'],
                'end'   => (clone $seg['start'])->setTimestamp($eStart)
            ],
            [
                'start' => (clone $seg['end'])->setTimestamp($eEnd),
                'end'   => $seg['end']
            ]
        ];
    }

    // No overlap → 保留原段
    return [$seg];
}

// ✅ 使用示例
$sets = [
    [
        'start' => new DateTime('2022-07-04 00:00:00', new DateTimeZone('UTC')),
        'end'   => new DateTime('2022-07-04 23:59:59', new DateTimeZone('UTC'))
    ]
];

$exclusions = [
    [
        'start' => new DateTime('2022-07-04 09:00:00', new DateTimeZone('UTC')),
        'end'   => new DateTime('2022-07-04 13:00:00', new DateTimeZone('UTC'))
    ],
    [
        'start' => new DateTime('2022-07-04 14:00:00', new DateTimeZone('UTC')),
        'end'   => new DateTime('2022-07-04 18:00:00', new DateTimeZone('UTC'))
    ]
];

$result = dateIntervalDiff($sets, $exclusions);
foreach ($result as $r) {
    echo 'From ' . $r['start']->format(DateTime::ATOM) . ' to ' . $r['end']->format(DateTime::ATOM) . '<br>';
}
// 输出:
// From 2022-07-04T00:00:00+00:00 to 2022-07-04T09:00:00+00:00
// From 2022-07-04T13:00:00+00:00 to 2022-07-04T14:00:00+00:00
// From 2022-07-04T18:00:00+00:00 to 2022-07-04T23:59:59+00:00

⚠️ 关键注意事项:

  • 所有 DateTime 实例必须使用相同 DateTimeZone,否则时间戳比较将出错;
  • 本实现默认将 [A,B] 和 [B,C] 视为可合并相邻区间(即闭区间语义),如需开区间行为(如 [A,B)),请调整 <= 为 <;
  • 输入区间需保证 start <= end,否则应提前校验;
  • 大量区间场景下,建议对 $exclusions 预排序并采用区间树优化,但本版已满足中小规模业务需求。

该方案彻底规避了原始递归陷阱,逻辑清晰、边界完备、结果可预测,是 PHP 时间区间运算的推荐实践模式。

相关文章

PHP速学教程(入门到精通)
PHP速学教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

php

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
Laper
Laper Hot

Laper是专为编剧、导演和制片人推出的 AI 原生剧本创作工具。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

音述AI
音述AI Hot

一款AI音频处理工具,主要用于音述AI是一个以“用声音述说故事”为核心的 AI 音乐创作与声音分享社区,适合需要提升相关任务效率的用户。

超级简历WonderCV

一款AI办公效率工具,主要用于免费求职简历模版下载制作,应届生职场人必备简历制作神器,适合需要提升相关任务效率的用户。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

VibeKnow
VibeKnow Hot

一款AI视频创作工具,主要用于全球首个AI知识视频创作平台,文档、文章、网页,一键生成视频,适合需要提升相关任务效率的用户。

切问学术

切问学术是一款AI论文写作工具,复旦大学NLP团队推出的AI学术智能体。

咔片AIPPT

一款在线AI演示文稿制作工具,可根据主题和内容需求辅助生成PPT结构与页面,提高演示材料制作效率。

相关专题

更多
php文件怎么打开
php文件怎么打开

打开php文件步骤:1、选择文本编辑器;2、在选择的文本编辑器中,创建一个新的文件,并将其保存为.php文件;3、在创建的PHP文件中,编写PHP代码;4、要在本地计算机上运行PHP文件,需要设置一个服务器环境;5、安装服务器环境后,需要将PHP文件放入服务器目录中;6、一旦将PHP文件放入服务器目录中,就可以通过浏览器来运行它。

10904

2023.09.01

php怎么取出数组的前几个元素
php怎么取出数组的前几个元素

取出php数组的前几个元素的方法有使用array_slice()函数、使用array_splice()函数、使用循环遍历、使用array_slice()函数和array_values()函数等。本专题为大家提供php数组相关的文章、下载、课程内容,供大家免费下载体验。

6361

2023.10.11

php反序列化失败怎么办
php反序列化失败怎么办

php反序列化失败的解决办法检查序列化数据。检查类定义、检查错误日志、更新PHP版本和应用安全措施等。本专题为大家提供php反序列化相关的文章、下载、课程内容,供大家免费下载体验。

2135

2023.10.11

php怎么连接mssql数据库
php怎么连接mssql数据库

连接方法:1、通过mssql_系列函数;2、通过sqlsrv_系列函数;3、通过odbc方式连接;4、通过PDO方式;5、通过COM方式连接。想了解php怎么连接mssql数据库的详细内容,可以访问下面的文章。

4028

2023.10.23

php连接mssql数据库的方法
php连接mssql数据库的方法

php连接mssql数据库的方法有使用PHP的MSSQL扩展、使用PDO等。想了解更多php连接mssql数据库相关内容,可以阅读本专题下面的文章。

4734

2023.10.23

html怎么上传
html怎么上传

html通过使用HTML表单、JavaScript和PHP上传。更多关于html的问题详细请看本专题下面的文章。php中文网欢迎大家前来学习。

3711

2023.11.03

PHP出现乱码怎么解决
PHP出现乱码怎么解决

PHP出现乱码可以通过修改PHP文件头部的字符编码设置、检查PHP文件的编码格式、检查数据库连接设置和检查HTML页面的字符编码设置来解决。更多关于php乱码的问题详情请看本专题下面的文章。php中文网欢迎大家前来学习。

5317

2023.11.09

php文件怎么在手机上打开
php文件怎么在手机上打开

php文件在手机上打开需要在手机上搭建一个能够运行php的服务器环境,并将php文件上传到服务器上。再在手机上的浏览器中输入服务器的IP地址或域名,加上php文件的路径,即可打开php文件并查看其内容。更多关于php相关问题,详情请看本专题下面的文章。php中文网欢迎大家前来学习。

4162

2023.11.13

OpenJDK JVM参数怎么调优
OpenJDK JVM参数怎么调优

Java应用运行缓慢,如何调整JVM参数?本专题介绍OpenJDK JVM性能调优的基本流程,涵盖性能指标确认、运行数据采集、内存配置和垃圾回收分析,帮助你先定位瓶颈,再通过对照测试验证效果,避免直接套用参数而影响应用稳定性。

20

2026.10.16

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
墨刀帮助中心
墨刀帮助中心

共0课时 | 0人学习

MyEclipse学习中心
MyEclipse学习中心

共0课时 | 0人学习

Apache Subversion 官方手册
Apache Subversion 官方手册

共0课时 | 0人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn