代码 PHP 8.6 的 Time\Duration:把超时和退避延迟换成带纳秒精度的值类型

2026-11-11 21:07:09

PHP 8.6 的 Time\Duration:把超时和退避延迟换成带纳秒精度的值类型

PHP 8.6 新增了 Time\Duration,用来表示一段固定长度的时间(比如超时),精度到纳秒。它是个 final readonly 值类型:不可变,可以拿 >、<、== 这类比较运算符直接比大小。

和 DateInterval 的区别

DateInterval 带日历单位。一天是 23 还是 25 小时、一个月是 28 还是 31 天,取决于它挂在哪个时区、哪个月份上。Time\Duration 没有天、月这类日历单位,它就是一段长度,长度无歧义。所以它适合描述「等 500 毫秒」这种语义,不适合描述「下个月同一天」。

工厂方法、运算、符号

工厂方法按单位切分:Time\Duration::fromSeconds()、Time\Duration::fromMilliseconds()、Time\Duration::fromIso8601DurationString()。

运算有四则:add()、sub()、multiplyBy()、divideBy()。改符号用 negate() 和 absolute()。compare() 可以当作排序回调传给 usort() 之类。

两个 getter 是属性形式:seconds 与 nanoseconds。

use Time\Duration;

$oneSecond   = Duration::fromSeconds(1);
$halfSecond  = $oneSecond->divideBy(2);
$total       = $oneSecond->add($halfSecond);

// 指数退避:第 $attempt 次重试前的延迟
$delay = Duration::fromMilliseconds(100)->multiplyBy(2 ** $attempt);

$total > $delay; // 比较运算符可用

手册示例

<?php
$timeout = \Time\Duration::fromSeconds(2)->add(\Time\Duration::fromMilliseconds(500));
printf("%d.%09d seconds\n", $timeout->seconds, $timeout->nanoseconds);
var_dump($timeout > \Time\Duration::fromSeconds(2));

输出:

2.500000000 seconds
bool(true)

printf 里的 %d.%09d 刚好对上 seconds 和 nanoseconds 两个属性——纳秒部分补足 9 位,不会读出 2.5 这种丢失精度的写法。

什么时候抛异常

两种情况抛 Time\TimeException:

  • 时长超出可表示范围,大约 292 年(纳秒精度下 64 位有符号整数的上限);
  • 传进来的 ISO 8601 duration 字符串非法。

落地取舍

该用它替代 int 毫秒的地方:指数退避、重试延迟、限流窗口这类「纯长度」参数。原来这些参数在函数签名里通常是 int $milliseconds 或者 float $seconds,调用方靠注释和命名猜单位,1000 到底是毫秒还是一秒全靠人肉约定。换成 Duration 之后单位写进构造过程,类型系统替你做一半检查;比较和加减也不用自己乘除换算。

别用的地方:需要日历语义时(「一个月后」「下个工作日」)它表达不了,这类还是 DateInterval / DateTimeImmutable 的地盘。需要写入数据库或跨语言交换时也建议保持数值或 ISO 8601 字符串,落库字段、JSON 契约、别的语言客户端都不认识这个类,序列化前转回 int 毫秒或 ISO 字符串更稳。

这个类被定位成一种共享类型:核心函数和新的 polling API 可以接受 Duration,而不是继续收松散的整数和浮点数。换句话说,它的价值不只在语法糖,而在于让「一段时长」在 PHP 内部有个统一的表达,参数在函数之间传递时不再丢单位。

PHP 8.6 GA 定在 2026 年 11 月 19 日。仓库:https://github.com/php/php-src

复制全文 生成海报 PHP 8.6 时间处理 类型设计

推荐文章

程序员茄子在线接单