fast-forward / clock
A PSR-20 compliant clock implementation for PHP, with support for time zones and a service provider for easy integration with Fast Forward Container.
Requires
- php: ^8.3
- fast-forward/config: ^1.4
- fast-forward/container: ^1.10
- psr/clock: ^1.0
Requires (Dev)
- fast-forward/dev-tools: dev-main
- llm/skills: ^1.15
Suggests
- llm/skills: Optionally synchronize the bundled agent skill in consumer projects.
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-10-21 00:59:49 UTC
README
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.
โจ Features
- ๐ 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
๐ฆ Installation
composer require fast-forward/clock
Requirements:
- PHP 8.3 or higher
fast-forward/container1.6 or higherpsr/clock1.0
๐ ๏ธ Usage
Agent skill
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.
Basic: Get the current time
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;
With a specific timezone
<?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;
Using with Fast Forward Container
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;
Freezing time for tests
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:00
FrozenClock 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'));
Using FrozenClock with Fast Forward Container in tests
<?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;
๐งฐ API Summary
| 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 |
SystemClock Constructor
public function __construct(DateTimeZone|string|null $timezone = null)
| Parameter | Type | Description |
|---|---|---|
$timezone |
DateTimeZone|string|null |
Timezone for the clock (defaults to system default) |
FrozenClock Constructor
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 |
๐ Integration
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
๐ Directory Structure
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)
โ๏ธ Advanced / Customization
Custom timezone via configuration
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());
Creating a custom clock
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)); } }
โ FAQ
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.
๐ก License
This project is licensed under the MIT License.
๐ค Contributing
Issues, documentation improvements, and pull requests are welcome!
See CONTRIBUTING.md for changelog fragments and the release-review flow.
