最稳方式是用 strtotime 标准化起止时间戳,for 循环每次加 86400 并 date('Y-m-d') 格式化;查库用 whereBetween + group('DATE(create_time)') 一次性聚合,再用 array_fill_keys 补零并 ksort 排序,最后统一转 float 避免类型不一致。

用 date 和 strtotime 生成连续日期数组最稳
ThinkPHP5.1 没有内置的「日期范围生成器」,直接写 for 循环加 date('Y-m-d', $timestamp) 是最可控的方式。别碰 Carbon 或第三方扩展——TP5.1 的自动加载和依赖管理容易在部署时出问题,尤其当服务器没装 intl 扩展时,Carbon::parse() 会静默失败或返回空。
常见错误是用 DateTime::modify() 在循环里反复调用,结果某天因夏令时切换导致跳过一天;还有人用 range(strtotime('2024-01-01'), strtotime('2024-01-31'), 86400),但 Windows 下 range 不支持大步长,会报 Warning 并返回空数组。
- 起止日期必须先用
strtotime标准化,避免字符串格式不一致(如'2024/01/01'和'2024-01-01') - 每次循环只对时间戳做
+86400,再用date('Y-m-d', $ts)格式化,不依赖对象状态 - 结束条件用
$ts ,不是 <code>$ts ,否则最后一天漏掉
查数据库时用 whereBetween 配合 group 而非 PHP 循环里逐条 where
高频场景下,比如按日汇总订单数、销售额,如果在 PHP 循环里对每个日期都执行一次 Db::table('order')->where('create_time', 'like', $date.'%')->sum('amount'),100 天就是 100 次查询,I/O 成瓶颈,且 MySQL 连接池容易打满。
正确做法是:一次性查出所有目标日期范围内的记录,用 whereBetween('create_time', [$start, $end]),再用 group('DATE(create_time)') 让 MySQL 做聚合。注意 create_time 字段类型要是 DATETIME 或 TIMESTAMP,如果是 INT 存时间戳,得写 group("FROM_UNIXTIME(create_time, '%Y-%m-%d')")。
立即学习“PHP免费学习笔记(深入)”;
-
whereBetween的两个值必须是标准 Y-m-d H:i:s 格式,不能传入纯日期字符串如'2024-01-01',否则 MySQL 会当成'2024-01-01 00:00:00',漏掉当天 0 点后的数据 - 聚合结果的日期字段名默认是
DATE(create_time),建议用->field('DATE(create_time) as date_day, sum(amount) as total')显式取别名,避免后续 PHP 中取键出错 - 查出来的是不连续的日期(比如某天没订单),需用前面生成的完整日期数组做
array_fill_keys合并补零
补零合并时小心 array_merge 键冲突和时区偏移
数据库只返回有数据的日期行,而业务要求输出 31 天全量结构(含 0 值),必须用 PHP 补齐。这时别用 array_merge($fullDates, $dbResult)——它会重排数字键,把日期字符串键全干掉;也别用 + 运算符,它不覆盖已存在键,导致有数据的那天被忽略。
真正有效的写法是:先用 array_fill_keys($dateList, ['total' => 0]) 初始化完整数组,再遍历数据库结果,按 $row['date_day'] 为键覆盖对应项。关键点在于确保两边的日期字符串完全一致:数据库里 DATE(create_time) 返回的是 '2024-01-01',你的 $dateList 也必须是同样格式,不能混用 Y/m/d 或带时间部分。
- 如果数据库字段是
INT时间戳,FROM_UNIXTIME默认走系统时区,而 PHP 的date()受date_default_timezone_set()影响,两边时区不一致会导致同一天变成两个键(如'2024-01-01'和'2024-01-02') - 补零后记得用
ksort()按日期升序排列,否则前端渲染顺序错乱
导出 Excel 或接口返回前检查日期字段类型和空值
汇总完的数据常要导出或提供给前端,这时候最容易翻车的是字段类型:MySQL 返回的 sum() 是字符串(如 "1234.50"),PHP 数组里补的 0 是整型,json_encode 后数值类型不统一,前端做图表时可能报错或显示异常。
- 统一转成 float:对每个
$item['total']做(float) $item['total'],别用intval或强制类型转换(int),否则小数丢失 - 如果某天数据为空(比如
null),补零逻辑没覆盖到,json_encode会变成null,前端解析失败,务必在合并后加isset($item['total']) ? $item['total'] : 0.00 - Excel 导出用
PHPExcel或PhpSpreadsheet时,日期列如果写入的是字符串'2024-01-01',得手动设单元格格式为日期类型,否则打开是普通文本,排序失效
日期循环本身不难,难的是每一步的格式对齐和类型守恒——数据库、PHP、JSON、Excel 四个环节的日期表示和数值精度稍有偏差,最终报表就差一天或少一列。



















