Are PHP DateInterval comparable like DateTime?

前端 未结 7 2095
孤城傲影
孤城傲影 2020-12-03 13:36

I discovered that a DateTime object in PHP can be compared to another as the \">\" and \"<\" operators are overloaded.

Is it the same with DateInterval?

A

7条回答
  •  囚心锁ツ
    2020-12-03 14:04

    EDIT:

    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;
    

    See @fyrye's answer for the rationale (and upvote it!). My original answer did not deal with leap years safely.


    Original Answer

    While I upvoted this question, I downvoted the accepted answer. That's because it didn't work for me on any of my PHP installations and because fundamentally it's hinging on something broken internally.

    What I did instead is migrate the aforementioned patch which never made it into trunk. FWIW I checked a recent release, PHP 5.6.5, and the patch still isn't there. The code was trivial to port. The only thing is a warning in how it makes the comparison

    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.

    Here's an example. Note, if you need to compare a DateInterval that was returned from some other call, you'll have to create a ComparableDateInterval from it first, if you want to use it as the source of the comparison.

    $int15 = new ComparableDateInterval('P15D');
    $int20 = new ComparableDateInterval('P20D');
    
    var_dump($int15->compare($int20) == -1); // should be true;
    

    Here's the code

    /**
     * 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;
        }
    }
    

提交回复
热议问题