datetime 类比 date() 更可靠,因其将时区作为对象属性封装,支持跨时区转换、夏令时和历史变更,而 date() 依赖全局配置且无法处理时区上下文;应优先用 datetime 替代 date() 和 strtotime()。

date() 和 DateTime 类在时区处理上根本不是一回事
直接用 date() 而不显式设置时区,PHP 会按 date.timezone 配置或系统默认走,但一旦涉及跨时区转换、夏令时、历史时区变更(比如中国 1992 年取消夏令时),date() 就完全无能为力;DateTime 类则把时区作为对象属性封装,所有计算都基于该时区上下文,这才是真正可预测的行为。
-
date('Y-m-d H:i:s', strtotime('2020-03-08 02:30:00'))在美国东部时间可能返回错误时间(跳过或重复的 2:30),而new DateTime('2020-03-08 02:30:00', new DateTimeZone('America/New_York'))会明确抛出异常或按规则归一化 - 用
date_default_timezone_set('Asia/Shanghai')全局设时区,只影响后续date()调用,不影响已创建的DateTime实例,混用极易出错 - 从数据库读出的带时区时间字符串(如
"2023-05-12T14:22:00+08:00"),strtotime()会丢掉时区偏移,DateTime::__construct()则完整保留
用 DateTime 替代 date() 的最小改造写法
不是“重写”,而是把字符串格式化动作从函数调用变成对象方法调用。核心是:**先构造带上下文的 DateTime 对象,再格式化输出**。
- 原写法:
date('Y-m-d', time())→ 改为:(new DateTime())->format('Y-m-d') - 原写法:
date('Y-m-d H:i:s', strtotime($input))→ 改为:(new DateTime($input))->format('Y-m-d H:i:s')(自动解析时区) - 若需固定时区(如统一转东八区):
(new DateTime($input, new DateTimeZone('UTC')))->setTimezone(new DateTimeZone('Asia/Shanghai'))->format('Y-m-d H:i:s') - 注意:
new DateTime()默认使用当前时区(由date_default_timezone_get()决定),不是 UTC,这点和 JavaScript 的new Date()不同
strtotime() 是最大的兼容陷阱,必须替换成 DateTime 构造器
strtotime() 返回的是 Unix 时间戳(int),本质是 UTC 秒数,但它对输入字符串的解析严重依赖当前时区上下文,且不暴露解析过程。一旦输入含模糊表述(如 “next Monday”、“last day of month”),结果可能因时区或 PHP 版本差异而不同。
-
strtotime('2024-02-29')在非闰年返回false,但new DateTime('2024-02-29')抛出Exception,更利于提前发现问题 -
strtotime('1 hour ago')依赖当前系统时间,无法回放;new DateTime('-1 hour')同样依赖当前时间,但配合DateTime::setTimestamp()可精准控制基准点 - 真正需要“相对时间计算”的场景,应优先用
DateTime::modify()或DateInterval:$dt = new DateTime(); $dt->add(new DateInterval('P1D'));
date() 的 format 参数在 DateTime::format() 中完全兼容
所有 date() 支持的格式字符(Y、m、d、H、i、s 等)在 DateTime::format() 中行为一致,无需调整格式字符串。真正要小心的是那些隐式依赖全局状态的功能:
立即学习“PHP免费学习笔记(深入)”;
-
getdate()返回关联数组,DateTime::getTimestamp()返回 int,DateTime::getTimezone()返回DateTimeZone对象——别试图用date()函数去处理DateTime对象 -
mktime()生成时间戳,DateTime::createFromFormat()才是它的现代替代,尤其处理非标准格式(如"2023/04/05 14:30")时更可靠 - 如果你还在用
date('U')获取时间戳,直接改用(new DateTime())->getTimestamp(),它更清晰地表达了“此刻的 UTC 时间戳”语义
date() 处理带时区需求,就像用计算器做微积分——不是不能按,而是按完不知道哪一步错了。











