DateInterval
DateInterval 表示一个时间段(如"1年2个月3天"),它是 DateTime 操作的核心辅助类。DateInterval 可以从格式化字符串创建,也可以从日期差值中获取。PHP 8.2+ 引入了 DateInterval::createFromDateString() 的改进。
前置知识
阅读本节前,建议先了解:DateTime 类
基础概念
ISO 8601 持续时间格式
DateInterval 使用 ISO 8601 持续时间格式:P[n]Y[n]M[n]D[T[n]H[n]M[n]S]
P- 持续时间的开始标记(Period)Y- 年,M- 月,D- 天T- 时间部分的开始标记(Time)H- 小时,M- 分钟,S- 秒
php
<?php
declare(strict_types=1);
// P1Y = 1年
// P2M = 2个月
// P3D = 3天
// PT4H = 4小时
// PT30M = 30分钟
// PT45S = 45秒
// P1Y2M3DT4H5M6S = 1年2个月3天4小时5分6秒
// P1W = 1周创建 DateInterval
从 ISO 格式创建
php
<?php
declare(strict_types=1);
// 标准 ISO 格式
$interval = new DateInterval('P1Y2M3D');
echo "1年2月3天" . PHP_EOL;
$interval = new DateInterval('P1DT2H30M');
echo "1天2小时30分" . PHP_EOL;
$interval = new DateInterval('PT45S');
echo "45秒" . PHP_EOL;
// 日期 + 时间部分
$interval = new DateInterval('P1Y2M3DT4H5M6S');
print_r($interval);
// [y] => 1, [m] => 2, [d] => 3, [h] => 4, [i] => 5, [s] => 6从自然语言创建
php
<?php
declare(strict_types=1);
// createFromDateString 使用 strtotime 风格的字符串
$interval = DateInterval::createFromDateString('1 year');
$interval = DateInterval::createFromDateString('2 months');
$interval = DateInterval::createFromDateString('3 days');
$interval = DateInterval::createFromDateString('4 hours');
$interval = DateInterval::createFromDateString('-1 week');
$interval = DateInterval::createFromDateString('next monday');
// 注意:createFromDateString 返回 DateInterval | false
$interval = DateInterval::createFromDateString('invalid string');
if ($interval === false) {
echo "无法解析" . PHP_EOL;
}从日期差值获取
php
<?php
declare(strict_types=1);
$dt1 = new DateTime('2024-01-15');
$dt2 = new DateTime('2025-03-20');
$diff = $dt1->diff($dt2);
echo "年: {$diff->y}, 月: {$diff->m}, 天: {$diff->d}" . PHP_EOL;
echo "总天数: {$diff->days}" . PHP_EOL;
echo "总月数: " . ($diff->y * 12 + $diff->m) . PHP_EOL;
// invert: 1 表示负方向
if ($diff->invert) {
echo "dt2 在 dt1 之前" . PHP_EOL;
}属性和方法
DateInterval 属性
php
<?php
declare(strict_types=1);
$interval = new DateInterval('P1Y2M3DT4H5M6S');
echo $interval->y . PHP_EOL; // 年: 1
echo $interval->m . PHP_EOL; // 月: 2
echo $interval->d . PHP_EOL; // 天: 3
echo $interval->h . PHP_EOL; // 小时: 4
echo $interval->i . PHP_EOL; // 分钟: 5
echo $interval->s . PHP_EOL; // 秒: 6
echo $interval->f . PHP_EOL; // 微秒: 0
echo $interval->invert . PHP_EOL; // 方向: 0
echo $interval->days . PHP_EOL; // 总天数(仅从 diff 获取时有值): null格式化输出
php
<?php
declare(strict_types=1);
$interval = new DateInterval('P1Y2M3DT4H5M6S');
// format() - 格式化输出
echo $interval->format('%Y年 %M月 %D天 %H时 %I分 %S秒') . PHP_EOL;
echo $interval->format('总天数: %a') . PHP_EOL;
// 格式字符:
// %Y - 年 %M - 月 %D - 天
// %H - 小时 %I - 分钟 %S - 秒
// %R - 符号(- 表示负)
// %a - 总天数
// %d - 不足 1 月的天数
// %m - 不足 1 年的月数
// %h - 不足 1 天的小时数
// PHP 8.2+ 使用 __toString()
echo $interval . PHP_EOL; // P1Y2M3DT4H5M6S修改和比较
反转间隔
php
<?php
declare(strict_types=1);
$interval = new DateInterval('P1M');
$inverted = clone $interval;
$inverted->invert = 1;
// 使用 invert 参数
$dt = new DateTime('2024-01-15');
echo $dt->sub($interval)->format('Y-m-d') . PHP_EOL; // 2023-12-15
echo $dt->add($inverted)->format('Y-m-d') . PHP_EOL; // 2023-12-15PHP 8.2+ Static 构造器
php
<?php
declare(strict_types=1);
// PHP 8.2+ DateInterval 构造器改进
$interval = DateInterval::createFromDateString('1 month');
// PHP 8.4 可以使用 static 构造
$interval = new DateInterval(interval_spec: 'P1M');实战示例
DatePeriod 遍历日期范围
php
<?php
declare(strict_types=1);
$start = new DateTimeImmutable('2024-01-01');
$end = new DateTimeImmutable('2024-01-31');
$interval = new DateInterval('P1D');
// 创建日期范围
$period = new DatePeriod($start, $interval, $end);
foreach ($period as $day) {
$weekday = $day->format('N');
if ($weekday <= 5) {
echo $day->format('Y-m-d (周{$weekday})') . " - 工作日" . PHP_EOL;
}
}
// 带倒计数的日期范围
$period = new DatePeriod($start, $interval, 7, DatePeriod::INCLUDE_END_DATE);
foreach ($period as $i => $day) {
echo "第" . ($i + 1) . "天: " . $day->format('Y-m-d') . PHP_EOL;
}
// ISO 8601 递归规则
$period = new DatePeriod('R4/2024-01-01T00:00:00Z/P7D');
foreach ($period as $day) {
echo $day->format('Y-m-d') . PHP_EOL;
}注意事项
days属性仅在通过diff()创建的 DateInterval 中可用invert属性为 1 时表示负方向- 使用
format()输出时注意大小写区分
下一节
继续学习:DateTimeZone 时区处理