art*_*gis 27 php datetime dateinterval
我发现PHP中的DateTime对象可以与另一个对象进行比较,因为">"和"<"运算符被重载.
和DateInterval一样吗?
当我试图回答这个问题时,我发现了一些奇怪的东西:
<?php
$today = new DateTime();
$release = new DateTime('14-02-2012');
$building_time = new DateInterval('P15D');
var_dump($today->diff($release));
var_dump($building_time);
var_dump($today->diff($release)>$building_time);
var_dump($today->diff($release)<$building_time);
if($today->diff($release) < $building_time){
echo 'oK';
}else{
echo 'Just a test';
}
Run Code Online (Sandbox Code Playgroud)
它总是呼应"只是一个测试".var_dump输出是:
object(DateInterval)#4 (8) {
["y"]=>
int(0)
["m"]=>
int(0)
["d"]=>
int(18)
["h"]=>
int(16)
["i"]=>
int(49)
["s"]=>
int(19)
["invert"]=>
int(1)
["days"]=>
int(18)
}
object(DateInterval)#3 (8) {
["y"]=>
int(0)
["m"]=>
int(0)
["d"]=>
int(15)
["h"]=>
int(0)
["i"]=>
int(0)
["s"]=>
int(0)
["invert"]=>
int(0)
["days"]=>
bool(false)
}
bool(false)
bool(true)
Run Code Online (Sandbox Code Playgroud)
当我尝试使用DateTime作为"01-03-2012"时,一切正常.
fyr*_*rye 13
简而言之,DateInterval默认情况下目前不支持比较对象(从PHP 5.6开始).
正如您所知,DateTime对象具有可比性.
实现期望结果的方法是DateInterval从DateTime对象中减去或添加并比较两者以确定差异.
$buildDate = new DateTime();
$releaseDate = clone $buildDate;
$releaseDate->modify('2012-02-14');
$buildDate->add(new DateInterval('P15D'));
var_dump($releaseDate < $buildDate); //bool(true)
Run Code Online (Sandbox Code Playgroud)
编辑
自PHP 7.1发布以来,由于增加了对微秒的支持,因此结果与PHP 5.x不同.
$a = new \DateTime;
$b = new \DateTime;
var_dump($a < $b);
Run Code Online (Sandbox Code Playgroud)
结果(7.1+):
bool(true)
Run Code Online (Sandbox Code Playgroud)
结果(5.x - 7.0.x,7.1.3):
bool(false)
Run Code Online (Sandbox Code Playgroud)
为避免此行为,建议您使用clone比较DateTime对象.
$a = new \DateTime;
$b = clone $a;
var_dump($a < $b);
Run Code Online (Sandbox Code Playgroud)
结果(5.x - 7.x):
bool(true)
Run Code Online (Sandbox Code Playgroud)
Tim*_*tle 11
看起来有一个相关的错误/功能请求,不确定是否曾在主干中.它没有记录(我可以找到)任何一种方式 - 所以可能不安全使用.
也就是说,经过一些测试后,似乎可以对它们进行比较,但只有在它们以某种方式被"评估"之后(进行var转储会改变结果).这是我的测试/结果:
<?php
$int15 = new DateInterval('P15D');
$int20 = new DateInterval('P20D');
var_dump($int15 > $int20); //should be false;
var_dump($int20 > $int15); //should be true;
var_dump($int15 < $int20); //should be true;
var_dump($int20 < $int15); //should be false;
var_dump($int15);
var_dump($int20);
var_dump($int15 > $int20); //should be false;
var_dump($int20 > $int15); //should be true;
var_dump($int15 < $int20); //should be true;
var_dump($int20 < $int15); //should be false;
$date = new DateTime();
$diff = $date->diff(new DateTime("+10 days"));
var_dump($int15 < $diff); //should be false;
var_dump($diff < $int15); //should be true;
var_dump($int15 > $diff); //should be true;
var_dump($diff > $int15); //should be false;
var_dump($diff);
var_dump($int15 < $diff); //should be false;
var_dump($diff < $int15); //should be true;
var_dump($int15 > $diff); //should be true;
var_dump($diff > $int15); //should be false;
Run Code Online (Sandbox Code Playgroud)
结果(我省略了间隔对象的完整转储):
bool(false)
bool(false)
bool(false)
bool(false)
object(DateInterval)#1 (8) {...}
object(DateInterval)#2 (8) {...}
bool(false)
bool(true)
bool(true)
bool(false)
bool(false)
bool(true)
bool(true)
bool(false)
object(DateInterval)#5 (8) {...}
bool(false)
bool(true)
bool(true)
bool(false)
编辑:
class ComparableDateInterval extends DateInterval
{
/**
* Leap-year safe comparison of DateInterval objects.
*/
public function compare(DateInterval $oDateInterval)
{
$fakeStartDate1 = date_create();
$fakeStartDate2 = clone $fakeStartDate1;
$fakeEndDate1 = $fakeStartDate1->add($this);
$fakeEndDate2 = $fakeStartDate2->add($oDateInterval);
if($fakeEndDate1 < $fakeEndDate2) {
return -1;
} elseif($fakeEndDate1 == $fakeEndDate2) {
return 0;
}
return 1;
}
}
$int15 = new ComparableDateInterval('P15D');
$int20 = new ComparableDateInterval('P20D');
var_dump($int15->compare($int20) == -1); // should be true;
Run Code Online (Sandbox Code Playgroud)
请参阅@ fyrye的答案,了解其基本原理(并提出它!).我原来的答案没有安全地处理闰年.
原始答案
虽然我对这个问题表示赞同,但我还是接受了接受的答案.那是因为它在我的任何PHP安装中都不适用于我,因为从根本上它正在内部破坏的东西上.
我所做的是迁移上面从未成为主干的补丁.FWIW我检查了最近的版本,PHP 5.6.5,补丁仍然不存在.端口代码很简单.唯一的问题就是如何进行比较
如果计算了$ this-> days,我们知道它是准确的,所以我们将使用它.如果没有,我们需要对月份和年份进行假设,这不一定是个好主意.由于我没有ISO 8601规范来检查是否有标准假设,因此我已将月份定义为30天和30天完全凭空,因为我们可能实际上想要错误没有$ this->天可用.
这是一个例子.请注意,如果你需要比较一个DateInterval是从一些其他的调用返回,你必须create一个ComparableDateInterval从它第一,如果你想用它作为比较的来源.
$int15 = new ComparableDateInterval('P15D');
$int20 = new ComparableDateInterval('P20D');
var_dump($int15->compare($int20) == -1); // should be true;
Run Code Online (Sandbox Code Playgroud)
这是代码
/**
* The stock DateInterval never got the patch to compare.
* Let's reimplement the patch in userspace.
* See the original patch at http://www.adamharvey.name/patches/DateInterval-comparators.patch
*/
class ComparableDateInterval extends DateInterval
{
static public function create(DateInterval $oDateInterval)
{
$oDi = new ComparableDateInterval('P1D');
$oDi->s = $oDateInterval->s;
$oDi->i = $oDateInterval->i;
$oDi->h = $oDateInterval->h;
$oDi->days = $oDateInterval->days;
$oDi->d = $oDateInterval->d;
$oDi->m = $oDateInterval->m;
$oDi->y = $oDateInterval->y;
$oDi->invert = $oDateInterval->invert;
return $oDi;
}
public function compare(DateInterval $oDateInterval)
{
$oMyTotalSeconds = $this->getTotalSeconds();
$oYourTotalSeconds = $oDateInterval->getTotalSeconds();
if($oMyTotalSeconds < $oYourTotalSeconds)
return -1;
elseif($oMyTotalSeconds == $oYourTotalSeconds)
return 0;
return 1;
}
/**
* If $this->days has been calculated, we know it's accurate, so we'll use
* that. If not, we need to make an assumption about month and year length,
* which isn't necessarily a good idea. I've defined months as 30 days and
* years as 365 days completely out of thin air, since I don't have the ISO
* 8601 spec available to check if there's a standard assumption, but we
* may in fact want to error out if we don't have $this->days available.
*/
public function getTotalSeconds()
{
$iSeconds = $this->s + ($this->i * 60) + ($this->h * 3600);
if($this->days > 0)
$iSeconds += ($this->days * 86400);
// @note Maybe you prefer to throw an Exception here per the note above
else
$iSeconds += ($this->d * 86400) + ($this->m * 2592000) + ($this->y * 31536000);
if($this->invert)
$iSeconds *= -1;
return $iSeconds;
}
}
Run Code Online (Sandbox Code Playgroud)
不,这现在不可能,以后也永远不可能。比较两个 有一个基本问题DateInterval。
ADateInterval是相对的,而 aDateTime是绝对的:P1D表示 1 天,因此您会认为这表示 (24*60*60) 86.400 秒。但由于闰秒,情况并非总是如此。
这看起来是一种罕见的情况,不要忘记将月与日进行比较更难:
P1M 和 P30D - 哪个更大?即使我现在在二月,它是 P1M 吗?或者即使我现在在八月,它也是 P30D 吗?PT24H30M 和 P1D 怎么样? https://bugs.php.net/bug.php?id=49914#1490336933