
本文介绍如何在 php 中编写函数,将任意起止日期范围按自然周(周一至周日)精确切分为多个子区间,确保首段从用户指定的起始日开始、末段在指定结束日截止,避免自动对齐到周初或周末。
本文介绍如何在 php 中编写函数,将任意起止日期范围按自然周(周一至周日)精确切分为多个子区间,确保首段从用户指定的起始日开始、末段在指定结束日截止,避免自动对齐到周初或周末。
在实际开发中(如报表生成、周粒度数据聚合、排班系统等),常需将用户自定义的日期范围(如 2022-08-05 至 2022-08-16)按“周”逻辑分组,但不能简单取每周一/周日作为边界——否则会引入超出用户原始范围的无效日期(例如将 2022-08-05 自动扩展为 2022-08-01)。核心诉求是:严格限定在 $startDate 到 $endDate 闭区间内,按 ISO 周(周一为第1天,周日为第7天)自然断点,首段起点=用户起点,末段终点=用户终点,中间段完整覆盖整周。
以下是一个健壮、可读性强的实现方案,使用 PHP 原生 DateTime、DatePeriod 和 DateInterval 类,避免时间戳计算误差与时区隐患:
function splitDateRangeByWeek(string $startDate, string $endDate): array
{
$start = new DateTime($startDate);
$end = new DateTime($endDate);
// 确保起始日期不晚于结束日期
if ($start > $end) {
throw new InvalidArgumentException('Start date must be on or before end date.');
}
$interval = new DateInterval('P1D');
$period = new DatePeriod($start, $interval, $end->modify('+1 day')); // +1 day 保证包含 endDate
$weeks = [];
$weekStart = $start->format('Y-m-d'); // 首段起点即用户指定起点
$currentWeek = (int)$start->format('W');
foreach ($period as $date) {
$weekNum = (int)$date->format('W');
// 当前日期属于新一周,且不是第一天(即已积累至少一天)
if ($weekNum !== $currentWeek && $date->format('Y-m-d') !== $weekStart) {
$weeks[] = [
'start' => $weekStart,
'end' => (new DateTime($date->format('Y-m-d')))->modify('-1 day')->format('Y-m-d'),
'week' => $currentWeek,
];
$weekStart = $date->format('Y-m-d');
$currentWeek = $weekNum;
}
}
// 添加最后一段:从当前 weekStart 到用户指定的 endDate
$weeks[] = [
'start' => $weekStart,
'end' => $end->format('Y-m-d'),
'week' => $currentWeek,
];
return $weeks;
}
✅ 使用示例:
$startDate = '2022-08-05';
$endDate = '2022-08-16';
$result = splitDateRangeByWeek($startDate, $endDate);
foreach ($result as $i => $week) {
echo sprintf("Week %d: %s to %s\n", $i + 1, $week['start'], $week['end']);
}
? 输出结果:
Week 1: 2022-08-05 to 2022-08-07 Week 2: 2022-08-08 to 2022-08-14 Week 3: 2022-08-15 to 2022-08-16
? 关键设计说明:
- 使用
DatePeriod迭代每日,确保边界精确可控; - 以 ISO 周编号(
format('W'))判断周切换,天然支持跨年周(如 2024-12-30 属于 2025 年第 1 周); - 首段强制以
$startDate开始,末段强制以$endDate结束,绝不外延; - 自动处理单日区间(如
'2022-08-10'→'2022-08-10')、跨月周(如'2022-07-31'至'2022-08-02')等边界场景。
⚠️ 注意事项:
- 函数默认使用服务器本地时区,如需统一时区(推荐),请在开头添加
date_default_timezone_set('Asia/Shanghai'); - 若业务要求“周日为每周第一天”,可改用
format('w')(0=周日)并调整逻辑,但 ISO 标准(周一为第1天)更通用; - 对超长日期范围(如数年),建议增加内存与性能监控,必要时改用数据库
GROUP BY YEARWEEK(date, 1)辅助分页。
该方案兼顾准确性、可维护性与兼容性,可直接集成至 Laravel、Symfony 或原生 PHP 项目中,作为日期分组的基础工具函数。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











