FastForward\Clock provides PSR-20 compliant clock implementations and seamless integration with Fast Forward Container.
This package offers two clock implementations: SystemClock for production time and FrozenClock for deterministic testing.
- π PSR-20 Compliant - Full support for the PSR-20 Clock interface
- π§ͺ Testing Made Easy -
FrozenClocklets you freeze time for reliable tests - π Timezone Support - Configure timezones in production and tests
- π Container Integration - Automatic service registration via
ClockServiceProvider - π― Beginner Friendly - Simple API designed for developers new to PSR patterns
composer require fast-forward/clockRequirements:
- PHP 8.3 or higher
fast-forward/container1.6 or higherpsr/clock1.0
The package ships fast-forward-clock, a portable consumer skill covering PSR-20, application wiring, timezones and deterministic tests. See installation and lifecycle for optional project synchronization or guarded project/global copies. Composer does not install or activate the skill automatically.
The simplest way to get the current time is using SystemClock:
<?php
declare(strict_types=1);
use FastForward\Clock\SystemClock;
$clock = new SystemClock();
echo $clock->now()->format(DATE_ATOM) . PHP_EOL;<?php
declare(strict_types=1);
use FastForward\Clock\SystemClock;
$clock = new SystemClock('America/Sao_Paulo');
echo $clock->now()->format('Y-m-d H:i:s P') . PHP_EOL;For larger applications, use the service provider to register the clock in your container:
<?php
declare(strict_types=1);
use FastForward\Clock\ServiceProvider\ClockServiceProvider;
use Psr\Clock\ClockInterface;
use function FastForward\Container\container;
$container = container(new ClockServiceProvider());
$clock = $container->get(ClockInterface::class);
echo $clock->now()->format(DATE_ATOM) . PHP_EOL;Use FrozenClock to create deterministic tests:
<?php
declare(strict_types=1);
use FastForward\Clock\FrozenClock;
$clock = new FrozenClock('2026-04-07 10:00:00');
echo $clock->now()->format(DATE_ATOM) . PHP_EOL;
// Output: 2026-04-07T10:00:00+00:00FrozenClock accepts multiple input formats:
<?php
declare(strict_types=1);
use FastForward\Clock\FrozenClock;
// From a DateTimeImmutable
$clock1 = new FrozenClock(new DateTimeImmutable('2026-04-07 10:00:00'));
// From a string
$clock2 = new FrozenClock('next Monday');
// From a timestamp
$clock3 = new FrozenClock(1775640000);
// From another ClockInterface
$clock4 = new FrozenClock(new SystemClock('America/New_York'));<?php
declare(strict_types=1);
use FastForward\Clock\ServiceProvider\ClockServiceProvider;
use FastForward\Clock\FrozenClock;
use FastForward\Container\ServiceProvider\ArrayServiceProvider;
use Psr\Clock\ClockInterface;
use Psr\Container\ContainerInterface;
use function FastForward\Container\container;
$frozenClock = new FrozenClock('2026-04-07 10:00:00');
$testProvider = new ArrayServiceProvider([
FrozenClock::class => static fn(ContainerInterface $container): FrozenClock => $frozenClock,
ClockInterface::class => static fn(ContainerInterface $container): FrozenClock => $container->get(FrozenClock::class),
]);
$container = container($testProvider, new ClockServiceProvider());
$clock = $container->get(ClockInterface::class);
echo $clock->now()->format(DATE_ATOM) . PHP_EOL;| Class | Description |
|---|---|
FastForward\Clock\SystemClock |
PSR-20 clock that returns current system time with optional timezone |
FastForward\Clock\FrozenClock |
PSR-20 clock that returns a fixed time (ideal for testing) |
FastForward\Clock\ServiceProvider\ClockServiceProvider |
Registers clock services in Fast Forward Container |
public function __construct(DateTimeZone|string|null $timezone = null)| Parameter | Type | Description |
|---|---|---|
$timezone |
DateTimeZone|string|null |
Timezone for the clock (defaults to system default) |
public function __construct(DateTimeInterface|ClockInterface|string|int|float $clock = 'now')| Parameter | Type | Description |
|---|---|---|
$clock |
DateTimeInterface|ClockInterface|string|int|float |
The time to freeze. Accepts DateTimeImmutable, string (relative or absolute), timestamp, or another ClockInterface |
This package integrates seamlessly with:
- Fast Forward Container - Use
ClockServiceProviderfor automatic registration - PSR-20 - Both
SystemClockandFrozenClockimplementPsr\Clock\ClockInterface - Any PSR-11 Container - Both clocks can be instantiated directly without the service provider
src/
βββ SystemClock.php # Production clock implementation
βββ FrozenClock.php # Testing clock implementation
βββ ServiceProvider/
β βββ ClockServiceProvider.php # Container service provider
β βββ Factory/
β βββ DateTimeZoneFactory.php # Timezone factory
tests/
βββ SystemClockTest.php
βββ FrozenClockTest.php
βββ ServiceProvider/
βββ ClockServiceProviderTest.php
examples/
βββ 01-system-clock.php
βββ 02-system-clock-with-timezone.php
βββ 03-frozen-clock.php
βββ 04-frozen-clock-inputs.php
βββ 05-frozen-clock-container.php
βββ 06-psr20-clock-interface.php
docs/
βββ ... (Sphinx documentation)
If you use Fast Forward Config, you can configure the default timezone:
<?php
use FastForward\Config\Config;
use FastForward\Clock\ServiceProvider\ClockServiceProvider;
use function FastForward\Container\container;
$config = new Config([
DateTimeZone::class => 'America/Sao_Paulo',
]);
$container = container($config, new ClockServiceProvider());Both clocks are final readonly classes, but you can wrap them:
<?php
declare(strict_types=1);
use FastForward\Clock\FrozenClock;
use Psr\Clock\ClockInterface;
final class StubbedClock implements ClockInterface
{
public function __construct(private ClockInterface $clock)
{
}
public function now(): \DateTimeImmutable
{
return $this->clock->now();
}
public static function create(string $time): self
{
return new self(new FrozenClock($time));
}
}Q: What's the difference between SystemClock and FrozenClock?
A: SystemClock returns the current time and is suitable for production. FrozenClock returns a fixed time and is ideal for testing.
Q: Which interface should I use in my application?
A: Use Psr\Clock\ClockInterface for maximum portability. Both implementations satisfy this interface.
Q: How do I freeze time in tests?
A: Use FrozenClock directly, or register it via ArrayServiceProvider before ClockServiceProvider.
Q: Can I use this without Fast Forward Container?
A: Yes! Simply instantiate SystemClock or FrozenClock directly in your code.
Q: Does this work with other PSR-11 containers?
A: Yes. The clocks are standalone classes that don't require any container.
Q: How do I set a specific timezone?
A: Pass a timezone string or DateTimeZone instance to the SystemClock constructor.
This project is licensed under the MIT License.
Issues, documentation improvements, and pull requests are welcome!
See CONTRIBUTING.md for changelog fragments and the release-review flow.
