JSR 310: Источники времени
Прежде чем переходить к более подробному описанию классов, описывающих метки времени, неплохо было бы упомянуть источники времени, хотя на практике они используются не так часто. Источники времени представлены интерфейсом InstantSource и его основной реализацией — Clock.
InstantSource используется для получения количества миллисекунд, прошедших с начала эпохи UNIX в UTC. Кроме этого InstantSource может быть использован для создания экземпляров классов Instant и Clock. Экземпляр InstantSource может быть получен при помощи одного из четырёх статических методов:
system— возвращает источник времени, использующий системные часы.fixed— возвращает фиксированный источник времени на основе переданного экземпляраInstant. Такой источник времени всегда будет возвращать одно и то же время.offset— возвращает источник времени, возвращающий время с указанным смещением.tick— возвращает источник времени, который обновляет время с указанной периодичностью
// Получение системного источника времени var systemInstantSource = InstantSource.system(); // Получение фиксированного источника времени var fixedInstantSource = InstantSource.fixed(Instant.now()); // Получение источника времени, который будет возвращать время на 10 секунд больше системного var offsetInstantSource = InstantSource.offset(systemInstantSource, Duration.ofSeconds(10)); // Получение источника времени, который будет обновлять время раз в десять секунд var tickInstantSource = InstantSource.tick(systemInstantSource, Duration.ofSeconds(10));
Стандартные реализации InstantSource опираются на системное время, однако существуют и сторонние реализации, использующие серверы точного времени (NTP).
InstantSource предоставляет 3 метода:
instant— для получения Instant с текущей для источника меткой времениmillis— для получения количества миллисекунд с начала эпохи UNIX текущей для источника метки времениclock— для получения экземпляраClockдля указанного часового пояса
// Получение Instant от InstantSource
Instant instant = systemInstantSource.instant();
// Получения количества миллисекунд с начала эпохи UNIX
long millis = systemInstantSource.millis();
// Получение Clock с часовым поясом Asia/Yekaterinburg
Clock clock = systemInstantSource.withZone(ZoneId.of("Asia/Yekaterinburg"));Clock аналогичен InstantSource, но учитывает часовой пояс и может быть использован для создания экземпляров любых классов, описывающих метки времени, при помощи метода now(Clock clock). Часовой пояс, указанный при создании экземпляра Clock никак не влияет на значение создаваемых экземпляров Instant, так как последний всегда работает в UTC.
Получить экземпляр Clock можно при помощи статических методов:
systemDefaultZone— получение системных часов с системным часовым поясомsystemUTC— получение системных часов с UTCsystem— получение системных часов с указанным часовым поясомfixed— получение фиксированных часовoffset— получение часов с указанным сдвигомtick— получение часов с указанной периодичностью обновленияtickMillis— получение часов с обновлением раз в n миллисекундtickSeconds— получение часов с обновлением раз в n секундtickMinutes— получение часов с обновлением ра в n минут
// Получение системных часов с системным часовым поясом
var systemDefaultZoneClock = Clock.systemDefaultZone();
// Получение системных часов в UTC
var systemUTCClock = Clock.systemUTC();
// Получение системных часов с часовым поясом Asia/Yekaterinburg
var systemClock = Clock.system(ZoneId.of("Asia/Yekaterinburg"));
// Получение фиксированных часов с часовым поясом Asia/Yekaterinburg
var fixedClock = Clock.fixed(Instant.now(), ZoneId.of("Asia/Yekaterinburg"));
// Получение часов, которые возвращают время на 10 секунд впереди текущего
var offsetClock = Clock.offset(systemDefaultZoneClock, Duration.ofSeconds(10));
// Получение часов с обновлением раз в 10 секунд
var tickClock = Clock.tick(systemUTCClock, Duration.ofSeconds(10));
// Получение часов с обновлением раз в миллисекунду с часовым поясом Asia/Yekaterinburg
var tickMillisClock = Clock.tickMillis(ZoneId.of("Asia/Yekaterinburg"));
// Получение часов с обновлением раз в секунду с часовым поясом Asia/Yekaterinburg
var tickSecondsClock = Clock.tickSeconds(ZoneId.of("Asia/Yekaterinburg"));
// Получение часов с обновлением раз в минуту с часовым поясом Asia/Yekaterinburg
var tickMinutesClock = Clock.tickMinutes(ZoneId.of("Asia/Yekaterinburg"));Аналогично InstantSource класс Clock предоставляет доступ к Instant и количеству миллисекунд с начала эпохи UNIX текущей для часов метки времени. Кроме этого предоставляются методы getZone и withZone для получения часового пояса часов и создания копии часов с указанным часовым поясом соответственно.
// Получение Instant
Instant instant = systemDefaultZoneClock.instant();
// Получения количества миллисекунд с начала эпохи UNIX
long millis = systemDefaultZoneClock.millis();
// Получение копии Clock с часовым поясом Asia/Novosibirsk
Clock clock = systemDefaultZoneClock.withZone(ZoneId.of("Asia/Novosibirsk"));
// Получение часового пояса часов
ZoneId zone = sytemDefaultZoneClock.getZone();Источники времени могут быть полезны, если вы хотите больше контроля над кодом. Например, такой код сложно тестировать точно:
record SomeEntity(Instant createdAt) {
}
class SomeService {
SomeEntity createEntity() {
return new SomeEntity(Instant.now());
}
}Максимум вы сможете написать такой тест:
class SomeTest {
SomeService service = new SomeService();
@Test
void createEntity_ReturnValidEntity() {
var entity = someService.createEntity();
// Вы не можете проверить значение timestamp,
// так как не имеете доступа к механизму создания Instant
assertNotNull(entity.createdAt());
}
}Однако используя источники времени вы можете получить более контролируемый код:
class SomeService {
private final Clock clock;
SomeService(Clock clock) {
this.clock = clock;
}
SomeEntity createEntity() {
return new SomeEntity(Instant.now(this.clock));
}
}И этот код можно покрыть более точными тестами:
class SomeTest {
static final Instant INSTANT = Instant.parse("2026-06-15T14:07:00Z");
SomeService service = new SomeService(Clock.fixed(Instant, ZoneOffset.UTC));
@Test
void createEntity_ReturnValidEntity() {
var entity = someService.createEntity();
// Теперь вы можете проверить значение свойства createdAt
assertEquals(INSTANT, entity.createdAt());
}
}