
本文介绍如何从包含多个商品有效期的 json 数组中,准确筛选出最近的有效期项(即最小合法日期),自动忽略 `"0000-00"` 等无效值,并返回完整数据对象。
在实际业务开发中(如库存管理、批次追踪等场景),常需从一组带 expiration 字段的 JSON 数据中找出最近到期的商品项。注意:“最近”在此语境下指时间上最早发生的合法到期日(例如 2022-03 比 2023-02 更近),同时必须排除形如 "0000-00" 的占位或无效日期。
原始代码存在两个关键问题:
- min($item->expiration) 无法跨循环比较——每次迭代都用单个字符串调用 min(),结果恒为该字符串本身;
- 字符串直接比较 "2022-03" 和 "2023-02" 虽然可行,但依赖固定格式且缺乏健壮性(如 "2022-12" 与 "2023-01" 的字典序比较仍正确,但不推荐依赖);更可靠的方式是转换为时间戳进行数值比较。
以下是经过优化的专业实现:
/**
* 从 JSON 格式的商品数组中获取最近到期的有效项
* @param string $jsonItems JSON 字符串,格式如: '[{"expiration":"2022-03","quantity":50}, ...]'
* @return array|null 最近到期的项(关联数组),若无有效项则返回 null
*/
function getNearestExp(string $jsonItems): ?array
{
$items = json_decode($jsonItems, associative: true);
if (json_last_error() !== JSON_ERROR_NONE || !is_array($items)) {
throw new InvalidArgumentException('Invalid JSON input');
}
$validItems = [];
foreach ($items as $item) {
// 跳过缺失字段或无效 expiration 值
if (!isset($item['expiration']) || $item['expiration'] === '0000-00') {
continue;
}
// 尝试解析为年-月格式的时间戳(默认当月第一天)
$timestamp = strtotime($item['expiration'] . '-01');
if ($timestamp === false) {
continue; // 跳过无法解析的日期格式
}
$validItems[$timestamp] = $item;
}
if (empty($validItems)) {
return null;
}
ksort($validItems); // 按时间戳升序排列 → 最早到期项在首位
return array_values($validItems)[0];
}
// 使用示例:
$itemsJson = '[{"expiration": "0000-00", "quantity": -50}, {"expiration": "2023-02", "quantity": 100}, {"expiration": "2022-03", "quantity": 50}]';
$result = getNearestExp($itemsJson);
var_dump($result); // 输出: ["expiration" => "2022-03", "quantity" => 50]✅ 关键优势说明:
- 使用 strtotime($item['expiration'] . '-01') 确保 YYYY-MM 格式被正确解析为时间戳(补 -01 避免解析失败);
- 采用 ksort() 对时间戳键排序,逻辑清晰且性能良好;
- 增加了 JSON 解析错误、空数组、非法日期等边界情况处理;
- 返回标准关联数组(非对象),便于后续数组操作或 JSON 序列化。
⚠️ 注意事项:
- 若数据中存在 YYYY-MM-DD 完整格式,本函数同样兼容;
- 不建议直接字符串比较(如 min(array_column($items, 'expiration'))),因 '2022-12' > '2023-01' 在字典序中为真,但时间上错误;
- 生产环境建议配合数据库 DATE 或 VARCHAR 字段的索引优化,高频查询时可预计算并缓存最近到期项。
通过此方法,你不仅能精准获取 2022-03 这一最近到期项,还能确保代码具备可维护性、鲁棒性与扩展性。










