PHP 相对时间显示怎么写:今天、昨天、前天与几天前的正确计算

认真发呆
认真发呆
发布时间:2026-08-25 | 82人浏览过
原创

前言

在信息流、评论区和消息列表里,“刚刚”“昨天 17:08”通常比完整时间戳更适合阅读,但很多 PHP 写法一旦直接用秒差除以 86400,就会把“前天”误算成“1天前”。这篇文章拆开这段函数的判断逻辑,说明它为什么要同时保留“真实经过时间”和“自然日差值”两套计算方式,以及这些阈值在页面展示里分别解决什么问题。

在动态流、评论区和通知列表里,直接显示完整日期通常不如“刚刚”“昨天 17:08”更顺眼,但这类时间文案看似简单,真正写起来很容易在“昨天”和“前天”的边界上算错。下面这段 PHP 实现的重点,不是单纯计算过去了多少秒,而是把“几天前”改成按自然日判断,这样更符合用户在社交场景里的阅读习惯。

为什么直接除以 86400 容易出错

不少示例会把“当前时间戳 - 目标时间戳”直接除以 86400,再取整得到相差天数。这个办法适合算精确经过了多少个 24 小时,但不一定适合页面上的相对时间文案。

用纸面时间轴展示直接除以 86400 与按自然日计算的区别
“几天前”为什么不能只看 86400同样是前天 22:00 到今天 17:08,按秒差与按自然日得出的阅读结果并不一样。

比如现在是 17:08,某条动态更新时间是前天 22:00。如果只按秒差来算,因为它还没有满 48 小时,结果可能只会得到 1;但从用户理解来看,这条内容已经跨过“昨天”这一整天,更自然的显示应该是“前天”或者“2天前”。

也就是说,这里要区分两种逻辑:

  • “刚刚”“几分钟前”“几小时前”:适合按真实经过秒数来算。
  • “昨天”“前天”“几天前”:更适合按自然日边界来算,而不是按是否满 24 小时来算。

这段实现的核心思路

原文采用的处理方式是:先取出今天的最大时间,也就是今天 23:59:59,再用它和目标时间做差。这样得到的不是“现在距离目标过去了多久”,而是“目标时间距离今天结束还差多少秒”。

随后再用这个值除以 86400 并向下取整,就能得到一个更贴近自然日语义的结果:

  • 今天任意时间到今天 23:59:59 的差值都还不到 1 天,因此会落在 0。
  • 昨天任意时间到今天 23:59:59 的差值会落在 1 天到 2 天之间,因此会得到 1。
  • 前天任意时间则会得到 2。

这样一来,“昨天任何时间都算一天前,前天任意时间都算两天前”这个目标就能实现,而不会被“是否满 24 小时”干扰。

PHP 函数原样实现

下面这段代码可以直接放进项目中使用。它把“真实经过时间”和“自然日差值”分开处理:前者决定“刚刚、几分钟前、几小时前”,后者决定“今天、昨天、前天、几天前”。

用四张便签拆解函数里的关键变量和输出分支
函数内部到底在分什么这段函数同时处理“真实经过秒数”和“自然日差值”,两套判断各司其职。

/**
 * 获取已经过了多久
 * PHP时间转换
 * 刚刚、几分钟前、几小时前
 * 今天昨天前天几天前
 * @param  string $targetTime 时间戳
 * @return string
 */
function get_last_time($targetTime)
{
    // 今天最大时间
    $todayLast   = strtotime(date('Y-m-d 23:59:59'));
    $agoTimeTrue = time() - $targetTime;
    $agoTime     = $todayLast - $targetTime;
    $agoDay      = floor($agoTime / 86400);

    if ($agoTimeTrue < 60) {
        $result = '刚刚';
    } elseif ($agoTimeTrue < 3600) {
        $result = (ceil($agoTimeTrue / 60)) . '分钟前';
    } elseif ($agoTimeTrue < 3600 * 12) {
        $result = (ceil($agoTimeTrue / 3600)) . '小时前';
    } elseif ($agoDay == 0) {
        $result = '今天 ' . date('H:i', $targetTime);
    } elseif ($agoDay == 1) {
        $result = '昨天 ' . date('H:i', $targetTime);
    } elseif ($agoDay == 2) {
        $result = '前天 ' . date('H:i', $targetTime);
    } elseif ($agoDay > 2 && $agoDay < 16) {
        $result = $agoDay . '天前 ' . date('H:i', $targetTime);
    } else {
        $format = date('Y') != date('Y', $targetTime) ? "Y-m-d H:i" : "m-d H:i";
        $result = date($format, $targetTime);
    }
    return $result;
}

几个关键变量分别在做什么

$todayLast 表示今天结束时刻的时间戳,用来辅助计算自然日差值。

$agoTimeTrue 表示当前时刻和目标时间的真实秒差,所以它适合判断“刚刚”“几分钟前”“几小时前”。

$agoTime 则是“今天 23:59:59”和目标时间之间的秒差,它不是拿来描述精确经过时间,而是专门服务于“昨天、前天、几天前”这一层文案。

$agoDay 通过 floor($agoTime / 86400) 得出天数区间,决定最终该落入“今天”“昨天”“前天”还是“X天前”。

显示规则是怎么划分的

这段函数的判断顺序也值得注意,因为它体现了页面展示时的优先级。

  • 少于 60 秒,显示“刚刚”。
  • 少于 3600 秒,显示“X分钟前”。
  • 少于 3600 * 12 秒,显示“X小时前”。
  • 超过 12 小时后,不再继续显示“X小时前”,而是切换到“今天 HH:mm”或“昨天 HH:mm”这样的格式。

这个分界点设在 12 小时,意味着同一天里较早的内容,会优先显示成“今天 09:15”这种形式,而不是“14小时前”。这属于产品展示策略,不是必须固定的规则,但对信息流场景来说确实更直观。

此外,$agoDay > 2 && $agoDay < 16 时会显示为“X天前 HH:mm”。超过这个范围后,就切回普通日期格式:

  • 不是同一年时使用 Y-m-d H:i
  • 同一年内使用 m-d H:i

这种处理可以避免“20天前”“83天前”这类文案在列表里显得过于生硬,同时也保留了足够明确的时间信息。

适用场景与边界判断

这类函数很适合用于动态发布时间、评论时间、消息通知时间等强调“相对新旧关系”的地方。它的优势是读起来快,用户几乎不需要再换算日期。

不过在使用时,也要注意它本质上是偏展示层的规则:

  • 如果你的业务更强调精确时间,比如订单创建时间、日志记录时间,就不一定适合统一替换成“几天前”。
  • “12 小时”与“16 天”的阈值并不是通用标准,而是当前这段代码的展示选择,可以按产品需求调整。
  • 函数注释里写的是 @param string $targetTime,但从代码实际用法看,它接收的是时间戳,接入时最好保证传入值就是可参与时间计算的时间戳。

如果你要解决的核心问题是“前天晚上 22:00 到今天下午 17:08,为什么不能算成 1 天前”,那么这段实现已经抓住了关键:社交场景里的相对时间,不该只按秒差算,还要按自然日来表达。

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表