cloud-castle / clock
PSR-20 набор часов для PHP 8.1+: системные, замороженные, мок-, монотонные, со смещением, масштабом и усечением, шаговые и очередь; TimeSpan с наносекундной точностью, Stopwatch, Deadline, DatePoint и CalendarDate — календарная арифметика месяцев/лет с контролем переполнения и локализацией уровня Ca
Requires
- php: >=8.1
- cloud-castle/inflector: ^1.4
- psr/clock: ^1.0
Requires (Dev)
- beste/clock: ^3.0
- deptrac/deptrac: ^3.0 || ^4.0
- ergebnis/clock: ^0.2 || ^1.0 || ^2.0
- ergebnis/composer-normalize: ^2.45
- friendsofphp/php-cs-fixer: ^3.75
- icanhazstring/composer-unused: ^0.9
- infection/infection: ^0.29 || ^0.33
- lcobucci/clock: ^3.0
- nesbot/carbon: ^2.72 || ^3.0
- php-parallel-lint/php-parallel-lint: ^1.4
- phpmd/phpmd: ^2.15
- phpstan/extension-installer: ^1.4
- phpstan/phpstan: ^1.12 || ^2.1
- phpstan/phpstan-deprecation-rules: ^1.2 || ^2.0
- phpstan/phpstan-phpunit: ^1.4 || ^2.0
- phpstan/phpstan-strict-rules: ^1.6 || ^2.0
- phpunit/phpunit: ^10.5 || ^11.5
- psalm/plugin-phpunit: ^0.19 || ^0.20
- rector/rector: ^1.2 || ^2.0
- roave/security-advisories: dev-latest
- squizlabs/php_codesniffer: ^3.12 || ^4.0
- symfony/clock: ^6.4 || ^7.0
- vimeo/psalm: ^6.0
- webmozart/assert: ^1.11
Provides
This package is auto-updated.
Last update: 2026-07-31 06:44:13 UTC
README
🇷🇺 Русский · 🇬🇧 English · 🇩🇪 Deutsch · 🇫🇷 Français · 🇪🇸 Español · 🇮🇹 Italiano
CloudCastle Clock
PSR-20 часы для PHP 8.1+ с детерминированным временем в тестах: 9 реализаций часов (системные, замороженные, мок-, монотонные, со смещением, масштабом, усечением, шаговые, очередь),
TimeSpanс наносекундной точностью,Stopwatch,Deadline,DatePointиCalendarDate— календарная работа уровня Carbon с локализацией без ICU/ext-intl. Лёгкие зависимости:psr/clockиcloud-castle/inflector.
Установка
composer require cloud-castle/clock
Требуется PHP 8.1+.
Быстрый старт
<?php
use CloudCastle\Clock\CalendarDate;
use CloudCastle\Clock\Clock;
use CloudCastle\Clock\ClockHolder;
use CloudCastle\Clock\Deadline;
use CloudCastle\Clock\MockClock;
use CloudCastle\Clock\Stopwatch;
use CloudCastle\Clock\TimeSpan;
// Продакшн: системные часы в нужной зоне, внедряются как ClockInterface.
$clock = Clock::utc();
$moment = $clock->now(); // DateTimeImmutable
$unix = $clock->timestamp(); // int без создания объекта даты
// Тесты: мок-часы с виртуальным сном — retry/backoff за миллисекунды.
$mock = MockClock::from('2026-01-01 00:00:00', 'UTC');
$mock->sleep(30.0); // время сдвинулось, процесс не спал
$mock->advance(TimeSpan::ofMinutes(5)->seconds());
// Глобальные часы со скоуп-подменой: после колбэка всё как было.
ClockHolder::within($mock, function (): void {
// Clock::now() внутри читает мок-часы.
});
// Длительности с наносекундной точностью.
$span = TimeSpan::fromString('1h 30m')->plus(TimeSpan::ofSeconds(15.5));
echo $span->toIso8601(); // PT1H30M15.5S
// Дедлайны, привязанные к часам.
$deadline = Deadline::after($clock, TimeSpan::ofSeconds(30));
if (!$deadline->isExpired()) {
$left = $deadline->remaining(); // TimeSpan
}
// Секундомер на монотонном таймере — с кругами.
$watch = Stopwatch::start();
$watch->lap('подготовка');
$seconds = $watch->stop();
// Календарная работа уровня Carbon без ICU/ext-intl.
$date = CalendarDate::parse('2026-01-31', 'UTC');
echo $date->addMonthsNoOverflow(1)->format('Y-m-d'); // 2026-02-28 (не «3 марта»)
echo $date->subDays(9)->diffForHumans($date, 'ru'); // «9 дней назад» (склонение через inflector)
Возможности
- 9 реализаций часов:
SystemClock,FrozenClock,MockClock(виртуальныйsleep()),MonotonicClock,OffsetClock,ScaledClock,TruncatingClock,StepClock,QueueClock+CallbackClockи адаптерPsrClockAdapter. TimeSpan— длительность с наносекундной точностью: фабрики от наносекунд до недель, разбор ISO 8601 /1h 30m/ relative-строк PHP, точная арифметика с контролем переполнения.Deadline— дедлайны сisExpired()/remaining(), детерминированные в тестах.Stopwatch— монотонный секундомер с кругами иmeasure(callable).DatePoint— моменты от часов приложения с типизированными ошибками.CalendarDate— календарная работа уровня Carbon без ICU/ext-intl: арифметика месяцев/лет с контролем переполнения (addMonthsNoOverflow()), границы периодов/декады/века, предикаты (isWeekend(),isToday(),isLastOfMonth()), разницы (diffInDays()…diffInYears()), навигация (next(),firstOfMonth()), fluent-сеттеры, форматтеры (toDateString(), ISO/RFC/ATOM/W3C), детерминированный парсинг относительных строк (parse('next friday')от часов приложения — без глобальногоsetTestNow), локализованные названия иdiffForHumans()на 6 языках (склонения — черезcloud-castle/inflector, включая три славянские формы русского).ClockHolder— глобальные часы сwithin()(скоуп-подмена с автовосстановлением).- Дешёвые примитивы
timestamp()/microtime()без материализации объекта даты.
Полное описание каждой возможности — в wiki.
Сравнение с аналогами
Все таблицы ниже сгенерированы автоматически из честных сравнительных тестов (benchmarks/compare.php) на ОДИНАКОВОЙ операции для всех аналогов, PHP 8.3.32, без Xdebug.
1. Функциональность
| Возможность | 🏆 CloudCastle | symfony | carbon | lcobucci | beste | ergebnis | new¹ |
|---|---|---|---|---|---|---|---|
| PSR-20 ClockInterface (now(): DateTimeImmutable) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
| Замороженные часы для детерминизма тестов | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
| Мок-часы с виртуальным sleep() | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Монотонные часы (защита от прыжков системного времени) | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Смещение времени декоратором (OffsetClock) | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Масштаб времени декоратором (ScaledClock) | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Усечение моментов до произвольной единицы | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Value object длительности с наносекундной точностью | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ |
| Дедлайны с остатком времени (Deadline) | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Шаговые часы и часы-очередь для сценарных тестов | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Секундомер с кругами в составе пакета | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Дешёвые примитивы timestamp()/microtime() без объекта даты | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Момент от часов приложения (DatePoint) | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
| Скоуп-подмена глобальных часов с автовосстановлением | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ |
| Адаптер произвольных PSR-20 часов | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ |
| Календарная арифметика месяцев/лет с контролем переполнения (CalendarDate) | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ |
| Локализованный diffForHumans и isoFormat без ext-intl | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Календарные предикаты и разницы (isWeekend, diffInDays, next) | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ |
| Всего | 🏆 18 | 6 | 7 | 2 | 3 | 2 | 0 |
2. Безопасность и корректность
| Свойство | 🏆 CloudCastle | symfony | carbon | lcobucci | beste | ergebnis | new¹ |
|---|---|---|---|---|---|---|---|
| Иммутабельные показания (DateTimeImmutable) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Явная временная зона (не зависит от date.timezone) | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ❌ |
| Детерминизм тестов без обязательного глобального состояния | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ❌ |
| Монотонность (устойчивость к откату системных часов) | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Типизированные исключения вместо false/warning на границе ввода | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ |
| Санитайзинг враждебного ввода в сообщениях исключений | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Контроль целочисленного переполнения длительностей | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Минимальные зависимости (≤2, без ICU/ext-intl) | ✅ | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ |
| Всего | 🏆 8 | 4 | 3 | 4 | 3 | 4 | 2 |
3. Производительность: now()
Получение текущего момента now(), 200 000 раз (минимум из 6).
| Решение | Время (мс) | Итог |
|---|---|---|
| new¹ | 117,5 | базовый уровень (не библиотека) |
| 🏆 lcobucci | 130,4 | быстрейшее среди библиотек |
| ergebnis | 132,9 | аналог |
| beste | 133 | аналог |
| CloudCastle | 138,7 | аналог |
| symfony | 181 | аналог |
| carbon | 806,1 | аналог |
4. Производительность: unix-метка
Текущая unix-метка (целые секунды), 200 000 раз.
| Решение | Время (мс) | Итог |
|---|---|---|
| new¹ | 21 | базовый уровень (не библиотека) |
| 🏆 CloudCastle | 33,8 | быстрейшее среди библиотек |
| beste | 157,9 | аналог |
| ergebnis | 162,4 | аналог |
| lcobucci | 163,4 | аналог |
| symfony | 223,2 | аналог |
| carbon | 911,9 | аналог |
5. Потребление памяти библиотекой
Прирост памяти самой библиотеки на 200 000 вызовов now() (изолированный процесс, без веса автолоадера).
| Решение | Память (KB) | Итог |
|---|---|---|
| new¹ | 1 | базовый уровень (не библиотека) |
| 🏆 beste | 3 | легчайшее среди библиотек |
| ergebnis | 5 | аналог |
| lcobucci | 6 | аналог |
| symfony | 30 | аналог |
| CloudCastle | 56 | аналог |
| carbon | 3 436 | аналог |
6. Утечки памяти
Рост памяти после прогрева за 200 000 вызовов now() (0 — утечек нет).
| Решение | Память (KB) | Итог |
|---|---|---|
| 🏆 CloudCastle | 0 | без утечек |
| symfony | 0 | без утечек |
| carbon | 0 | без утечек |
| lcobucci | 0 | без утечек |
| beste | 0 | без утечек |
| ergebnis | 0 | без утечек |
| new¹ | 0 | базовый уровень (не библиотека) |
7. Пик памяти процесса
Пик памяти изолированного процесса на 200 000 вызовов now().
| Решение | Память (KB) | Итог |
|---|---|---|
| 🏆 CloudCastle | 6 950 | легчайшее среди библиотек |
| symfony | 6 950 | аналог |
| lcobucci | 6 950 | аналог |
| beste | 6 950 | аналог |
| ergebnis | 6 950 | аналог |
| new¹ | 6 950 | базовый уровень (не библиотека) |
| carbon | 10 226 | аналог |
8. Качество кода
| Инструмент | 🏆 CloudCastle | symfony | carbon | lcobucci | beste | ergebnis |
|---|---|---|---|---|---|---|
| PHPStan | ✅ max + strict | ❌ | ✅ | ✅ + strict | ✅ + strict | ✅ + strict |
| Psalm | ✅ level 1 | ✅ (monorepo) | ❌ | ❌ | ✅ | ❌ |
| PHPMD | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ |
| PHPCS / CS-Fixer | ✅ PSR-12 + fixer | ✅ fixer | ✅ | ✅ | ❌ | ✅ fixer |
| Rector | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ |
| Deptrac (слои архитектуры) | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Мутационное тестирование (Infection) | ✅ MSI — | ❌ | ❌ | ✅ | ❌ | ✅ |
| Покрытие строк (живое) | ✅ — | — | — | — | — | — |
| Всего | 🏆 8 | 2 | 3 | 3 | 2 | 4 |
Инструменты аналогов — по require-dev их опубликованных дистрибутивов (снапшот июль 2026); symfony/clock — по монорепозиторию symfony/symfony. Метрики CloudCastle — живые результаты локального прогона.
¹ Базовый уровень (нативные вызовы/примитивы без полноты решения) показан для контекста и не претендует на победу среди библиотек-аналогов.
Честная выжимка
Плюсы:
- функциональный суперсет всех сравниваемых PSR-20 библиотек вместе взятых,
включая календарную работу уровня Carbon (
CalendarDate); - детерминизм времени в тестах без глобального состояния (изолированные экземпляры часов) и со скоуп-подменой там, где глобальность удобна;
- наносекундная арифметика длительностей с контролем переполнения — тихих искажений не бывает;
- лёгкие зависимости: только
psr/clockиcloud-castle/inflector(корректные склонения без ICU/ext-intl) — на порядок легче связки Carbon +symfony/translation.
Минусы:
- моложе и менее распространён, чем
symfony/clockиnesbot/carbon: меньше ответов на StackOverflow; - нет готовой интеграции с Laravel-экосистемой (фасады, Eloquent-касты дат),
как у Carbon, — сознательное ограничение области: пакет фреймворк-агностичен
(сам календарный API
CalendarDate— от арифметики и предикатов до парсинга относительных строк иdiffForHumans— Carbon-паритетен).
Рекомендации по применению:
- Бизнес-сервисы и финтех — внедряйте
ClockInterface, в тестахMockClock/FrozenClock: детерминированные проверки TTL, дедлайнов, retry. - Очереди, лимитеры, кэши —
MonotonicClockдля интервалов, устойчивых к переводу системных часов;Deadlineдля тайм-аутов. - Профилирование и метрики —
Stopwatchс кругами;timestamp()на горячих путях вместоnow()->getTimestamp(). - Симуляции и сценарные тесты —
StepClock/QueueClock/ScaledClock. - Календарь и локализация —
CalendarDateдля арифметики месяцев/лет без переполнения (addMonthsNoOverflow()), детерминированного парсинга относительных строк (parse('next friday')от часов приложения), форматтеров (toDateString(), ISO/RFC/ATOM), предикатов (isWeekend(),isToday()), разниц (diffInDays()) иdiffForHumans()на 6 языках без ICU/ext-intl. - Не стоит брать только ради тесной интеграции с Laravel (фасады, Eloquent-касты дат) — это единственное, чем Carbon здесь богаче.
Разработка
composer install
composer check # линтеры + статический анализ + тесты
composer fix # автоисправления (Rector, PHP CS Fixer, PHPCBF)
composer ci # полный CI-пайплайн локально
Полный список команд с описаниями: composer run-script --list.
Документация
- Репозиторий: https://gitverse.ru/cloud-castle/clock
- Wiki (руководства и страницы возможностей): https://gitverse.ru/cloud-castle/clock/wiki
- История изменений: CHANGELOG.md
- Обновление между версиями: UPGRADING.md
- Как внести вклад: CONTRIBUTING.md
- Кодекс поведения: CODE_OF_CONDUCT.md
- Политика безопасности: SECURITY.md
Лицензия
MIT © CloudCastle (alex-4-17@yandex.ru)
🇷🇺 Русский · 🇬🇧 English · 🇩🇪 Deutsch · 🇫🇷 Français · 🇪🇸 Español · 🇮🇹 Italiano