1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115:
<?php
/**
* This file is part of the phpcommon/comparison package.
*
* (c) Marcos Passos <marcos@marcospassos.com>
*
* For the full copyright and license information, please view the LICENSE file
* that was distributed with this source code.
*/
namespace PhpCommon\Comparison\Hasher;
use PhpCommon\Comparison\Equatable;
use PhpCommon\Comparison\Hasher;
use PhpCommon\Comparison\UnexpectedTypeException;
use DateTimeInterface as DateTime;
/**
* An equivalence relation to determine whether two instances of
* DateTime have the same date, time and time zone.
*
* @author Marcos Passos <marcos@croct.com>
*/
class DateTimeHasher implements Hasher
{
/**
* Checks whether the current relation is considered equal to another.
*
* Since this class is stateless, its instances will always be considered
* equal if they are of the same type.
*/
public function equals(Equatable $other)
{
return self::class === get_class($other);
}
/**
* Checks whether the given values are considered equivalent.
*
* This equivalence relation considers two instances of {@link DateTime} to
* be equivalent if they have the same date, time and time zone.
*
* @param DateTime $left The date/time to compare.
* @param mixed $right The value to compare.
*
* @return boolean Returns `true` if the date/time are considered
* equivalent, `false` otherwise.
*/
public function equivalent($left, $right)
{
$this->assertDateTime($left);
if (!$right instanceof DateTime) {
return false;
}
$leftTimezone = $left->getTimezone();
$rightTimezone = $right->getTimezone();
// Compare the date and time
if ($left->getTimestamp() !== $right->getTimestamp()) {
return false;
}
// Compare the timezone
return $leftTimezone->getName() === $rightTimezone->getName();
}
/**
* Returns a hash code for the given DateTime instance.
*
* The resulting hash code is guaranteed to be _coherent_ with the
* {@link equivalent()} method, which means that for any references
* `$x` and `$y`, if `equivalent($x, $y)`, then `hash($x) === hash($y)`.
* It is computed by summing the values returned by the methods
* {@link \DateTimeInterface::getTimestamp()} and
* {@link \DateTimeInterface::getOffset()}, as shown in the following
* expression:
*
* ```php
* $hashCode = $date->getTimestamp() + $date->getOffset();
* ```
*
* @param DateTime $value The date/time to compare.
*
* @return integer The hash code.
*
* @see equivalent()
*/
public function hash($value)
{
$this->assertDateTime($value);
$timezone = $value->getTimezone();
return $value->getTimestamp() + crc32($timezone->getName());
}
/**
* Asserts the given value is an instance of {@link DateTime}.
*
* @param mixed $value The value to assert.
*
* @throws UnexpectedTypeException If the given value is not an instance of
* {@link DateTime}.
*/
protected function assertDateTime($value)
{
if (!$value instanceof DateTime) {
throw UnexpectedTypeException::forType(DateTime::class, $value);
}
}
}