263 lines
7.9 KiB
PHP
263 lines
7.9 KiB
PHP
<?php
|
|
|
|
namespace Safe;
|
|
|
|
use DateInterval;
|
|
use DateTime;
|
|
use DateTimeInterface;
|
|
use DateTimeZone;
|
|
use Safe\Exceptions\DatetimeException;
|
|
|
|
/**
|
|
* This class is used to implement a safe version of the DatetimeImmutable class.
|
|
* While it technically overloads \DateTimeImmutable for typehint compatibility,
|
|
* it is actually used as a wrapper of \DateTimeImmutable, mostly to be able to overwrite functions like getTimestamp() while still being able to edit milliseconds via setTime().
|
|
*/
|
|
class DateTimeImmutable extends \DateTimeImmutable
|
|
{
|
|
/**
|
|
* @var \DateTimeImmutable
|
|
*/
|
|
private $innerDateTime;
|
|
|
|
/**
|
|
* DateTimeImmutable constructor.
|
|
* @param string $time
|
|
* @param DateTimeZone|null $timezone
|
|
* @throws \Exception
|
|
*/
|
|
public function __construct($time = 'now', $timezone = null)
|
|
{
|
|
parent::__construct($time, $timezone);
|
|
$this->innerDateTime = new parent($time, $timezone);
|
|
}
|
|
|
|
//switch between regular datetime and safe version
|
|
public static function createFromRegular(\DateTimeImmutable $datetime): self
|
|
{
|
|
$safeDatetime = new self($datetime->format('Y-m-d H:i:s.u'), $datetime->getTimezone()); //we need to also update the wrapper to not break the operators '<' and '>'
|
|
$safeDatetime->innerDateTime = $datetime; //to make sure we don't lose information because of the format().
|
|
return $safeDatetime;
|
|
}
|
|
|
|
//usefull if you need to switch back to regular DateTimeImmutable (for example when using DatePeriod)
|
|
public function getInnerDateTime(): \DateTimeImmutable
|
|
{
|
|
return $this->innerDateTime;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// overload functions with false errors
|
|
|
|
/**
|
|
* @param string $format
|
|
* @param string $time
|
|
* @param DateTimeZone|null $timezone
|
|
* @throws DatetimeException
|
|
*/
|
|
public static function createFromFormat($format, $time, $timezone = null): self
|
|
{
|
|
$datetime = parent::createFromFormat($format, $time, $timezone);
|
|
if ($datetime === false) {
|
|
throw DatetimeException::createFromPhpError();
|
|
}
|
|
return self::createFromRegular($datetime);
|
|
}
|
|
|
|
/**
|
|
* @param string $format
|
|
* @return string
|
|
* @throws DatetimeException
|
|
*/
|
|
public function format($format): string
|
|
{
|
|
/** @var string|false $result */
|
|
$result = $this->innerDateTime->format($format);
|
|
if ($result === false) {
|
|
throw DatetimeException::createFromPhpError();
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* @param DateTimeInterface $datetime2
|
|
* @param bool $absolute
|
|
* @return DateInterval
|
|
* @throws DatetimeException
|
|
*/
|
|
public function diff($datetime2, $absolute = false): DateInterval
|
|
{
|
|
/** @var \DateInterval|false $result */
|
|
$result = $this->innerDateTime->diff($datetime2, $absolute);
|
|
if ($result === false) {
|
|
throw DatetimeException::createFromPhpError();
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* @param string $modify
|
|
* @return DateTimeImmutable
|
|
* @throws DatetimeException
|
|
*/
|
|
public function modify($modify): self
|
|
{
|
|
/** @var \DateTimeImmutable|false $result */
|
|
$result = $this->innerDateTime->modify($modify);
|
|
if ($result === false) {
|
|
throw DatetimeException::createFromPhpError();
|
|
}
|
|
return self::createFromRegular($result); //we have to recreate a safe datetime because modify create a new instance of \DateTimeImmutable
|
|
}
|
|
|
|
/**
|
|
* @param int $year
|
|
* @param int $month
|
|
* @param int $day
|
|
* @return DateTimeImmutable
|
|
* @throws DatetimeException
|
|
*/
|
|
public function setDate($year, $month, $day): self
|
|
{
|
|
/** @var \DateTimeImmutable|false $result */
|
|
$result = $this->innerDateTime->setDate($year, $month, $day);
|
|
if ($result === false) {
|
|
throw DatetimeException::createFromPhpError();
|
|
}
|
|
return self::createFromRegular($result); //we have to recreate a safe datetime because modify create a new instance of \DateTimeImmutable
|
|
}
|
|
|
|
/**
|
|
* @param int $year
|
|
* @param int $week
|
|
* @param int $day
|
|
* @return DateTimeImmutable
|
|
* @throws DatetimeException
|
|
*/
|
|
public function setISODate($year, $week, $day = 1): self
|
|
{
|
|
/** @var \DateTimeImmutable|false $result */
|
|
$result = $this->innerDateTime->setISODate($year, $week, $day);
|
|
if ($result === false) {
|
|
throw DatetimeException::createFromPhpError();
|
|
}
|
|
return self::createFromRegular($result); //we have to recreate a safe datetime because modify create a new instance of \DateTimeImmutable
|
|
}
|
|
|
|
/**
|
|
* @param int $hour
|
|
* @param int $minute
|
|
* @param int $second
|
|
* @param int $microseconds
|
|
* @return DateTimeImmutable
|
|
* @throws DatetimeException
|
|
*/
|
|
public function setTime($hour, $minute, $second = 0, $microseconds = 0): self
|
|
{
|
|
/** @var \DateTimeImmutable|false $result */
|
|
$result = $this->innerDateTime->setTime($hour, $minute, $second, $microseconds);
|
|
if ($result === false) {
|
|
throw DatetimeException::createFromPhpError();
|
|
}
|
|
return self::createFromRegular($result);
|
|
}
|
|
|
|
/**
|
|
* @param int $unixtimestamp
|
|
* @return DateTimeImmutable
|
|
* @throws DatetimeException
|
|
*/
|
|
public function setTimestamp($unixtimestamp): self
|
|
{
|
|
/** @var \DateTimeImmutable|false $result */
|
|
$result = $this->innerDateTime->setTimestamp($unixtimestamp);
|
|
if ($result === false) {
|
|
throw DatetimeException::createFromPhpError();
|
|
}
|
|
return self::createFromRegular($result);
|
|
}
|
|
|
|
/**
|
|
* @param DateTimeZone $timezone
|
|
* @return DateTimeImmutable
|
|
* @throws DatetimeException
|
|
*/
|
|
public function setTimezone($timezone): self
|
|
{
|
|
/** @var \DateTimeImmutable|false $result */
|
|
$result = $this->innerDateTime->setTimezone($timezone);
|
|
if ($result === false) {
|
|
throw DatetimeException::createFromPhpError();
|
|
}
|
|
return self::createFromRegular($result);
|
|
}
|
|
|
|
/**
|
|
* @param DateInterval $interval
|
|
* @return DateTimeImmutable
|
|
* @throws DatetimeException
|
|
*/
|
|
public function sub($interval): self
|
|
{
|
|
/** @var \DateTimeImmutable|false $result */
|
|
$result = $this->innerDateTime->sub($interval);
|
|
if ($result === false) {
|
|
throw DatetimeException::createFromPhpError();
|
|
}
|
|
return self::createFromRegular($result);
|
|
}
|
|
|
|
/**
|
|
* @throws DatetimeException
|
|
*/
|
|
public function getOffset(): int
|
|
{
|
|
/** @var int|false $result */
|
|
$result = $this->innerDateTime->getOffset();
|
|
if ($result === false) {
|
|
throw DatetimeException::createFromPhpError();
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
//overload getters to use the inner datetime immutable instead of itself
|
|
|
|
/**
|
|
* @param DateInterval $interval
|
|
* @return DateTimeImmutable
|
|
*/
|
|
public function add($interval): self
|
|
{
|
|
return self::createFromRegular($this->innerDateTime->add($interval));
|
|
}
|
|
|
|
/**
|
|
* @param DateTime $dateTime
|
|
* @return DateTimeImmutable
|
|
*/
|
|
public static function createFromMutable($dateTime): self
|
|
{
|
|
return self::createFromRegular(parent::createFromMutable($dateTime));
|
|
}
|
|
|
|
/**
|
|
* @param mixed[] $array
|
|
* @return DateTimeImmutable
|
|
*/
|
|
public static function __set_state($array): self
|
|
{
|
|
return self::createFromRegular(parent::__set_state($array));
|
|
}
|
|
|
|
public function getTimezone(): DateTimeZone
|
|
{
|
|
return $this->innerDateTime->getTimezone();
|
|
}
|
|
|
|
public function getTimestamp(): int
|
|
{
|
|
return $this->innerDateTime->getTimestamp();
|
|
}
|
|
}
|