最可靠的方式是用 DateTime 类配合 diff() 方法,因其支持时区感知、日历语义和夏令时处理;strtotime() 相减易受时区、闰秒、DST 等影响导致结果错误。

PHP 计算时间差最可靠的方式是用 DateTime 类配合 diff() 方法,而不是手动解析字符串或用 strtotime() 相减 —— 后者在跨月、夏令时、闰秒等场景下极易出错。
为什么 strtotime() 相减不推荐用于精确时间差
直接用 strtotime($end) - strtotime($start) 得到的是秒数,看似简单,但隐藏严重逻辑缺陷:
- 它把所有日期都当作“本地时区的 Unix 时间戳”,若输入字符串没带时区(如
"2024-03-15 10:00"),PHP 会按date_default_timezone_get()解析,跨时区协作时结果不可控 - 遇到 2023-10-29 凌晨 2:30(欧洲夏令时回拨)这类时间点,
strtotime()可能返回 false 或重复值,导致差值为负或跳变 - 无法区分“30天”和“1个月”——
strtotime("+1 month")在 1 月 31 日执行会变成 3 月 3 日,而非 2 月 28 日,差值计算失去业务语义
用 DateTime + diff() 正确计算可读时间差
这是 PHP 原生支持的、带时区感知和日历语义的时间差计算方式。关键在于:先构造两个 DateTime 对象,再调用 diff() 获取 DateInterval 实例。
$start = new DateTime('2024-01-15 09:30:00', new DateTimeZone('Asia/Shanghai'));
$end = new DateTime('2024-03-22 17:45:30', new DateTimeZone('Asia/Shanghai'));
$diff = $start->diff($end);
<p>echo $diff->format('%y 年 %m 月 %d 天 %h 小时 %i 分钟 %s 秒');
// 输出:0 年 2 月 7 天 8 小时 15 分钟 30 秒
-
format()中的占位符必须小写(%y不是%Y),否则返回空字符串 - 若需总天数,用
$diff->days(注意:这是绝对天数,不包含年/月折算) - 跨时区比较时,务必显式传入
DateTimeZone,不要依赖默认时区
需要纯秒级差值?仍建议走 DateTime 而非 strtotime()
虽然 diff() 主要面向人类可读格式,但获取总秒数依然更稳健:
立即学习“PHP免费学习笔记(深入)”;
$start = new DateTime('2024-01-01 00:00:00');
$end = new DateTime('2024-01-02 03:30:45');
$diff = $start->diff($end);
<p>$totalSeconds = $diff->days <em> 86400 + $diff->h </em> 3600 + $diff->i * 60 + $diff->s;
// 比直接相减更可控:避免因时区或 DST 导致的秒数偏差
- 不要用
$end->getTimestamp() - $start->getTimestamp(),因为getTimestamp()会强制转成 UTC 时间戳,若对象本身有时区偏移,可能引入隐式转换误差 - 如果确定两端都是 UTC 时间且无夏令时影响,
getTimestamp()相减可用,但属于特例,不是通用解法
真正容易被忽略的点是:时间差的“语义”取决于业务场景——是看日历跨度(用 diff()),还是看真实流逝秒数(用 UTC 时间戳差)?前者适合展示给用户,后者适合计费或超时控制。选错方法,线上就可能凌晨三点突然多算一天。



















