Clock в js-joda представляет собой абстракцию источника текущего времени. Вместо прямого обращения к системным часам используется объект, который инкапсулирует получение текущего момента и зоны времени. Такой подход позволяет отделить бизнес-логику от реального времени, упростить тестирование и обеспечить воспроизводимость временных вычислений.
Основная идея заключается в том, что время в приложении перестаёт быть глобальной зависимостью. Вместо вызовов, завязанных на окружение, используется явный объект Clock, который можно подменять, фиксировать или сдвигать.
В классических подходах к работе с датой и временем код напрямую обращается к системным функциям, получая текущее время в момент вызова. Это создаёт скрытую зависимость от внешнего состояния системы.
Clock в js-joda решает эту проблему, предоставляя унифицированный источник времени, который:
В результате временная логика становится детерминированной.
Clock в js-joda работает как поставщик текущего времени в виде Instant и привязанной зоны ZoneId. Любой экземпляр Clock реализует две ключевые характеристики:
Через него формируются объекты Instant, ZonedDateTime и другие временные структуры библиотеки.
Наиболее часто используется системный Clock, который отражает реальное текущее время операционной системы.
import { Clock, Instant } from '@js-joda/core';
const clock = Clock.systemUTC();
const now = Instant.now(clock);
Здесь создаётся Clock, привязанный к UTC. Каждый вызов Instant.now(clock) получает актуальное время.
Альтернативный вариант — использование локальной временной зоны окружения:
import { Clock } from '@js-joda/core';
const clock = Clock.systemDefaultZone();
В этом случае Clock учитывает настройки системы, включая региональные смещения.
Clock всегда работает в контексте ZoneId. Это позволяет согласовать время с конкретным географическим или логическим регионом.
import { Clock, ZoneId } from '@js-joda/core';
const zone = ZoneId.of('Europe/Berlin');
const clock = Clock.system(zone);
Такой подход обеспечивает корректное преобразование Instant в ZonedDateTime с учётом локальных правил времени, включая переходы на летнее время.
Clock используется как источник для создания моментальных меток времени.
import { Instant, Clock } from '@js-joda/core';
const clock = Clock.systemUTC();
const instant = Instant.now(clock);
Instant представляет собой абсолютную точку на временной шкале, не зависящую от зоны. Clock лишь определяет, какой именно момент считается текущим.
Одним из ключевых инструментов является фиксированный Clock, который всегда возвращает один и тот же момент времени.
import { Clock, Instant, ZoneId } from '@js-joda/core';
const fixedInstant = Instant.parse('2025-01-01T00:00:00Z');
const zone = ZoneId.of('UTC');
const clock = Clock.fixed(fixedInstant, zone);
Такой Clock полезен для тестирования, где требуется предсказуемость времени. Независимо от количества вызовов, результат не изменяется.
Это устраняет проблему нестабильных тестов, связанных с реальным временем.
Clock позволяет создавать смещённые версии текущего времени. Это используется для моделирования будущего или прошлого.
import { Clock, Duration, ZoneId } from '@js-joda/core';
const baseClock = Clock.systemUTC();
const offsetClock = Clock.offset(baseClock, Duration.ofHours(5));
offsetClock всегда возвращает время, которое на 5 часов больше базового. Такой механизм удобен для моделирования временных сценариев, например:
Clock.tick позволяет дискретизировать время с заданным шагом. Это важно для систем, где требуется округление времени до фиксированного интервала.
import { Clock, ZoneId, Duration } from '@js-joda/core';
const zone = ZoneId.of('UTC');
const baseClock = Clock.system(zone);
const tickClock = Clock.tick(baseClock, Duration.ofSeconds(10));
В этом случае время «скачет» с шагом 10 секунд. Любой вызов будет возвращать ближайшее значение, кратное указанному интервалу.
Это используется в:
Clock активно используется для создания ZonedDateTime, так как объединяет момент времени и зону.
import { ZonedDateTime, Clock } from '@js-joda/core';
const clock = Clock.systemUTC();
const zdt = ZonedDateTime.now(clock);
Здесь ZonedDateTime формируется на основе текущего Instant, полученного из Clock, и его зоны.
Одним из ключевых преимуществ Clock является возможность инъекции зависимости времени.
Вместо прямого вызова системного времени логика принимает Clock как параметр:
import { Instant } from '@js-joda/core';
function isExpired(clock, deadlineInstant) {
const now = Instant.now(clock);
return now.isAfter(deadlineInstant);
}
В реальной среде передаётся системный Clock:
const result = isExpired(Clock.systemUTC(), deadline);
В тестах используется фиксированный Clock:
const clock = Clock.fixed(
Instant.parse('2025-01-01T00:00:00Z'),
ZoneId.of('UTC')
);
const result = isExpired(clock, Instant.parse('2024-12-31T23:59:59Z'));
Такой подход полностью устраняет нестабильность тестов, связанную с текущим временем.
Clock в js-joda является неизменяемым объектом. Любые операции, такие как offset или tick, создают новый экземпляр Clock, не модифицируя исходный.
Это обеспечивает:
Каждый Clock можно рассматривать как чистую функцию, возвращающую время в заданных условиях.
Существуют несколько основных разновидностей Clock:
Каждый тип решает отдельный класс задач. systemUTC подходит для серверных приложений, fixed — для тестов, offset — для моделирования сценариев, tick — для временных интервалов.
Clock позволяет переносить работу со временем внутрь доменной модели без привязки к окружению.
Пример проверки активности сессии:
import { Instant } from '@js-joda/core';
function isSessionActive(clock, lastActivity, timeoutMinutes) {
const now = Instant.now(clock);
const expiration = lastActivity.plusMinutes(timeoutMinutes);
return now.isBefore(expiration);
}
Такая реализация полностью отделяет вычисления от источника времени.
В распределённых системах Clock помогает стандартизировать временные вычисления между сервисами. Использование UTC Clock минимизирует расхождения, связанные с локальными настройками времени.
Особенно важно:
Clock становится точкой синхронизации временных операций.
Clock может быть связан с конкретной зоной, но сам Instant остаётся независимым от неё. Это означает, что изменение ZoneId влияет только на представление времени, но не на сам момент.
const clock = Clock.system(ZoneId.of('UTC'));
Переключение зоны требует создания нового Clock, что подчёркивает неизменяемость модели.
Clock используется как базовый строительный блок в логике, где время играет роль:
В каждом случае Clock служит источником истины о текущем времени, но не содержит бизнес-логики.
Clock часто используется вместе с Duration и Instant для построения временных вычислений.
import { Clock, Instant, Duration } from '@js-joda/core';
const clock = Clock.systemUTC();
const now = Instant.now(clock);
const future = now.plus(Duration.ofMinutes(30));
Здесь Clock определяет базовую точку отсчёта, а Duration задаёт смещение.
В архитектурном плане Clock выполняет роль инфраструктурного адаптера. Он отделяет доменную логику от системных вызовов времени.
Такой подход приводит к: