beste/clock
A collection of PSR-20 clock implementations for PHP, including SystemClock, LocalizedClock, UTCClock, FrozenClock for tests, MinuteClock, and WrappingClock. Freeze time, set time zones, or wrap any object with a now() method. Install via composer require beste/clock.
psr/clock (one interface, now(): DateTimeImmutable) under MIT. The real decision is whether to standardize on a PSR-20 clock abstraction at all, and if so whether to spend an afternoon writing it in-house or take a zero-dependency package. Cost of adoption is near-zero; cost of not having decided is unbounded flaky tests in every time-sensitive feature.FrozenClock (fixed instant, manually settable) and MinuteClock (collapses sub-minute precision, useful for idempotency keys and "same minute" grouping) directly reduce that risk.FrozenClock::at(...) / ::withNowFrom(...) lets acceptance criteria be written as "given the clock reads 2026-03-01 00:00, when the job runs, then the invoice is prorated." That converts time travel from an engineering hack into a product-testable scenario, which affects how confidently you can promise date-boundary behavior (midnight rollover, DST, leap day, month-end) in a release.UTCClock (storage and inter-service communication — the README's explicit "the clock you should™ use") vs. LocalizedClock::in('Europe/Berlin') (user-facing display and region-specific business rules) is a product-legible distinction, not just a technical one. WrappingClock lets existing legacy objects with a now() method participate without a refactor, which matters for incremental migration scoping.Carbon::setTestNow(). Choosing PSR-20 is a decision to keep business/domain code framework-agnostic and portable, typically because you expect shared libraries, a service extraction, or a non-Laravel consumer. That's a strategic call about coupling, and it should be made deliberately rather than by default.Consider adopting when:
now() / Carbon::now() / date() inside business logic, and you want a standards-based interface (psr/clock) rather than a bespoke one.FrozenClock and MinuteClock for free instead of maintaining your own test clock and test helper, and you value a dependency tree that is effectively just psr/clock.UTCClock for persistence, LocalizedClock for a regional rule engine) rather than relying on the ambient date.timezone.Look elsewhere (or just implement it) when:
Carbon::setTestNow() / CarbonImmutable::setTestNow() already does this, is battle-tested, and — critically — is respected by framework internals. beste/clock does not touch the scheduler, queue timestamps, cache TTL, or DB NOW(); injecting a FrozenClock into your service does not freeze the world.MockClock and no notion of time advancing on its own; FrozenClock is static until you call setTo(). If you want sleep()-aware or auto-advancing test clocks with assertion helpers, look at symfony/clock (MockClock, NowPinnedClock), which also has a much larger install base.ClockInterface in AppServiceProvider yourself and override it in test setup. That's two lines, but it's work you own.MockClock class that does not exist in this package — a good reminder that the README, not secondhand notes, is the source of truth here.To executives: "A small, free, MIT-licensed library that makes time-sensitive features — trials, renewals, expiries, scheduled promotions — deterministic and testable. It lets us validate date-boundary behavior (midnight rollover, month-end, DST) in automated tests instead of hoping, which reduces production incidents and QA cycles on exactly the features that generate revenue. Cost is effectively zero: no license, no vendor, two lines of container configuration. The honest caveat is that the strategic value comes from the pattern (a testable clock), not the library — so read this as 'approve a standard,' not 'approve a vendor.'"
To engineering: "Six thin PSR-20 clock implementations — SystemClock, UTCClock, LocalizedClock, FrozenClock, MinuteClock, WrappingClock. It gives us a clean ClockInterface seam so domain code stops reaching for Carbon::now(), and FrozenClock gives us deterministic tests without hand-rolling a test clock. Dependency tree is essentially just psr/clock; no service provider, so we bind it ourselves in AppServiceProvider and swap to FrozenClock in test setup. Two things to be clear-eyed about: it does not integrate with the scheduler, queue, cache TTLs, or DB time, so Carbon::setTestNow() remains the tool for framework-level test time; and FrozenClock requires manual setTo() — it won't tick. If we want richer mocks with a bigger maintainer base, symfony/clock is the credible alternative. My recommendation is to adopt the PSR-20 interface as our standard and take this package only if we want the implementations for free — with a quick check on release cadence and current PHP/Laravel support before we pin it."
How can I help you explore Laravel packages today?