Я обнаружил, что объект DateTime в PHP можно сравнить с другим, поскольку операторы «>» и «<» перегружены.
То же самое с 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 (ложь) BOOL (ложь) BOOL (ложь) BOOL (ложь) object (DateInterval) # 1 (8) {...} object (DateInterval) # 2 (8) {...} BOOL (ложь) BOOL (истина) BOOL (истина) BOOL (ложь) BOOL (ложь) BOOL (истина) BOOL (истина) BOOL (ложь) object (DateInterval) # 5 (8) {...} BOOL (ложь) BOOL (истина) BOOL (истина) BOOL (ложь)
Короче говоря, сравнение объектов DateInterval
по умолчанию не поддерживается (по php 5.6).
Как вы уже знаете, объекты DateTime
сопоставимы.
Способ достижения желаемого результата состоит в том, чтобы вычесть или добавить DateInterval
из объекта DateTime
и сравнить эти два, чтобы определить разницу.
Пример: https://3v4l.org/XeSJe
$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, благодаря дополнительной поддержке микросекунд .
Пример: https://3v4l.org/rCigC
$a = new \DateTime; $b = new \DateTime; var_dump($a < $b);
Результаты (7.1+) :
bool(false)
Результаты (5.x – 7.0.x, 7.1.3) :
bool(true)
Чтобы обойти это поведение, рекомендуется использовать clone
для сравнения объектов DateTime
.
Пример: https://3v4l.org/CSpV8
$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 был рассчитан, мы знаем, что это точно, поэтому мы будем использовать это. Если нет, нам нужно сделать предположение о длине месяца и года, что не всегда является хорошей идеей. Я определил месяцы как 30 дней и лет как 365 дней полностью из воздуха, так как у меня нет спецификации ISO 8601, чтобы проверить, есть ли стандартное предположение, но мы можем на самом деле хотеть ошибиться, если мы надеемся У вас есть $ this-> days.
Вот пример. Обратите внимание, что если вам нужно сравнить DateInterval
который был возвращен из какого-либо другого вызова, сначала вам необходимо создать DateInterval
, если вы хотите использовать его в качестве источника сравнения.
$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; } }
Нет, сейчас это невозможно, и этого никогда не будет. Существует принципиальная проблема при сравнении двух DateInterval
.
DateInterval
относительна, а DateTime
является абсолютной: P1D
означает 1 день, поэтому вы думаете, что это означает (24 * 60 * 60) 86,400 секунд. Но из-за прыжка второй это не всегда так.
Это похоже на редкую ситуацию, не забывайте, что сравнивать месяцы с днями еще сложнее:
P1M и P30D – какой из них больше? это P1M, хотя я в настоящее время в феврале? Или это P30D, хотя я в настоящее время в августе? Что относительно PT24H30M и P1D? https://bugs.php.net/bug.php?id=49914#1490336933
Откуда $aujourdhui
? Конечно, это то же самое, что и $today
лингвистически, но PHP этого не знает! Изменение кода для использования $today
будет печатать "oK"
!
Если не определено, $aujourdhui->diff($release)
будет оцениваться до 0
если ваш PHP-интерпретатор не прерывается с ошибкой (мой делает).
Я использовал следующее обходное сравнение, сравнивающее DateIntervals:
version_compare(join('.', (array) $dateIntervalA), join('.', (array) $dateIntervalB));