Clock Component¶
Overview¶
Phalcon\Time\Clock is a small abstraction that returns the current time as a DateTimeImmutable object. It allows you to decouple your code from the system clock, which makes time-dependent logic predictable and easy to test.
The component ships with two implementations:
- Phalcon\Time\Clock\SystemClock - returns the real, system time
- Phalcon\Time\Clock\FrozenClock - returns a fixed time, useful for testing
Both implementations adhere to the Phalcon\Time\Clock\ClockInterface contract:
<?php
namespace Phalcon\Time\Clock;
use DateTimeImmutable;
interface ClockInterface
{
public function now(): DateTimeImmutable;
}
SystemClock¶
Phalcon\Time\Clock\SystemClock returns the current time using the timezone passed to its constructor. The class is final and cannot be extended.
<?php
use DateTimeZone;
use Phalcon\Time\Clock\SystemClock;
$clock = new SystemClock(new DateTimeZone('Pacific/Niue'));
echo $clock->now()->format('Y-m-d H:i:s');
Constructor¶
The constructor requires a DateTimeZone object that will be used every timenow() is called. Factory methods¶
For convenience, two static constructors are exposed:
Returns a new instance configured with the current default timezone (as returned by date_default_timezone_get()). Returns a new instance configured with theUTC timezone. <?php
use Phalcon\Time\Clock\SystemClock;
$clock = SystemClock::fromUTC();
echo $clock->now()->format(DATE_ATOM);
now()¶
Returns a new DateTimeImmutable representing now in the configured timezone. Each call returns a fresh object with the current time.<?php
use Phalcon\Time\Clock\SystemClock;
$clock = SystemClock::fromUTC();
$first = $clock->now();
sleep(1);
$second = $clock->now();
var_dump($first == $second); // false
FrozenClock¶
Phalcon\Time\Clock\FrozenClock always returns the same point in time until you change it. This is the implementation of choice for testing code that depends on the current time. The class is final and cannot be extended.
<?php
use DateTimeImmutable;
use Phalcon\Time\Clock\FrozenClock;
$clock = new FrozenClock(new DateTimeImmutable('2026-01-01 12:00:00'));
echo $clock->now()->format('Y-m-d H:i:s'); // 2026-01-01 12:00:00
echo $clock->now()->format('Y-m-d H:i:s'); // 2026-01-01 12:00:00
Constructor¶
The constructor accepts a DateTimeImmutable object, which will be returned by every subsequent call tonow() until the clock is mutated. Factory methods¶
Same convenience constructors as SystemClock are available:
Returns a new instance frozen at now using the current default timezone. Returns a new instance frozen at now using theUTC timezone. now()¶
Returns the DateTimeImmutable currently held by the clock. The returned value does not change untilset() or adjust() is called. set()¶
Replaces the time held by the clock. Every consumer that has a reference to the clock will observe the new value on the next call tonow(). <?php
use DateTimeImmutable;
use Phalcon\Time\Clock\FrozenClock;
$clock = new FrozenClock(new DateTimeImmutable('2026-01-01 12:00:00'));
echo $clock->now()->format('Y-m-d H:i:s'); // 2026-01-01 12:00:00
$clock->set(new DateTimeImmutable('2026-06-15 09:30:00'));
echo $clock->now()->format('Y-m-d H:i:s'); // 2026-06-15 09:30:00
adjust()¶
Mutates the clock by applying a DateTimeImmutable::modify() expression. Every consumer that has a reference to the clock will observe the new value on the next call tonow(). If the modifier string is invalid, a Phalcon\Time\Clock\Exception is thrown. <?php
use DateTimeImmutable;
use Phalcon\Time\Clock\FrozenClock;
$clock = new FrozenClock(new DateTimeImmutable('2026-01-01 12:00:00'));
$clock->adjust('+1 day');
echo $clock->now()->format('Y-m-d H:i:s'); // 2026-01-02 12:00:00
$clock->adjust('+2 hours');
echo $clock->now()->format('Y-m-d H:i:s'); // 2026-01-02 14:00:00
<?php
use DateTimeImmutable;
use Phalcon\Time\Clock\Exception;
use Phalcon\Time\Clock\FrozenClock;
$clock = new FrozenClock(new DateTimeImmutable('2026-01-01 12:00:00'));
try {
$clock->adjust('not a real modifier');
} catch (Exception $ex) {
echo $ex->getMessage(); // Invalid modifier: "not a real modifier"
}
Exception¶
Any exception thrown by the component is a Phalcon\Time\Clock\Exception, which extends the base PHP \Exception class. At the moment, it is only thrown by FrozenClock::adjust() when the modifier string cannot be parsed.
Dependency Injection¶
Because the clock is exposed through a small interface, it is a good candidate for dependency injection. Inject the interface into your services and pass the implementation that suits the context (system or frozen).
<?php
use Phalcon\Di\Di;
use Phalcon\Time\Clock\ClockInterface;
use Phalcon\Time\Clock\SystemClock;
$container = new Di();
$container->setShared(
ClockInterface::class,
function () {
return SystemClock::fromUTC();
}
);
In your services, type-hint against ClockInterface:
<?php
use Phalcon\Time\Clock\ClockInterface;
class InvoiceService
{
public function __construct(
private ClockInterface $clock
) {
}
public function isOverdue(\DateTimeImmutable $dueDate): bool
{
return $this->clock->now() > $dueDate;
}
}
In your tests, swap the implementation for a FrozenClock so that time-dependent assertions become deterministic.