# tez-utils-clock Testable clock abstractions and a high-resolution stopwatch for PHP — `ClockInterface`, `SystemClock`, `FrozenClock` for deterministic tests, and `Stopwatch` with lap support. PHP 8.3+, no dependencies. ## Installation ```bash composer require tez/utils-clock ``` ## Components | Class | Purpose | |-------|---------| | `ClockInterface` | PSR-20 compatible clock contract | | `SystemClock` | Returns the actual current time | | `FrozenClock` | Returns a fixed time — for deterministic tests | | `Stopwatch` | High-resolution elapsed time and lap measurement | --- ## ClockInterface ```php interface ClockInterface { public function now(): \DateTimeImmutable; } ``` PSR-20 compatible. Inject `ClockInterface` into services instead of calling `new \DateTimeImmutable()` directly — this makes time controllable in tests without mocking. --- ## SystemClock Returns the actual current time, honoring the system timezone. ```php use Tez\Utils\Clock\SystemClock; $clock = new SystemClock(); $now = $clock->now(); // \DateTimeImmutable ``` --- ## FrozenClock Returns a fixed `DateTimeImmutable` — always the same instant until `set()` is called. Use it in tests to control time without relying on the system clock. ```php use Tez\Utils\Clock\FrozenClock; $clock = new FrozenClock(new \DateTimeImmutable('2026-01-01 12:00:00')); $clock->now(); // 2026-01-01 12:00:00 $clock->now(); // 2026-01-01 12:00:00 — always the same // Advance to a new instant $clock->set(new \DateTimeImmutable('2026-06-15 08:30:00')); $clock->now(); // 2026-06-15 08:30:00 ``` ### Testing example ```php final class OrderServiceTest extends TestCase { public function testOrderExpiresAfterDeadline(): void { $clock = new FrozenClock(new \DateTimeImmutable('2026-01-01 12:00:00')); $service = new OrderService($clock); self::assertFalse($service->isExpired($order)); // Advance the clock past the deadline $clock->set(new \DateTimeImmutable('2026-01-10 00:00:00')); self::assertTrue($service->isExpired($order)); } } ``` --- ## Stopwatch High-resolution elapsed time measurement using `hrtime()` (nanosecond precision). Not affected by NTP adjustments or system clock changes. ```php use Tez\Utils\Clock\Stopwatch; $sw = Stopwatch::start(); // ... do work ... $sw->elapsed(); // total seconds since start (float) $sw->elapsedMs(); // total milliseconds since start (float) ``` ### Lap timing `lap()` returns the elapsed seconds since the last `lap()` call (or since start if no lap yet), then moves the lap marker forward. ```php $sw = Stopwatch::start(); // ... first stage ... $stage1 = $sw->lap(); // seconds for stage 1 // ... second stage ... $stage2 = $sw->lap(); // seconds for stage 2 only $total = $sw->elapsed(); // total since start (unaffected by laps) ``` ### Reset ```php $sw->reset(); // resets both the total timer and the lap marker to now ``` --- ## Requirements - PHP 8.3+ - No runtime dependencies ## License MIT