Time in tests, and the clock we injected

A test that fails between 23:00 and midnight, once a year, and passes on every rerun.

interface Clock { public function now(): DateTimeImmutable; }

final class FrozenClock implements Clock
{
    public function __construct(private DateTimeImmutable $at) {}

    public function now(): DateTimeImmutable { return $this->at; }
}

// in the test
$clock = new FrozenClock(new DateTimeImmutable('2023-02-16 23:59:59'));

PSR-20 standardised this interface in 2022, which is worth adopting for the same reason as PSR-18 — the fake works with anything that takes the interface. What it does not cover is the code calling time() or now() directly, and finding those is a grep rather than a refactor: 41 call sites here, of which 30 were in tests and did not matter.