31

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)

「01-03-2012」としてDateTimeを試してみると、すべてが機能します。

4

7 に答える 7

19

つまり、オブジェクトの比較はDateInterval現在デフォルトではサポートされていません (php 5.6 以降)。

ご存知のように、DateTimeオブジェクトは比較可能です。

DateInterval目的の結果を得る方法は、オブジェクトからを減算または加算しDateTime、2 つを比較して違いを判断することです。

例: https://3v4l.org/kjJPg

$buildDate = new DateTime('2012-02-15');
$releaseDate = clone $buildDate;
$releaseDate->setDate(2012, 2, 14);
$buildDate->add(new DateInterval('P15D'));

var_dump($releaseDate < $buildDate); //bool(true)

編集

PHP 7.1 のリリース時点では、マイクロ秒のサポートが追加されたため、結果は PHP 5.x とは異なります。

例: https://3v4l.org/rCigC

$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

例: https://3v4l.org/CSpV8

$a = new \DateTime;
$b = clone $a;
var_dump($a < $b);

結果(5.x - 7.x) :

bool(false)
于 2015-02-24T16:18:42.463 に答える
12

関連するバグ/機能リクエストがあったようですが、それがトランクに届いたかどうかはわかりません。いずれにしても(私が見つけることができる)文書化されていないため、おそらく安全に使用できません。

とはいえ、いくつかのテストの後、それらを比較できるように見えますが、何らかの方法で「評価」された後でのみです (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(偽)
bool(偽)
bool(偽)
bool(偽)
object(DateInterval)#1 (8) {...}
object(DateInterval)#2 (8) {...}
bool(偽)
ブール(真)
ブール(真)
bool(偽)

bool(偽)
ブール(真)
ブール(真)
bool(偽)
object(DateInterval)#5 (8) {...}
bool(偽)
ブール(真)
ブール(真)
bool(偽)
于 2012-03-03T17:19:54.013 に答える
8

編集:

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 が計算されている場合、それが正確であることがわかっているので、それを使用します。そうでない場合は、月と年の長さについて仮定する必要がありますが、これは必ずしも良い考えではありません。標準的な仮定があるかどうかを確認するための ISO 8601 仕様を入手できないため、月を 30 日、年を 365 日と完全に定義しましたが、そうでない場合は実際にエラーを出したい場合があります。 $this->days が利用可能ではありません。

これが例です。DateInterval他の呼び出しから返された a を比較する必要がある場合、それを比較のソースとして使用する場合は、まずそれからcreateaを取得する必要があることに注意してください。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;
    }
}
于 2015-02-09T20:43:23.007 に答える
-1

どこ$aujourdhuiから来たの?確かにそれは言語的には同じ$todayですが、PHPはそれを知りません!使用するコードを変更すると、$today印刷されます"oK"

定義されていない場合、PHPインタープリターがエラーで中止されないかどうか$aujourdhui->diff($release)を評価します(私の場合は中止します)。0

于 2012-03-03T17:05:02.130 に答える
-3

DateIntervals を比較する次の回避策を使用しました。

version_compare(join('.', (array) $dateIntervalA), join('.', (array) $dateIntervalB));
于 2012-08-07T21:17:01.950 に答える