PHP Conference Japan 2024

DateInterval::__construct

(PHP 5 >= 5.3.0, PHP 7, PHP 8)

DateInterval::__construct创建一个新的 DateInterval 对象

描述

public DateInterval::__construct(string $duration)

创建一个新的 DateInterval 对象。

参数

duration

一个时间间隔规范。

格式以字母 P 开头,表示“周期”。每个持续时间周期由一个整数值和一个周期指示符表示。如果持续时间包含时间元素,则规范的该部分以字母 T 开头。

duration 周期指示符
周期指示符 描述
Y
M
D
W 周。转换为天数。在 PHP 8.0.0 之前,不能与 D 结合使用。
H 小时
M 分钟
S

以下是一些简单的例子。两天是 P2D。两秒是 PT2S。六年五分钟是 P6YT5M

注意:

单位类型必须从左侧最大的比例单位输入到右侧最小的比例单位。所以年份在月份之前,月份在日期之前,日期在分钟之前,等等。因此,一年四天必须表示为 P1Y4D,而不是 P4D1Y

规范也可以表示为日期时间。一年四天的示例为 P0001-00-04T00:00:00。但是,此格式中的值不能超过给定周期的翻转点(例如,25 小时无效)。

这些格式基于 » ISO 8601 持续时间规范

错误/异常

duration 不能被解析为时间间隔时,抛出 DateMalformedIntervalStringException 异常。在 PHP 8.3 之前,这是 Exception

变更日志

版本 描述
8.3.0 现在抛出 DateMalformedIntervalStringException 而不是 Exception
8.2.0 只有 yfinvertdays 将可见,包括一个新的 from_string 布尔属性。
8.0.0 W 可以与 D 结合使用。

示例

示例 #1 构造和使用 DateInterval 对象

<?php
// 创建一个特定日期
$someDate = \DateTime::createFromFormat("Y-m-d H:i", "2022-08-25 14:18");

// 创建时间间隔
$interval = new \DateInterval("P7D");

// 添加时间间隔
$someDate->add($interval);

// 将时间间隔转换为字符串
echo $interval->format("%d");

以上示例将输出


7

示例 #2 DateInterval 示例

<?php

$interval
= new DateInterval('P1W2D');
var_dump($interval);

?>

PHP 8.2 中以上示例的输出

object(DateInterval)#1 (10) {
  ["y"]=>
  int(0)
  ["m"]=>
  int(0)
  ["d"]=>
  int(9)
  ["h"]=>
  int(0)
  ["i"]=>
  int(0)
  ["s"]=>
  int(0)
  ["f"]=>
  float(0)
  ["invert"]=>
  int(0)
  ["days"]=>
  bool(false)
  ["from_string"]=>
  bool(false)
}

PHP 8 中以上示例的输出

object(DateInterval)#1 (16) {
  ["y"]=>
  int(0)
  ["m"]=>
  int(0)
  ["d"]=>
  int(9)
  ["h"]=>
  int(0)
  ["i"]=>
  int(0)
  ["s"]=>
  int(0)
  ["f"]=>
  float(0)
  ["weekday"]=>
  int(0)
  ["weekday_behavior"]=>
  int(0)
  ["first_last_day_of"]=>
  int(0)
  ["invert"]=>
  int(0)
  ["days"]=>
  bool(false)
  ["special_type"]=>
  int(0)
  ["special_amount"]=>
  int(0)
  ["have_weekday_relative"]=>
  int(0)
  ["have_special_relative"]=>
  int(0)
}

PHP 7 中以上示例的输出

object(DateInterval)#1 (16) {
  ["y"]=>
  int(0)
  ["m"]=>
  int(0)
  ["d"]=>
  int(2)
  ["h"]=>
  int(0)
  ["i"]=>
  int(0)
  ["s"]=>
  int(0)
  ["f"]=>
  float(0)
  ["weekday"]=>
  int(0)
  ["weekday_behavior"]=>
  int(0)
  ["first_last_day_of"]=>
  int(0)
  ["invert"]=>
  int(0)
  ["days"]=>
  bool(false)
  ["special_type"]=>
  int(0)
  ["special_amount"]=>
  int(0)
  ["have_weekday_relative"]=>
  int(0)
  ["have_special_relative"]=>
  int(0)
}

参见

添加注释

用户贡献的注释 14 条注释

owen at beliefs.com
11 年前
M 用于表示月份和分钟。

如 ISO 6801 的参考维基百科页面 http://en.wikipedia.org/wiki/Iso8601#Durations 所述

为了解决歧义,“P1M”是一月的持续时间,“PT1M”是一分钟的持续时间(注意时间指示符 T,它位于时间值之前)。

使用:PHP 5.3.2-1ubuntu4.19

// 3 个月
$dateTime = new DateTime;echo $dateTime->format( DateTime::ISO8601 ), PHP_EOL;
$dateTime->add(new DateInterval("P3M"));
echo $dateTime->format( DateTime::ISO8601 ), PHP_EOL;
结果是
2013-07-11T11:12:26-0400
2013-10-11T11:12:26-0400

// 3 分钟
$dateTime = new DateTime;echo $dateTime->format( DateTime::ISO8601 ), PHP_EOL;
$dateTime->add(new DateInterval("PT3M"));
echo $dateTime->format( DateTime::ISO8601 ), PHP_EOL;
结果是
2013-07-11T11:12:42-0400
2013-07-11T11:15:42-0400

在时间间隔中的 P 之后插入 T 以添加 3 分钟而不是 3 个月。
Hernanibus
7 年前
文档中未说明,但不能直接创建负时间间隔,即不能创建“-2 天”的时间间隔,例如:

<?
$interval = new DateInterval("P-2D");//或
$interval = new DateInterval("-P2D");
?>

相反,必须先创建时间间隔,然后将其 'invert' 属性设置为 1,即:

<?
$interval = new DateInterval("P2D");
$interval->invert = 1;
?>

需要注意的是,此区间值作为负数处理,因此从给定日期中减去区间值需要“加”上它。

<?
$interval = new DateInterval("P2D");
$interval->invert = 1;
$date = new DateTime ("1978-01-23 17:46:00");
$date->add($interval)->format("Y-m-d H:i:s");//结果是 "1978-01-21 17:46:00"
?>
kuzb
13年前
需要注意的是,此类不会根据单一时间单位的值计算日/小时/分钟/秒等。例如

<?php
$di
= new DateInterval('PT3600S');
echo
$di->format('%H:%i:%s');

?>

将会输出 0:0:3600,而不是预期的 1:0:0
admin at torntech dot com
9年前
警告 - 尽管 $interval_spec 接受 ISO 8601 规范格式,但它不接受规范中规定的带有句点或逗号的小数部分值。

https://bugs.php.net/bug.php?id=53831

<?php
/* ISO 8601 文档中的示例 */
$interval = new DateInterval('P0.5Y');
?>

将会导致
致命错误:捕获到的异常 'Exception',消息为 'DateInterval::__construct(): Unknown or bad format (P0.5Y)'
buvinghausen at gmail dot com
12年前
我认为,如果直接使用 DateTime 类中的 sub 方法会更容易。

<?php
$date
= new DateTime();
$date->sub(new DateInterval("P89D"));
Anonymous
3年前
注意,即使周期为空,添加时间也必须输入 P。

添加 1 小时

<?php

$plusOneHour
= (new DateTime('now'))->add(new DateInterval("PT1H"));

var_dump($plusOneHour);

?>
kevinpeno at gmail dot com
13年前
注意,虽然 DateInterval 对象具有 $invert 属性,但不能像在 XSD 中指定负数 ("-P1Y") 一样直接向构造函数提供负数。这样做会引发异常。

相反,需要使用正区间 ("P1Y") 进行构造,并指定 $invert 属性 === 1。
daniellehr at gmx dot de
12年前
或者,您可以使用 DateInterval::createFromDateString() 来处理负区间

<?php
$date
= new DateTime();
$date->add(DateInterval::createFromDateString('-89 days'));
userexamplecom at mailinator dot com
8年前
注意,如果您有一个 1 月 31 日的 DateTime 对象,并添加一个月的 DateInterval,那么您将进入 3 月而不是 2 月。

例如
---
// 假设当前日期为 2017-01-31
$today = new DateTime('now', $timeZoneObject);
$today->add(new DateInterval('P1M'));
echo $today->format('m');
// 输出:03
---
jawzx01 at gmail dot com
12年前
如前所述,要创建一个负的 DateInterval 对象,您可以这样编码

<?php
$date1
= new DateTime();
$eightynine_days_ago = new DateInterval( "P89D" );
$eightynine_days_ago->invert = 1; //使其为负数。
$date1->add( $eightynine_days_ago );
?>

然后 $date1 就是 89 天前。
sloanlance+php.net gmail com
6年前
⚠️ 请记住“admin at torntech dot com”在之前的评论中对 DateInterval 提出的警告(https://php.net/manual/en/dateinterval.construct.php#116750)。重申一下

某些版本的 PHP(例如 5.6.31)存在一个 bug,不允许在作为 DateInterval 构造函数参数的 ISO 8601 持续时间字符串中使用小数部分。也就是说,以下示例将失败

<?php
// 'P0.5Y' 根据 ISO 8601 有效
$interval = new DateInterval('P0.5Y'); // 抛出异常
?>

<?php
// 'PT585.829S' 根据 ISO 8601 有效
$interval = new DateInterval('PT585.829S'); // 抛出异常
?>

如果此 bug 影响到您,请访问 PHP Bug Tracking System 中此 bug 的报告,并投票说明它影响到您:https://bugs.php.net/bug.php?id=53831
lsloan-php dot net at umich dot edu
8年前
尽管 PHP 将时间段称为“区间”,但 ISO 8601 将其称为“持续时间”。在 ISO 8601 中,“区间”是其他东西。

虽然 ISO 8601 允许持续时间的所有部分使用小数(例如,“P0.5Y”),但 DateInterval 则不允许。计算持续时间时请谨慎。如果持续时间有小数部分,则将其存储在 DateInterval 对象中时可能会丢失。
Ray.Paseur sometimes uses Gmail
8年前
要恢复区间规范字符串

<?php
function get_interval_spec(DateTime $alpha, DateTime $omega)
{
$intvl = $alpha->diff($omega);

$date = NULL;
if (
$intvl->y) $date .= $intvl->y . 'Y';
if (
$intvl->m) $date .= $intvl->m . 'M';
if (
$intvl->d) $date .= $intvl->d . 'D';

$time = NULL;
if (
$intvl->h) $time .= $intvl->h . 'H';
if (
$intvl->i) $time .= $intvl->i . 'M';
if (
$intvl->s) $time .= $intvl->s . 'S';
if (
$time) $time = 'T' . $time;

$text ='P' . $date . $time;
if (
$text == 'P') return 'PT0S';
return
$text;
}
grzeniufication
4年前
如果您想在数据库中持久化区间对象,实现 __toString() 方法可能会有用。格式化的区间值比 serialize 的输出更容易被人理解。这是一个例子

<?php

命名空间 App;

DateInterval 扩展 \DateInterval
{
公共函数
__toString()
{
返回
$this->format('P%yY%mM%dDT%hH%iM%sS');
}
}

$interval1 = 新 DateInterval('P1Y');
$interval2 = 新 DateInterval(strval($interval1));
assert($interval1 == $interval2);
To Top