返回博客

PHP date('u') 永远输出零——datetime(6) 微秒精度静默丢失

某天在排查一个后台管理系统的数据问题时,发现批量写入的记录按 create_time 排序后顺序完全乱掉了。数据库字段类型是 datetime(6),本意是用微秒区分同一秒内的多条记录——但查库一看,所有时间戳清一色以 .000000 结尾。没有任何报错,代码跑得毫无违和感,数据就这样悄悄烂掉了。

现象

  • MySQL 列类型 datetime(6),期待亚秒精度
  • PHP 写入时使用 date('Y-m-d H:i:s.u') 格式化当前时间
  • 入库后所有记录的微秒部分均为 .000000
  • 毫秒级批量操作中,多条记录 create_time 完全相同,排序失去意义

根因

PHP 的 date() 函数接受的时间戳参数是 Unix integer(整数秒),本身不携带任何亚秒信息。格式符 u 在文档里确实列出来了,含义是"微秒"——但 date() 内部拿到整数秒后,微秒部分永远是零,所以输出永远是 000000

这是 PHP 的设计行为,不是 bug,但文档的措辞非常容易让人误以为 date('u') 能返回当前时间的微秒。

与之相对,DateTime 对象在构造时(new DateTime())会从系统时钟抓取完整的微秒精度,保存在内部结构里;DateTime::format('u') 读取的正是这个内部值,因此能输出真实微秒。

坑:date('u') 不产生微秒,只产生六个零
echo date('Y-m-d H:i:s.u');
// 输出:2026-03-15 10:00:00.000000  ← 永远是零

即便你的 MySQL 列是 datetime(6),写进去的也只有秒级精度。 没有 warning,没有 exception,数据静默截断。

正解

换用 DateTime::format(),它从对象内部读取微秒,而不是从整数时间戳里推算:

// 错误写法
$time = date('Y-m-d H:i:s.u');
 
// 正确写法
$time = (new \DateTime())->format('Y-m-d H:i:s.u');

验证对比:

echo date('Y-m-d H:i:s.u');
// 2026-03-15 10:00:00.000000  ← 全零
 
echo (new \DateTime())->format('Y-m-d H:i:s.u');
// 2026-03-15 10:00:00.482351  ← 真实微秒

如果项目里多处需要微秒时间戳,封装成静态 helper 最稳:

private static function now(): string
{
    return (new \DateTime())->format('Y-m-d H:i:s.u');
}

如果你持有的是一个 microtime(true) 浮点数,想从中取微秒,也不能走 date(),需要先转换为 DateTime:

$dt = \DateTime::createFromFormat('U.u', number_format(microtime(true), 6, '.', ''));
echo $dt->format('Y-m-d H:i:s.u');
正解:用 DateTime::format() 而不是 date()

(new \DateTime())->format('Y-m-d H:i:s.u') 能输出真实微秒。 凡是需要写入 datetime(6) 列、或依赖亚秒精度排序的场景,一律用这个。

提示:PHP 文档列出了 u,但 date() 拿不到它

PHP 官方手册在 date() 的格式符表格里确实写了 u = Microseconds,但紧接着有一句小字:「Since PHP 5.2.2, this character is not yet implemented in the date() function itself(……在 date() 函数本身中尚未实现)」。很容易扫一眼就跳过。

一句话外卖

PHP date() 的时间戳是整数秒,格式符 u 在其中永远为零;需要微秒精度,必须用 DateTime::format()