最直接方法是用 array_filter 配合闭包:对 month 字段直接比较,对 'Y-m-d' 日期用 substr($item['date'], 5, 2) 提取月份,对时间戳用 date('n', $item['ts']) 避免补零问题。

用 array_filter 筛选月份字段最直接
PHP 里筛选带月份的数据,核心不是“生成月份数组”,而是从已有数组中按 month、date 或 created_at 这类字段提取对应月份的元素。直接用 array_filter 配合闭包最轻量,也最可控。
常见错误是先用 date('m') 把所有时间转成字符串再比对,但没考虑时区或格式不统一(比如数据库存的是 '2024-03-15',而代码里写成 '03' 却忘了补零)。
- 如果数据里有明确的
month数字字段(如3表示三月),直接==比较即可 - 如果是完整日期字符串(如
'2024-03-15'),用substr($item['date'], 5, 2)提取月份更可靠,避免strtotime的隐式转换开销和时区干扰 - 若字段是 UNIX 时间戳,用
date('n', $item['ts'])(注意用'n'不补零,避免'03' == 3类型不匹配)
where in 多个月份怎么写才不翻车
要查“1月、3月、12月”的数据,别手写三个 array_filter 再 array_merge——性能差还难维护。正确做法是把目标月份预存在一个数组里,用 in_array 判断:
$targetMonths = [1, 3, 12];
$filtered = array_filter($data, function($item) use ($targetMonths) {
return in_array((int)$item['month'], $targetMonths);
});
容易踩的坑:$item['month'] 可能是字符串 '01',强制转 (int) 能统一类型;如果用 in_array($item['month'], $targetMonths, true),严格模式下 '1' 和 1 就不匹配了。
立即学习“PHP免费学习笔记(深入)”;
date() 格式化后筛选反而慢且易错
有人喜欢先把整个数组的日期都用 date('Y-m', strtotime($item['created_at'])) 格式化,再用 strpos 或正则筛。这既多余又危险:每次循环都调用 strtotime,性能随数据量线性下降;而且一旦 $item['created_at'] 是空、null 或非法格式,strtotime 返回 false,date() 就报 Warning: date(): Invalid date format。
真正该做的,是提前校验或默认兜底:
- 用
is_string($item['created_at']) && strlen($item['created_at']) >= 7快速跳过明显异常值 - 提取月份优先走字符串截取(如
substr($item['created_at'], 0, 7)对应'2024-03'),比解析快 3–5 倍 - 真需要格式化再比较时,加
@抑制警告,但不如从源头过滤干净
Laravel Collection 的 whereMonth 不适合原始数组
如果你在 Laravel 项目里操作 Eloquent 结果集,$collection->whereMonth('created_at', 3) 很方便。但它底层依赖 Carbon 和属性访问器,**对纯 PHP 关联数组无效**——直接调会报 Call to undefined method Illuminate\Support\Collection::whereMonth() 或更隐蔽的 Undefined index: created_at 错误。
正确姿势只有两个:
- 原始数组就老实用
array_filter+ 手动提取月份 - 非要上 Collection,得先
collect($yourArray),但注意这会额外内存开销,万条以上数据慎用
月份筛选本身不复杂,真正的复杂点在于数据源格式不一致、空值处理被忽略、以及把框架方法误当成通用 PHP 功能来用。











