
本文介绍如何将类似“15 minutes”“4 hours”的字符串格式时间限制,安全转换为时间戳,并准确计算距离当前时刻的剩余秒数及可读性友好的倒计时(如“2 hours left”)。
本文介绍如何将类似“15 minutes”“4 hours”的字符串格式时间限制,安全转换为时间戳,并准确计算距离当前时刻的剩余秒数及可读性友好的倒计时(如“2 hours left”)。
在构建工单(ticket)系统时,常需为每个工单设定动态响应时限(如“30 minutes”“2 days”),并实时展示剩余处理时间。但 PHP 的 strtotime() 并不直接支持对纯描述性时间字符串(如 "15 minutes")进行相对减法运算——它只能解析为绝对时间戳(如从 now 开始推算),而无法直接用于与 DateInterval 对象做算术运算。
正确做法是:先将目标时限字符串解析为相对于当前时间的未来时间戳,再减去当前时间戳,得到剩余秒数;最后将秒数智能换算为最合适的单位(秒/分/小时/天等)并格式化输出。
✅ 推荐实现方式(安全、可扩展、兼容性强)
function timeLeft($targetString, $referenceTime = null) {
$referenceTime = $referenceTime ?: time();
// 使用 strtotime 解析 "15 minutes", "4 hours", "2 days" 等相对表达式
$targetTimestamp = strtotime($targetString, $referenceTime);
if ($targetTimestamp === false) {
throw new InvalidArgumentException("Invalid time string: '$targetString'");
}
$remainingSeconds = $targetTimestamp - $referenceTime;
if ($remainingSeconds <= 0) {
return "Expired";
}
// 定义时间单位阶梯(从秒向上归约)
$units = [
['divisor' => 60, 'name' => 'second'],
['divisor' => 60, 'name' => 'minute'],
['divisor' => 24, 'name' => 'hour'],
['divisor' => 30, 'name' => 'day'], // 近似:1 month ≈ 30 days
['divisor' => 12, 'name' => 'month'], // 近似:1 year ≈ 12 months
];
$value = $remainingSeconds;
$unitIndex = 0;
// 向上归约至最大合适单位(例如:7200s → 2h,而非 120m)
while ($unitIndex < count($units) - 1 && $value >= $units[$unitIndex]['divisor']) {
$value /= $units[$unitIndex]['divisor'];
$unitIndex++;
}
$value = round($value);
$unitName = $units[$unitIndex]['name'];
$plural = ($value !== 1) ? 's' : '';
return "$value $unitName$plural left";
}
// ✅ 使用示例:
echo timeLeft("15 minutes") . "\n"; // "15 minutes left"
echo timeLeft("4 hours") . "\n"; // "4 hours left"
echo timeLeft("2 days") . "\n"; // "2 days left"
echo timeLeft("1 week") . "\n"; // "7 days left"(strtotime 支持 'week')⚠️ 注意事项与最佳实践
- 不要混用 DateTime::diff() 和 strtotime() 字符串:date_diff() 返回的是 DateInterval 对象,不能直接与整数时间戳相减;原问题中 $timeRemaining = $targetTime - $interval 是类型错误。
- strtotime() 的第二个参数至关重要:传入 $referenceTime(如 time() 或工单创建时间戳)可确保 "15 minutes" 被解析为「从该时刻起 15 分钟后」,而非默认从 Unix epoch 开始计算。
- 避免硬编码时间常量:如原答案中 1649991600 是具体时间戳,不具备通用性;应始终基于字符串动态解析。
- 单位近似需明确告知用户:"1 month" 在 strtotime() 中按 30 天处理,"1 year" 按 365 天,这对 SLA 场景足够实用,但高精度场景建议改用 DateInterval + DateTime::add() 显式计算。
- 务必校验解析结果:strtotime() 失败返回 false,需异常处理,防止静默错误。
✅ 总结
真正健壮的剩余时间计算,核心在于:
① 用 strtotime($targetString, $baseTime) 将自然语言时限转为绝对时间戳;
② 用差值获取剩余秒数;
③ 智能降维(秒→分→小时→天…)并格式化为用户友好文案。
该方案简洁、可读性强、易于维护,适用于绝大多数工单、告警、SLA 监控类业务场景。



















