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';
}
それは常に「ただのテスト」をエコーします。 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)
DateTimeを「01-03-2012」として試してみると、すべてが機能します。
関連するバグ/機能のリクエスト があったようですが、それがトランクに到達したかどうかはわかりません。どちらの方法でも(私が見つけることができる)文書化されていないので、おそらく安全に使用することはできません。
とは言うものの、いくつかのテストの後、それらを比較できるように見えますが、何らかの方法で「評価」された後でのみです(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;
結果(間隔オブジェクトの完全なダンプを省略しました):
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)
つまり、DateInterval
オブジェクトの比較は、現在デフォルトではサポートされていません(php 5.6以降)。
すでにご存知のように、DateTime
オブジェクトは同等です。
目的の結果を得る方法は、DateInterval
オブジェクトからDateTime
を減算または加算し、2つを比較して違いを判断することです。
$buildDate = new DateTime();
$releaseDate = clone $buildDate;
$releaseDate->modify('2012-02-14');
$buildDate->add(new DateInterval('P15D'));
var_dump($releaseDate < $buildDate); //bool(true)
編集
PHP 7.1のリリース時点では、結果はPHP 5.xの場合とは異なります。これは、 マイクロ秒のサポートが追加された s。
$a = new \DateTime;
$b = new \DateTime;
var_dump($a < $b);
結果(7.1 +):
bool(true)
結果(5.x-7.0.x、7.1.3):
bool(false)
この動作を回避するには、代わりにclone
を使用してDateTime
オブジェクトを比較することをお勧めします。
$a = new \DateTime;
$b = clone $a;
var_dump($a < $b);
結果(5.x-7.x):
bool(true)
編集:
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;
理論的根拠については@fyryeの回答を参照してください(そして賛成してください!)。私の最初の答えはうるう年を安全に扱っていませんでした。
元の回答
私はこの質問に賛成しましたが、受け入れられた答えには反対しました。これは、私のPHPインストールのいずれでも機能しなかったためであり、基本的に、内部で壊れたものに影響を与えているためです。
代わりに私がしたのは移行です 前述のパッチ これはトランクになりませんでした。 FWIW最近のリリース PHP 5.6.5 を確認しましたが、パッチがまだありません。コードは移植するのに簡単でした。唯一のことは、それがどのように比較を行うかについての警告です
$ this-> daysが計算されている場合、それが正確であることがわかっているので、それを使用します。そうでない場合は、月と年の長さについて仮定する必要がありますが、これは必ずしも良い考えではありません。標準的な仮定があるかどうかを確認するためのISO8601仕様がないため、月を30日、年を365日と定義しましたが、実際には、エラーが発生する可能性があります。 $ this-> daysを利用できません。
これが例です。他の呼び出しから返されたDateInterval
を比較する必要がある場合、それを使用する場合は、最初にcreate
a ComparableDateInterval
を実行する必要があることに注意してください。比較のソースとして。
$int15 = new ComparableDateInterval('P15D');
$int20 = new ComparableDateInterval('P20D');
var_dump($int15->compare($int20) == -1); // should be true;
これがコードです
/**
* 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;
}
}
いいえ、これは現在不可能であり、今後も不可能になります。 2つのDateInterval
の比較には根本的な問題があります。
DateInterval
は相対的ですが、DateTime
は絶対的です:P1D
は1日を意味するので、(24 * 60 * 60)86.400秒を意味すると思います。しかし、 うるう秒 のため、常にそうであるとは限りません。
これはまれな状況のように見えます。月と日を比較するのはさらに難しいことを忘れないでください。
P1MとP30D-どちらが大きいですか?私は現在2月ですが、それはP1Mですか?それとも、私が現在8月にいるのに、それはP30Dですか? PT24H30MとP1Dはどうですか? https://bugs.php.net/bug.php?id=49914#14903369
1か月以内の時間間隔で作業している場合は、2つの間隔を秒に変換して比較するのは簡単です。 $dateInterval->format("%s")
は秒コンポーネントのみを返すため、これを行うことになりました。
function intervalToSeconds($dateInterval) {
$s = (
($dateInterval->format("%d")*24*60*60) +
($dateInterval->format("%h")*60*60) +
($dateInterval->format("%i")*60) +
$dateInterval->format("%s")
);
return $s;
}