-
-
Notifications
You must be signed in to change notification settings - Fork 9.4k
Commit
DateTime
: an immutable implementation with strict error…
… handling and return types
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <fabien@symfony.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\Component\Clock; | ||
|
||
/** | ||
* An immmutable DateTime with stricter error handling and return types than the native one. | ||
* | ||
* @author Nicolas Grekas <p@tchwork.com> | ||
*/ | ||
final class DateTime extends \DateTimeImmutable | ||
{ | ||
/** | ||
* @throws \DateMalformedStringException When $datetime is invalid | ||
*/ | ||
public function __construct(string $datetime = 'now', \DateTimeZone $timezone = null, parent $reference = null) | ||
{ | ||
$now = $reference ?? Clock::get()->now(); | ||
|
||
if ('now' !== $datetime) { | ||
if (!$now instanceof static) { | ||
$now = static::createFromInterface($now); | ||
} | ||
|
||
if (\PHP_VERSION_ID < 80300) { | ||
try { | ||
$timezone = (new parent($datetime, $timezone ?? $now->getTimezone()))->getTimezone(); | ||
} catch (\Exception $e) { | ||
throw new \DateMalformedStringException($e->getMessage(), $e->getCode(), $e); | ||
} | ||
} else { | ||
$timezone = (new parent($datetime, $timezone ?? $now->getTimezone()))->getTimezone(); | ||
} | ||
|
||
$now = $now->setTimeZone($timezone)->modify($datetime); | ||
} elseif (null !== $timezone) { | ||
$now = $now->setTimezone($timezone); | ||
} | ||
|
||
if (\PHP_VERSION_ID < 80200) { | ||
$now = (array) $now; | ||
$this->date = $now['date']; | ||
Check failure on line 50 in src/Symfony/Component/Clock/DateTime.php GitHub Actions / PsalmUndefinedThisPropertyAssignment
|
||
$this->timezone_type = $now['timezone_type']; | ||
Check failure on line 51 in src/Symfony/Component/Clock/DateTime.php GitHub Actions / PsalmUndefinedThisPropertyAssignment
|
||
$this->timezone = $now['timezone']; | ||
Check failure on line 52 in src/Symfony/Component/Clock/DateTime.php GitHub Actions / PsalmUndefinedThisPropertyAssignment
|
||
$this->__wakeup(); | ||
|
||
return; | ||
} | ||
|
||
$this->__unserialize((array) $now); | ||
Check failure on line 58 in src/Symfony/Component/Clock/DateTime.php GitHub Actions / PsalmUndefinedMethod
|
||
} | ||
|
||
/** | ||
* @throws \DateMalformedStringException When $format or $datetime are invalid | ||
*/ | ||
public static function createFromFormat(string $format, string $datetime, \DateTimeZone $timezone = null): static | ||
Check failure on line 64 in src/Symfony/Component/Clock/DateTime.php GitHub Actions / PsalmMissingImmutableAnnotation
|
||
{ | ||
return parent::createFromFormat($format, $datetime, $timezone) ?: throw new \DateMalformedStringException(static::getLastErrors()['errors'][0] ?? 'Invalid date string or format.'); | ||
} | ||
|
||
public static function createFromInterface(\DateTimeInterface $object): static | ||
{ | ||
return parent::createFromInterface($object); | ||
} | ||
|
||
public static function createFromMutable(\DateTime $object): static | ||
Check failure on line 74 in src/Symfony/Component/Clock/DateTime.php GitHub Actions / PsalmMissingImmutableAnnotation
|
||
{ | ||
return parent::createFromMutable($object); | ||
} | ||
|
||
public function add(\DateInterval $interval): static | ||
Check failure on line 79 in src/Symfony/Component/Clock/DateTime.php GitHub Actions / PsalmMissingImmutableAnnotation
|
||
{ | ||
return parent::add($interval); | ||
} | ||
|
||
public function sub(\DateInterval $interval): static | ||
Check failure on line 84 in src/Symfony/Component/Clock/DateTime.php GitHub Actions / PsalmMissingImmutableAnnotation
|
||
{ | ||
return parent::sub($interval); | ||
} | ||
|
||
/** | ||
* @throws \DateMalformedStringException When $modifier is invalid | ||
*/ | ||
public function modify(string $modifier): static | ||
Check failure on line 92 in src/Symfony/Component/Clock/DateTime.php GitHub Actions / PsalmMissingImmutableAnnotation
|
||
{ | ||
if (\PHP_VERSION_ID < 80300) { | ||
return @parent::modify($modifier) ?: throw new \DateMalformedStringException(error_get_last()['message'] ?? sprintf('Invalid modifier: "%s".', $modifier)); | ||
} | ||
|
||
return parent::modify($modifier); | ||
Check failure on line 98 in src/Symfony/Component/Clock/DateTime.php GitHub Actions / PsalmFalsableReturnStatement
|
||
} | ||
|
||
public function setTimestamp(int $value): static | ||
{ | ||
return parent::setTimestamp($value); | ||
} | ||
|
||
public function setDate(int $year, int $month, int $day): static | ||
{ | ||
return parent::setDate($year, $month, $day); | ||
} | ||
|
||
public function setISODate(int $year, int $week, int $day = 1): static | ||
{ | ||
return parent::setISODate($year, $week, $day); | ||
} | ||
|
||
public function setTime(int $hour, int $minute, int $second = 0, int $microsecond = 0): static | ||
{ | ||
return parent::setTime($hour, $minute, $second, $microsecond); | ||
} | ||
|
||
public function setTimeZone(\DateTimeZone $timezone): static | ||
{ | ||
return parent::setTimeZone($timezone); | ||
} | ||
|
||
public function getTimezone(): \DateTimeZone | ||
{ | ||
return parent::getTimezone() ?: throw new \DateInvalidTimeZoneException('The DateTime object has no timezone.'); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <fabien@symfony.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\Component\Clock\Tests; | ||
|
||
use PHPUnit\Framework\TestCase; | ||
use Symfony\Component\Clock\DateTime; | ||
use Symfony\Component\Clock\Test\ClockSensitiveTrait; | ||
|
||
class DateTimeTest extends TestCase | ||
{ | ||
use ClockSensitiveTrait; | ||
|
||
public function testDateTime() | ||
{ | ||
self::mockTime('2010-01-28 15:00:00'); | ||
|
||
$date = new DateTime(); | ||
$this->assertSame('2010-01-28 15:00:00 UTC', $date->format('Y-m-d H:i:s e')); | ||
|
||
$date = new DateTime('+1 day Europe/Paris'); | ||
$this->assertSame('2010-01-29 16:00:00 Europe/Paris', $date->format('Y-m-d H:i:s e')); | ||
|
||
$date = new DateTime('2022-01-28 15:00:00 Europe/Paris'); | ||
$this->assertSame('2022-01-28 15:00:00 Europe/Paris', $date->format('Y-m-d H:i:s e')); | ||
} | ||
|
||
public function testCreateFromFormat() | ||
{ | ||
$date = DateTime::createFromFormat('Y-m-d H:i:s', '2010-01-28 15:00:00'); | ||
|
||
$this->assertInstanceOf(DateTime::class, $date); | ||
$this->assertSame('2010-01-28 15:00:00', $date->format('Y-m-d H:i:s')); | ||
|
||
$this->expectException(\DateMalformedStringException::class); | ||
$this->expectExceptionMessage('A four digit year could not be found'); | ||
DateTime::createFromFormat('Y-m-d H:i:s', 'Bad Date'); | ||
} | ||
|
||
public function testModify() | ||
{ | ||
$date = new DateTime('2010-01-28 15:00:00'); | ||
$date = $date->modify('+1 day'); | ||
|
||
$this->assertInstanceOf(DateTime::class, $date); | ||
$this->assertSame('2010-01-29 15:00:00', $date->format('Y-m-d H:i:s')); | ||
|
||
$this->expectException(\DateMalformedStringException::class); | ||
$this->expectExceptionMessage('Failed to parse time string (Bad Date)'); | ||
$date->modify('Bad Date'); | ||
} | ||
} |