Clock для управления временем

Clock в js-joda представляет собой абстракцию источника текущего времени. Вместо прямого обращения к системным часам используется объект, который инкапсулирует получение текущего момента и зоны времени. Такой подход позволяет отделить бизнес-логику от реального времени, упростить тестирование и обеспечить воспроизводимость временных вычислений.

Основная идея заключается в том, что время в приложении перестаёт быть глобальной зависимостью. Вместо вызовов, завязанных на окружение, используется явный объект Clock, который можно подменять, фиксировать или сдвигать.

В классических подходах к работе с датой и временем код напрямую обращается к системным функциям, получая текущее время в момент вызова. Это создаёт скрытую зависимость от внешнего состояния системы.

Clock в js-joda решает эту проблему, предоставляя унифицированный источник времени, который:

  • возвращает текущий Instant
  • учитывает временную зону
  • может быть зафиксирован на определённом моменте
  • может имитировать смещение времени
  • легко подменяется в тестах

В результате временная логика становится детерминированной.

Базовый контракт Clock

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 с учётом локальных правил времени, включая переходы на летнее время.

Получение текущего Instant

Clock используется как источник для создания моментальных меток времени.

import { Instant, Clock } from '@js-joda/core';

const clock = Clock.systemUTC();
const instant = Instant.now(clock);

Instant представляет собой абсолютную точку на временной шкале, не зависящую от зоны. Clock лишь определяет, какой именно момент считается текущим.

Фиксированный 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 со смещением

Clock позволяет создавать смещённые версии текущего времени. Это используется для моделирования будущего или прошлого.

import { Clock, Duration, ZoneId } from '@js-joda/core';

const baseClock = Clock.systemUTC();
const offsetClock = Clock.offset(baseClock, Duration.ofHours(5));

offsetClock всегда возвращает время, которое на 5 часов больше базового. Такой механизм удобен для моделирования временных сценариев, например:

  • симуляции будущих событий
  • проверки истечения сроков
  • тестирования бизнес-логики с задержками

Tick Clock и дискретизация времени

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

Clock активно используется для создания ZonedDateTime, так как объединяет момент времени и зону.

import { ZonedDateTime, Clock } from '@js-joda/core';

const clock = Clock.systemUTC();

const zdt = ZonedDateTime.now(clock);

Здесь ZonedDateTime формируется на основе текущего Instant, полученного из Clock, и его зоны.

Тестируемость через 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 и иммутабельность

Clock в js-joda является неизменяемым объектом. Любые операции, такие как offset или tick, создают новый экземпляр Clock, не модифицируя исходный.

Это обеспечивает:

  • предсказуемость поведения
  • безопасность в многопоточных сценариях
  • отсутствие скрытых побочных эффектов

Каждый Clock можно рассматривать как чистую функцию, возвращающую время в заданных условиях.

Сравнение типов Clock

Существуют несколько основных разновидностей Clock:

  • systemUTC — системное время в UTC
  • systemDefaultZone — системное время в локальной зоне
  • fixed — фиксированное время
  • offset — смещённое время
  • tick — дискретизированное время

Каждый тип решает отдельный класс задач. systemUTC подходит для серверных приложений, fixed — для тестов, offset — для моделирования сценариев, tick — для временных интервалов.

Использование Clock в доменной логике

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 и распределённые системы

В распределённых системах Clock помогает стандартизировать временные вычисления между сервисами. Использование UTC Clock минимизирует расхождения, связанные с локальными настройками времени.

Особенно важно:

  • устранение проблем с часовыми поясами
  • согласованность логов
  • единая временная шкала событий

Clock становится точкой синхронизации временных операций.

Поведение при смене зоны

Clock может быть связан с конкретной зоной, но сам Instant остаётся независимым от неё. Это означает, что изменение ZoneId влияет только на представление времени, но не на сам момент.

const clock = Clock.system(ZoneId.of('UTC'));

Переключение зоны требует создания нового Clock, что подчёркивает неизменяемость модели.

Практическое применение абстракции времени

Clock используется как базовый строительный блок в логике, где время играет роль:

  • ограничения доступа
  • истечения подписок
  • планирования задач
  • расчёта задержек
  • аудита событий

В каждом случае Clock служит источником истины о текущем времени, но не содержит бизнес-логики.

Сочетание Clock с Duration и Instant

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 в архитектуре приложений

В архитектурном плане Clock выполняет роль инфраструктурного адаптера. Он отделяет доменную логику от системных вызовов времени.

Такой подход приводит к:

  • снижению связанности компонентов
  • улучшению тестируемости
  • контролируемости временных сценариев
  • воспроизводимости поведения системы