Search by

PSR-20 набор часов для PHP 8.1+: системные, замороженные, мок-, монотонные, со смещением, масштабом и усечением, шаговые и очередь; TimeSpan с наносекундной точностью, Stopwatch, Deadline, DatePoint и CalendarDate — календарная арифметика месяцев/лет с контролем переполнения и локализацией уровня Ca

v1.3.1 2026-09-15 09:05 UTC

README

🇷🇺 Русский · 🇬🇧 English · 🇩🇪 Deutsch · 🇫🇷 Français · 🇪🇸 Español · 🇮🇹 Italiano

CloudCastle Clock

CloudCastle Clock

Packagist Version PHP Version License Downloads Monthly Downloads Packagist Stars Daily Downloads Security advisories

GitVerse Issues Releases CI

PHPStan Psalm PHPMD PHPCS Coverage Infection MSI OpenSSF Scorecard

PSR-20 часы для PHP 8.1+ с детерминированным временем в тестах: 11 реализаций часов (системные, замороженные, мок-, монотонные, со смещением, масштабом, усечением, шаговые, очередь, на замыкании, адаптер PSR-20), TimeSpan с наносекундной точностью, Stopwatch, Deadline, DatePoint и CalendarDate — календарная работа уровня Carbon с локализацией без ICU/ext-intl. Лёгкие зависимости: psr/clock и cloud-castle/inflector.

Установка

composer require cloud-castle/clock

Требуется PHP 8.1+ и расширение mbstring (его запрашивает cloud-castle/inflector — в типовых сборках PHP оно уже включено).

Быстрый старт

<?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(5)->diffForHumans($date, 'ru');   // «5 дней назад» (склонение через inflector)

Возможности

  • 11 реализаций часов: 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. Функциональность

Возможность🏆 CloudCastlesymfonycarbonlcobuccibesteergebnisnew¹
PSR-20 ClockInterface (now(): DateTimeImmutable)
Замороженные часы для детерминизма тестов
Мок-часы с виртуальным sleep()
Монотонные часы (защита от прыжков системного времени)
Смещение времени декоратором (OffsetClock)
Масштаб времени декоратором (ScaledClock)
Усечение моментов до произвольной единицы
Value object длительности с наносекундной точностью
Дедлайны с остатком времени (Deadline)
Шаговые часы и часы-очередь для сценарных тестов
Секундомер с кругами в составе пакета
Дешёвые примитивы timestamp()/microtime() без объекта даты
Момент от часов приложения (DatePoint)
Скоуп-подмена глобальных часов с автовосстановлением
Адаптер произвольных PSR-20 часов
Календарная арифметика месяцев/лет с контролем переполнения (CalendarDate)
Локализованный diffForHumans и isoFormat без ext-intl
Календарные предикаты и разницы (isWeekend, diffInDays, next)
Всего🏆 18672320

2. Безопасность и корректность

Свойство🏆 CloudCastlesymfonycarbonlcobuccibesteergebnisnew¹
Иммутабельные показания (DateTimeImmutable)
Явная временная зона (не зависит от date.timezone)
Детерминизм тестов без обязательного глобального состояния
Монотонность (устойчивость к откату системных часов)
Типизированные исключения вместо false/warning на границе ввода
Санитайзинг враждебного ввода в сообщениях исключений
Контроль целочисленного переполнения длительностей
Минимальные зависимости (≤2, без ICU/ext-intl)
Всего🏆 8434342

3. Производительность: now()

Получение текущего момента now(), 200 000 раз (минимум из 15).

РешениеВремя (мс)Итог
🏆 CloudCastle292,1быстрейшее среди библиотек
🏆 lcobucci292,6наравне с лидером (в пределах погрешности)
🏆 ergebnis293,2наравне с лидером (в пределах погрешности)
new¹310базовый уровень (не библиотека)
beste314,5аналог
symfony320аналог
carbon971аналог

4. Производительность: unix-метка

Текущая unix-метка (целые секунды), 200 000 раз.

РешениеВремя (мс)Итог
new¹219,2базовый уровень (не библиотека)
🏆 CloudCastle225,6быстрейшее среди библиотек
lcobucci298,1аналог
ergebnis298,5аналог
beste321,5аналог
symfony331,6аналог
carbon978,9аналог

5. Потребление памяти библиотекой

Прирост памяти самой библиотеки на 200 000 вызовов now() (изолированный процесс, без веса автолоадера).

РешениеПамять (KB)Итог
new¹1базовый уровень (не библиотека)
🏆 beste3легчайшее среди библиотек
lcobucci5аналог
ergebnis5аналог
symfony30аналог
CloudCastle63аналог
carbon3 425аналог

6. Утечки памяти

Рост памяти после прогрева за 200 000 вызовов now() (0 — утечек нет).

РешениеПамять (KB)Итог
🏆 CloudCastle0без утечек
symfony0без утечек
carbon0без утечек
lcobucci0без утечек
beste0без утечек
ergebnis0без утечек
new¹0базовый уровень (не библиотека)

7. Пик памяти процесса

Пик памяти изолированного процесса на 200 000 вызовов now().

РешениеПамять (KB)Итог
🏆 CloudCastle7 029легчайшее среди библиотек
🏆 symfony7 029наравне с лидером (в пределах погрешности)
🏆 lcobucci7 029наравне с лидером (в пределах погрешности)
🏆 beste7 029наравне с лидером (в пределах погрешности)
🏆 ergebnis7 029наравне с лидером (в пределах погрешности)
new¹7 029базовый уровень (не библиотека)
carbon10 298аналог

8. Качество кода

Инструмент🏆 CloudCastlesymfonycarbonlcobuccibesteergebnis
PHPStan✅ max + strict✅ + strict✅ + strict✅ + strict
Psalm✅ level 1✅ (monorepo)
PHPMD
PHPCS / CS-Fixer✅ PSR-12 + fixer✅ fixer✅ fixer
Rector
Deptrac (слои архитектуры)
Мутационное тестирование (Infection)✅ MSI 100.0%
Покрытие строк (живое)✅ 100.0%
Всего🏆 823324

Инструменты аналогов — по require-dev их опубликованных дистрибутивов (снапшот июль 2026); symfony/clock — по монорепозиторию symfony/symfony. Метрики CloudCastle — живые результаты локального прогона.

¹ Базовый уровень (нативные вызовы/примитивы без полноты решения) показан для контекста и не претендует на победу среди библиотек-аналогов.

Результаты, отличающиеся от лучшего менее чем на 5%, отмечены как равные: у этих решений горячий путь совпадает посимвольно (new DateTimeImmutable('now', $tz)), и на таком масштабе разницу определяет шум машины, а не код.

Честная выжимка

Плюсы:

  • функциональный суперсет всех сравниваемых 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.

Документация

Лицензия

MIT © CloudCastle (alex-4-17@yandex.ru)

🇷🇺 Русский · 🇬🇧 English · 🇩🇪 Deutsch · 🇫🇷 Français · 🇪🇸 Español · 🇮🇹 Italiano