Skip to content

Commit 2d1f62a

Browse files
[Clock] Add TimePoint: an immutable DateTime implementation with stricter error handling and return types
1 parent 3265ec2 commit 2d1f62a

File tree

12 files changed

+239
-41
lines changed

12 files changed

+239
-41
lines changed

.github/expected-missing-return-types.diff

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1512,6 +1512,16 @@ diff --git a/src/Symfony/Component/Cache/Traits/FilesystemCommonTrait.php b/src/
15121512
+ public function __wakeup(): void
15131513
{
15141514
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
1515+
diff --git a/src/Symfony/Component/Clock/ClockAwareTrait.php b/src/Symfony/Component/Clock/ClockAwareTrait.php
1516+
--- a/src/Symfony/Component/Clock/ClockAwareTrait.php
1517+
+++ b/src/Symfony/Component/Clock/ClockAwareTrait.php
1518+
@@ -33,5 +33,5 @@ trait ClockAwareTrait
1519+
* @return TimePoint
1520+
*/
1521+
- protected function now(): \DateTimeImmutable
1522+
+ protected function now(): TimePoint
1523+
{
1524+
$now = ($this->clock ??= new Clock())->now();
15151525
diff --git a/src/Symfony/Component/Config/ConfigCacheInterface.php b/src/Symfony/Component/Config/ConfigCacheInterface.php
15161526
--- a/src/Symfony/Component/Config/ConfigCacheInterface.php
15171527
+++ b/src/Symfony/Component/Config/ConfigCacheInterface.php

src/Symfony/Component/Clock/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ CHANGELOG
44
6.4
55
---
66

7+
* Add `TimePoint`: an immutable DateTime implementation with stricter error handling and return types
78
* Throw `DateMalformedStringException`/`DateInvalidTimeZoneException` when appropriate
89
* Add `$modifier` argument to the `now()` helper
910

src/Symfony/Component/Clock/Clock.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,14 @@ public static function set(PsrClockInterface $clock): void
4444
self::$globalClock = $clock instanceof ClockInterface ? $clock : new self($clock);
4545
}
4646

47-
public function now(): \DateTimeImmutable
47+
public function now(): TimePoint
4848
{
4949
$now = ($this->clock ?? self::get())->now();
5050

51+
if (!$now instanceof TimePoint) {
52+
$now = TimePoint::createFromInterface($now);
53+
}
54+
5155
return isset($this->timezone) ? $now->setTimezone($this->timezone) : $now;
5256
}
5357

src/Symfony/Component/Clock/ClockAwareTrait.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,13 @@ public function setClock(ClockInterface $clock): void
2929
$this->clock = $clock;
3030
}
3131

32+
/**
33+
* @return TimePoint
34+
*/
3235
protected function now(): \DateTimeImmutable
3336
{
34-
return ($this->clock ??= new Clock())->now();
37+
$now = ($this->clock ??= new Clock())->now();
38+
39+
return $now instanceof TimePoint ? $now : Moment::createFromInterface($now);
3540
}
3641
}

src/Symfony/Component/Clock/MockClock.php

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
*/
2121
final class MockClock implements ClockInterface
2222
{
23-
private \DateTimeImmutable $now;
23+
private TimePoint $now;
2424

2525
/**
2626
* @throws \DateMalformedStringException When $now is invalid
@@ -38,20 +38,16 @@ public function __construct(\DateTimeImmutable|string $now = 'now', \DateTimeZon
3838
}
3939
}
4040

41-
if (\PHP_VERSION_ID >= 80300 && \is_string($now)) {
42-
$now = new \DateTimeImmutable($now, $timezone ?? new \DateTimeZone('UTC'));
43-
} elseif (\is_string($now)) {
44-
try {
45-
$now = new \DateTimeImmutable($now, $timezone ?? new \DateTimeZone('UTC'));
46-
} catch (\Exception $e) {
47-
throw new \DateMalformedStringException($e->getMessage(), $e->getCode(), $e);
48-
}
41+
if (\is_string($now)) {
42+
$now = new TimePoint($now, $timezone ?? new \DateTimeZone('UTC'));
43+
} elseif (!$now instanceof TimePoint) {
44+
$now = TimePoint::createFromInterface($now);
4945
}
5046

5147
$this->now = null !== $timezone ? $now->setTimezone($timezone) : $now;
5248
}
5349

54-
public function now(): \DateTimeImmutable
50+
public function now(): TimePoint
5551
{
5652
return clone $this->now;
5753
}
@@ -62,7 +58,7 @@ public function sleep(float|int $seconds): void
6258
$now = substr_replace(sprintf('@%07.0F', $now), '.', -6, 0);
6359
$timezone = $this->now->getTimezone();
6460

65-
$this->now = (new \DateTimeImmutable($now, $timezone))->setTimezone($timezone);
61+
$this->now = TimePoint::createFromInterface(new \DateTimeImmutable($now, $timezone))->setTimezone($timezone);
6662
}
6763

6864
/**

src/Symfony/Component/Clock/MonotonicClock.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public function __construct(\DateTimeZone|string $timezone = null)
3838
$this->timezone = \is_string($timezone ??= date_default_timezone_get()) ? $this->withTimeZone($timezone)->timezone : $timezone;
3939
}
4040

41-
public function now(): \DateTimeImmutable
41+
public function now(): TimePoint
4242
{
4343
[$s, $us] = hrtime();
4444

@@ -56,7 +56,7 @@ public function now(): \DateTimeImmutable
5656

5757
$now = '@'.($s + $this->sOffset).'.'.$now;
5858

59-
return (new \DateTimeImmutable($now, $this->timezone))->setTimezone($this->timezone);
59+
return TimePoint::createFromInterface(new \DateTimeImmutable($now, $this->timezone))->setTimezone($this->timezone);
6060
}
6161

6262
public function sleep(float|int $seconds): void

src/Symfony/Component/Clock/NativeClock.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ public function __construct(\DateTimeZone|string $timezone = null)
2828
$this->timezone = \is_string($timezone ??= date_default_timezone_get()) ? $this->withTimeZone($timezone)->timezone : $timezone;
2929
}
3030

31-
public function now(): \DateTimeImmutable
31+
public function now(): TimePoint
3232
{
33-
return new \DateTimeImmutable('now', $this->timezone);
33+
return TimePoint::createFromInterface(new \DateTimeImmutable('now', $this->timezone));
3434
}
3535

3636
public function sleep(float|int $seconds): void

src/Symfony/Component/Clock/Resources/now.php

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,27 +15,14 @@
1515
/**
1616
* @throws \DateMalformedStringException When the modifier is invalid
1717
*/
18-
function now(string $modifier = null): \DateTimeImmutable
18+
function now(string $modifier = 'now'): TimePoint
1919
{
20-
if (null === $modifier || 'now' === $modifier) {
21-
return Clock::get()->now();
20+
if ('now' !== $modifier) {
21+
return new TimePoint($modifier);
2222
}
2323

2424
$now = Clock::get()->now();
2525

26-
if (\PHP_VERSION_ID < 80300) {
27-
try {
28-
$tz = (new \DateTimeImmutable($modifier, $now->getTimezone()))->getTimezone();
29-
} catch (\Exception $e) {
30-
throw new \DateMalformedStringException($e->getMessage(), $e->getCode(), $e);
31-
}
32-
$now = $now->setTimezone($tz);
33-
34-
return @$now->modify($modifier) ?: throw new \DateMalformedStringException(error_get_last()['message'] ?? sprintf('Invalid date modifier "%s".', $modifier));
35-
}
36-
37-
$tz = (new \DateTimeImmutable($modifier, $now->getTimezone()))->getTimezone();
38-
39-
return $now->setTimezone($tz)->modify($modifier);
26+
return $now instanceof TimePoint ? $now : Moment::createFromInterface($now);
4027
}
4128
}

src/Symfony/Component/Clock/Tests/ClockAwareTraitTest.php

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,15 @@
1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\Component\Clock\ClockAwareTrait;
1616
use Symfony\Component\Clock\MockClock;
17+
use Symfony\Component\Clock\TimePoint;
1718

1819
class ClockAwareTraitTest extends TestCase
1920
{
2021
public function testTrait()
2122
{
22-
$sut = new class() {
23-
use ClockAwareTrait {
24-
now as public;
25-
}
26-
};
23+
$sut = new ClockAwareTestImplem();
2724

28-
$this->assertInstanceOf(\DateTimeImmutable::class, $sut->now());
25+
$this->assertInstanceOf(TimePoint::class, $sut->now());
2926

3027
$clock = new MockClock();
3128
$sut = new $sut();
@@ -38,3 +35,10 @@ public function testTrait()
3835
$this->assertSame(1.0, round($sut->now()->getTimestamp() - $ts, 1));
3936
}
4037
}
38+
39+
class ClockAwareTestImplem
40+
{
41+
use ClockAwareTrait {
42+
now as public;
43+
}
44+
}

src/Symfony/Component/Clock/Tests/ClockTest.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use Symfony\Component\Clock\MockClock;
1818
use Symfony\Component\Clock\NativeClock;
1919
use Symfony\Component\Clock\Test\ClockSensitiveTrait;
20+
use Symfony\Component\Clock\TimePoint;
2021

2122
use function Symfony\Component\Clock\now;
2223

@@ -35,7 +36,7 @@ public function testMockClock()
3536

3637
public function testNativeClock()
3738
{
38-
$this->assertInstanceOf(\DateTimeImmutable::class, now());
39+
$this->assertInstanceOf(TimePoint::class, now());
3940
$this->assertInstanceOf(NativeClock::class, Clock::get());
4041
}
4142

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy