TemporalQuery для извлечения информации

Интерфейс TemporalQuery в библиотеке js-joda предназначен для извлечения данных из объектов временной модели через универсальный механизм запросов. Он используется совместно с методом query() и позволяет получать информацию о временном объекте без прямого обращения к его внутренней структуре.

Подход основан на концепции из Java Time API: объект времени предоставляет данные, а запрос (TemporalQuery) определяет, какую именно информацию необходимо извлечь.

Основная сигнатура:

temporal.query(query)

Где:

  • temporal — любой объект, реализующий TemporalAccessor
  • query — функция или объект запроса

Пример:

const { LocalDate, TemporalQueries } = require('@js-joda/core')

const date = LocalDate.parse('2025-03-15')

const chronology = date.query(TemporalQueries.chronology())

console.log(chronology)

Интерфейс TemporalAccessor

TemporalQuery работает только с объектами, реализующими интерфейс TemporalAccessor.

К таким объектам относятся:

  • LocalDate
  • LocalTime
  • LocalDateTime
  • ZonedDateTime
  • OffsetDateTime
  • Year
  • YearMonth
  • MonthDay
  • Instant

Каждый из них предоставляет метод:

query(query)

Внутри метод передаёт текущий объект в запрос.

Упрощённая схема:

query.queryFrom(temporal)

Структура TemporalQuery

В Js-joda запрос обычно представляет собой функцию.

Простейший пример:

const { LocalDate } = require('@js-joda/core')

const date = LocalDate.now()

const query = temporal => temporal.toString()

const result = date.query(query)

console.log(result)

Здесь:

  • объект LocalDate передаётся в функцию
  • функция возвращает строковое представление даты

Готовые запросы TemporalQueries

Библиотека содержит набор стандартных запросов в классе TemporalQueries.

Подключение:

const { TemporalQueries } = require('@js-joda/core')

Доступные запросы:

Запрос Назначение
chronology() календарная система
localDate() извлечение LocalDate
localTime() извлечение LocalTime
offset() временное смещение
precision() точность временного объекта
zone() зона
zoneId() идентификатор зоны

Извлечение LocalDate

Запрос localDate() извлекает дату из объекта.

Пример с LocalDateTime:

const {
    LocalDateTime,
    TemporalQueries
} = require('@js-joda/core')

const dt = LocalDateTime.parse('2025-04-10T12:30:45')

const date = dt.query(TemporalQueries.localDate())

console.log(date.toString())

Результат:

2025-04-10

Извлечение LocalTime

Аналогично работает localTime().

const {
    LocalDateTime,
    TemporalQueries
} = require('@js-joda/core')

const dt = LocalDateTime.parse('2025-04-10T12:30:45')

const time = dt.query(TemporalQueries.localTime())

console.log(time.toString())

Результат:

12:30:45

Получение зоны времени

zone()

Запрос возвращает объект зоны времени.

const {
    ZonedDateTime,
    ZoneId,
    TemporalQueries
} = require('@js-joda/core')

const zdt = ZonedDateTime.now(
    ZoneId.of('Europe/Berlin')
)

const zone = zdt.query(TemporalQueries.zone())

console.log(zone.toString())

zoneId()

Извлекает идентификатор зоны.

const zoneId = zdt.query(
    TemporalQueries.zoneId()
)

console.log(zoneId.id())

Результат:

Europe/Berlin

Получение UTC-смещения

Запрос offset() возвращает ZoneOffset.

const {
    OffsetDateTime,
    ZoneOffset,
    TemporalQueries
} = require('@js-joda/core')

const dt = OffsetDateTime.now(
    ZoneOffset.ofHours(3)
)

const offset = dt.query(
    TemporalQueries.offset()
)

console.log(offset.toString())

Результат:

+03:00

Определение точности объекта

precision()

Позволяет узнать минимальную единицу времени, поддерживаемую объектом.

const {
    LocalDate,
    TemporalQueries
} = require('@js-joda/core')

const date = LocalDate.now()

const precision = date.query(
    TemporalQueries.precision()
)

console.log(precision.toString())

Для LocalDate результат:

Days

Для LocalTime:

Nanos

Собственные запросы

Главная ценность TemporalQuery — возможность создавать специализированные запросы.


Извлечение номера квартала

const { LocalDate } = require('@js-joda/core')

const quarterQuery = temporal => {
    const month = temporal.getMonthValue()

    return Math.ceil(month / 3)
}

const date = LocalDate.parse('2025-08-11')

console.log(date.query(quarterQuery))

Результат:

3

Извлечение названия сезона

const { LocalDate } = require('@js-joda/core')

const seasonQuery = temporal => {
    const month = temporal.getMonthValue()

    if ([12, 1, 2].includes(month)) {
        return 'winter'
    }

    if ([3, 4, 5].includes(month)) {
        return 'spring'
    }

    if ([6, 7, 8].includes(month)) {
        return 'summer'
    }

    return 'autumn'
}

const date = LocalDate.parse('2025-10-20')

console.log(date.query(seasonQuery))

Использование сложных объектов

TemporalQuery может возвращать любые структуры данных.


Возврат объекта

const { ZonedDateTime } = require('@js-joda/core')

const infoQuery = temporal => ({
    year: temporal.year(),
    month: temporal.monthValue(),
    day: temporal.dayOfMonth()
})

const now = ZonedDateTime.now()

console.log(now.query(infoQuery))

Результат:

{
    year: 2025,
    month: 5,
    day: 25
}

Проверка поддерживаемых полей

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

Например:

  • LocalDate не содержит времени
  • LocalTime не содержит даты
  • Year содержит только год

Безопасная проверка

const {
    ChronoField,
    LocalDate
} = require('@js-joda/core')

const safeHourQuery = temporal => {
    if (
        temporal.isSupported(
            ChronoField.HOUR_OF_DAY
        )
    ) {
        return temporal.get(
            ChronoField.HOUR_OF_DAY
        )
    }

    return null
}

const date = LocalDate.now()

console.log(date.query(safeHourQuery))

Результат:

null

Комбинирование запросов

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


Пример композиции

const {
    ZonedDateTime
} = require('@js-joda/core')

const datePartQuery = temporal => ({
    year: temporal.year(),
    month: temporal.monthValue(),
    day: temporal.dayOfMonth()
})

const timePartQuery = temporal => ({
    hour: temporal.hour(),
    minute: temporal.minute()
})

const zdt = ZonedDateTime.now()

console.log(zdt.query(datePartQuery))
console.log(zdt.query(timePartQuery))

Использование с Instant

Instant содержит только UTC-время без календарной системы и зоны.

Поэтому некоторые запросы возвращают null.

const {
    Instant,
    TemporalQueries
} = require('@js-joda/core')

const instant = Instant.now()

console.log(
    instant.query(
        TemporalQueries.localDate()
    )
)

Результат:

null

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


Поведение query()

Метод query():

  1. принимает объект запроса
  2. передаёт текущий temporal-объект
  3. возвращает результат функции

Упрощённая модель:

query(query) {
    return query.queryFrom(this)
}

В Js-joda вместо полноценного интерфейса обычно используется функция.


Практический пример: анализ временного объекта

const {
    ZonedDateTime,
    TemporalQueries
} = require('@js-joda/core')

const zdt = ZonedDateTime.now()

const analysisQuery = temporal => ({
    date: temporal.query(
        TemporalQueries.localDate()
    ),

    time: temporal.query(
        TemporalQueries.localTime()
    ),

    zone: temporal.query(
        TemporalQueries.zoneId()
    ),

    precision: temporal.query(
        TemporalQueries.precision()
    )
})

console.log(
    zdt.query(analysisQuery)
)

Универсальный запрос даты

const {
    ChronoField
} = require('@js-joda/core')

const universalDateQuery = temporal => {
    const supportsDate =
        temporal.isSupported(
            ChronoField.YEAR
        )

    if (!supportsDate) {
        return null
    }

    return {
        year: temporal.get(
            ChronoField.YEAR
        ),

        month: temporal.get(
            ChronoField.MONTH_OF_YEAR
        ),

        day: temporal.get(
            ChronoField.DAY_OF_MONTH
        )
    }
}

Такой запрос можно применять к:

  • LocalDate
  • LocalDateTime
  • ZonedDateTime
  • OffsetDateTime

Отличие TemporalQuery от TemporalField

Эти механизмы решают разные задачи.

TemporalField

Используется для получения конкретного поля:

date.get(ChronoField.YEAR)

Возвращает строго определённое значение.


TemporalQuery

Используется для извлечения произвольной информации:

date.query(customQuery)

Может возвращать:

  • строки
  • числа
  • объекты
  • массивы
  • структуры данных
  • вычисляемые значения

Когда использовать TemporalQuery

TemporalQuery особенно полезен в следующих случаях:

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

Ограничения

Отсутствие мутации

TemporalQuery только читает данные.

Изменение temporal-объектов невозможно.


Возможность null

Некоторые запросы не применимы к определённым типам.

Например:

Instant.now().query(
    TemporalQueries.zone()
)

Вернёт:

null

Необходимость проверки поддержки полей

При написании универсальных запросов всегда требуется учитывать:

temporal.isSupported(field)

Без этой проверки возможны исключения.


Создание библиотеки запросов

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

Пример:

// queries.js

exports.quarterQuery = temporal => {
    return Math.ceil(
        temporal.getMonthValue() / 3
    )
}

exports.isWeekendQuery = temporal => {
    const day = temporal.dayOfWeek().value()

    return day === 6 || day === 7
}

Использование:

const {
    quarterQuery,
    isWeekendQuery
} = require('./queries')

const date = LocalDate.now()

console.log(date.query(quarterQuery))
console.log(date.query(isWeekendQuery))

Архитектурная роль TemporalQuery

TemporalQuery реализует паттерн Query Object.

Преимущества подхода:

  • отделение логики извлечения данных
  • переиспользование запросов
  • независимость от конкретного temporal-типа
  • расширяемость
  • упрощение тестирования

Механизм особенно полезен при построении:

  • систем календарей
  • расписаний
  • аналитики времени
  • временных DSL
  • бизнес-правил для дат и времени