最稳方案是用strtotime手动计算周区间:先校准起始日为周一,再循环生成N周的[start,end]数组,每次单独查询并确保$end设为23:59:59;推荐Carbon的startOfWeek()/endOfWeek()配合copy()避免修改原实例,跨年周须用date('o-W')作key。

ThinkPHP5.1里用date和strtotime手动算周区间最稳
TP5.1 的 think\facade\Date 或模型时间字段本身不提供「按周循环」的内置迭代器,直接用 foreach 遍历周会出错。得自己构造每周的起止时间戳——别依赖 Db::table()->whereTime() 的模糊匹配,它查的是单点,不是区间。
常见错误是写成:whereTime('create_time', 'between', ['2024-01-01', '2024-01-07']),这只能查某一周,没法自动推下周。真正要的是:给定一个起始日,循环生成接下来 N 周的 ['start', 'end'] 数组。
- 用
strtotime('this week')获取本周一(非周日),再用strtotime('+1 week', $timestamp)递增 - 确保起始日是周一:用
date('N', $ts) === '1'判断,否则先减去(date('N', $ts) - 1)天校准 - 每周结束时间建议用
strtotime('+6 days', $monday)得到周日 00:00:00,避免跨日时区偏差
DB 查询必须显式写 BETWEEN 时间范围
TP5.1 的 whereTime 不支持传入数组做「每轮不同范围」的批量查询,每次循环都得单独执行 SQL。想一次查完所有周的数据?不行——除非你手拼 UNION ALL 或改用原生查询,但维护成本高,不推荐。
所以标准做法是:先生成周区间数组,再 foreach 里调 Db::table()->where('create_time', 'between', [$start, $end])->select()。
立即学习“PHP免费学习笔记(深入)”;
- 注意 MySQL 的
datetime字段比较默认包含时分秒,$end设为'2024-01-07 23:59:59'更安全(或用+ <code>date_add($start, interval 6 day)) - 如果数据量大,别在循环里查,改用时间范围聚合:先
SELECT DATE_FORMAT(create_time,'%Y-%u') as week_key, COUNT(*) FROM table GROUP BY week_key,再在 PHP 侧对齐周序号 - TP5.1 默认时区是
date_default_timezone_get(),确保和数据库时区一致,否则周一可能偏移一天
用 Carbon 替代原生函数更可靠(需手动引入)
TP5.1 不自带 Carbon,但 composer require nesbot/carbon 后,周处理逻辑会清晰很多,尤其跨年、闰周(ISO 8601)场景下原生 strtotime 容易翻车。
示例:
use Carbon\Carbon;
$monday = Carbon::parse('2024-01-01')->startOfWeek(); // 强制取周一
for ($i = 0; $i < 5; $i++) {
$weekStart = $monday->copy()->addWeeks($i);
$weekEnd = $weekStart->copy()->endOfWeek();
$data[] = [
'week' => $weekStart->format('Y-m-d') . ' ~ ' . $weekEnd->format('Y-m-d'),
'list' => Db::table('log')->whereBetween('create_time', [$weekStart, $weekEnd])->select(),
];
}
-
startOfWeek()和endOfWeek()默认按 ISO 标准(周一到周日),不用手动算偏移 - 每次循环必须用
copy(),否则$monday会被修改,后续周全乱 - Carbon 实例可直接传给
whereBetween,TP5.1 的 Query 类能自动转为字符串
模板里输出周列表容易漏掉「空周」
前端要显示连续 8 周的统计卡片,但某几周没数据——如果只查出有记录的周,模板里就得补空数组,逻辑分散。更好的方式是在 PHP 层就生成完整周结构,再 merge 数据。
- 先用循环生成 8 个带
week_label和空items的数组 - 查库后用
week_start作 key 建哈希表,再遍历原始周数组做isset($map[$weekKey]) ? $map[$weekKey] : [] - 别用
date('W')当周 key:它每年从 1 开始,2023-12-31 可能是 2024-W01,导致跨年周错位;改用date('o-W')(ISO 年+周)
跨年那周最易被忽略:2023-12-31 是星期日,属于 2024 年第 1 周,但 date('Y-W') 会输出 2023-52,硬套会少一周。



















