
本文介绍如何将形如 "2022-07-15, 2022-07-16, ..." 的日期字符串解析、排序并智能合并为连续日期区间(如 "2022-07-15 - 2022-07-18")与孤立单日,适用于日程聚合、假期统计等场景。
本文介绍如何将形如 "2022-07-15, 2022-07-16, ..." 的日期字符串解析、排序并智能合并为连续日期区间(如 "2022-07-15 - 2022-07-18")与孤立单日,适用于日程聚合、假期统计等场景。
在实际开发中,常需对用户输入或数据库导出的一组离散日期进行逻辑聚类——识别出连续多日构成的区间,并将孤立单日单独保留。核心思路是:将字符串转为有序日期数组 → 按时间连续性分组 → 格式化输出区间或单日。由于输入格式为标准 YYYY-MM-DD,可直接利用字典序排序(无需额外时间戳转换),再通过日期偏移验证连续性。
以下是完整、健壮的 PHP 实现:
function groupDates(string $input): array
{
// 1. 分割并清理空格
$dates = array_map('trim', explode(',', $input));
// 2. 排序(YYYY-MM-DD 格式天然支持字典序升序)
sort($dates);
$ranges = [];
$currentRange = [];
foreach ($dates as $date) {
$timestamp = strtotime($date);
// 若当前范围为空,或当前日期不等于上一日期 +1 天,则开启新区间
if (empty($currentRange) || $timestamp !== strtotime(end($currentRange) . ' +1 day')) {
$currentRange = [$date];
$ranges[] = &$currentRange;
} else {
// 扩展当前区间终点
$currentRange[1] = $date;
}
}
// 3. 格式化结果:单日保持原样,区间用 "起始 - 结束" 表示
return array_map(function ($range) {
return count($range) === 1 ? $range[0] : $range[0] . ' - ' . $range[1];
}, $ranges);
}
// 使用示例
$input = "2022-07-15, 2022-07-16, 2022-07-17, 2022-07-18, 2022-07-22, 2022-07-25, 2022-07-26, 2022-07-27, 2022-07-28";
$result = groupDates($input);
print_r($result);
// 输出:
// Array
// (
// [0] => 2022-07-15 - 2022-07-18
// [1] => 2022-07-22
// [2] => 2022-07-25 - 2022-07-28
// )✅ 关键设计说明:
- 使用 array_map('trim', ...) 消除逗号后多余空格,提升鲁棒性;
- 避免引用传递(如原答案中的 &$range)带来的潜在副作用,改用更清晰的 $currentRange 管理逻辑;
- strtotime($date . ' +1 day') 是验证连续性的简洁方式,兼容所有 PHP 支持的时区(建议确保脚本时区已正确设置,如 date_default_timezone_set('Asia/Shanghai'));
- 单日区间不拼接连字符,语义更准确(如 "2022-07-22" 而非 "2022-07-22 - 2022-07-22")。
⚠️ 注意事项:
- 输入日期必须为有效 YYYY-MM-DD 格式,否则 strtotime() 返回 false 导致逻辑中断;生产环境建议添加 DateTime::createFromFormat() 校验;
- 若数据量极大(>10⁴ 条),可考虑预转换为时间戳数组以减少重复调用 strtotime 的开销;
- 该算法时间复杂度为 O(n log n)(主要由排序决定),空间复杂度 O(n),适用于常规业务规模。
通过此方法,你可轻松将杂乱的日期列表转化为结构清晰的时段表达,为前端展示、报表生成或业务规则判断提供坚实基础。

















