Определение разницы между датами

Разница между датами в работе с календарными системами требует строгого разделения понятий «календарный интервал» и «временная длительность». В экосистеме js-joda эти концепции реализованы через разные типы и функции: Period, Duration, а также утилиты ChronoUnit и методы between / until.


Календарные даты и временные точки

Вся модель расчётов опирается на различие типов:

  • LocalDate — дата без времени и часового пояса
  • LocalDateTime — дата и время без часового пояса
  • ZonedDateTime — дата и время с часовым поясом
  • Instant — абсолютная точка времени в UTC

Разница между датами интерпретируется по-разному в зависимости от используемого типа.


Календарная разница через Period

Period применяется для вычисления разницы в календарных единицах: годы, месяцы, дни. Он учитывает структуру календаря, а не фиксированное количество времени.

Пример вычисления периода между датами

import { LocalDate, Period } from '@js-joda/core';

const start = LocalDate.parse('2020-01-15');
const end = LocalDate.parse('2023-04-10');

const period = Period.between(start, end);

Объект period содержит:

  • years: 3
  • months: 2
  • days: 26

Ключевая особенность: разложение происходит по календарным границам, а не через общее количество дней.


Поведение Period при неоднородных месяцах

При расчётах учитывается фактическая длина месяцев:

const a = LocalDate.parse('2023-01-31');
const b = LocalDate.parse('2023-03-01');

const p = Period.between(a, b);

Результат будет зависеть от нормализации календарных переходов, а не от фиксированного количества дней.


Временная разница через Duration

Duration оперирует абсолютным временем, измеряемым в секундах и наносекундах. Он используется для Instant, LocalDateTime, ZonedDateTime.

Базовый пример

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

const start = LocalDateTime.parse('2023-01-01T10:00:00');
const end = LocalDateTime.parse('2023-01-01T12:30:00');

const duration = Duration.between(start, end);

Результат:

  • 2 часа 30 минут

Перевод в единицы измерения

duration.toHours();        // 2
duration.toMinutes();      // 150
duration.getSeconds();     // 9000

Duration всегда выражается как точное количество времени без календарной логики.


ChronoUnit для вычисления разницы

ChronoUnit предоставляет более низкоуровневый способ вычисления разницы между датами.

Разница в днях

import { LocalDate, ChronoUnit } from '@js-joda/core';

const start = LocalDate.parse('2023-01-01');
const end = LocalDate.parse('2023-01-10');

const days = ChronoUnit.DAYS.between(start, end);

Результат: 9


Разница в разных единицах

ChronoUnit.MONTHS.between(start, end);
ChronoUnit.YEARS.between(start, end);
ChronoUnit.WEEKS.between(start, end);

Каждая единица вычисляется по собственным правилам календарной нормализации.


Метод until как альтернатива between

Многие классы предоставляют метод until, который возвращает разницу до другой даты.

Пример

const start = LocalDate.parse('2023-01-01');
const end = LocalDate.parse('2023-01-10');

const days = start.until(end, ChronoUnit.DAYS);

Поведение эквивалентно ChronoUnit.DAYS.between(start, end), но с инверсией порядка вызова.


Разница для ZonedDateTime и влияние часовых поясов

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

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

const zone = ZoneId.of('Europe/Berlin');

const start = ZonedDateTime.of(
  2023, 3, 25, 10, 0, 0, 0, zone
);

const end = ZonedDateTime.of(
  2023, 3, 26, 10, 0, 0, 0, zone
);

const duration = Duration.between(start, end);

Несмотря на одинаковый «локальный интервал» в 24 часа, фактическая длительность может отличаться из-за перехода времени.


Различие между Period и Duration

Характеристика Period Duration
Единицы годы, месяцы, дни секунды, наносекунды
Основа календарь абсолютное время
Часовые пояса игнорируются учитываются
Типы LocalDate Instant, LocalDateTime

Комбинированные вычисления

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

Пример разбиения разницы

const startDate = LocalDate.parse('2020-01-01');
const endDate = LocalDate.parse('2023-03-15');

const period = Period.between(startDate, endDate);
const days = ChronoUnit.DAYS.between(startDate, endDate);

Period даёт структуру (годы/месяцы/дни), а ChronoUnit — общее количество дней.


Нормализация результатов

При переходе между единицами происходит автоматическая нормализация:

  • 60 секунд → 1 минута
  • 24 часа → 1 день (в Duration контексте)
  • 12 месяцев → 1 год (в Period контексте)

Типичные ошибки при вычислении разницы

Смешивание Period и Duration

Duration.between(LocalDate.parse('2023-01-01'), LocalDate.parse('2023-01-10'));

Такой вызов некорректен, так как Duration требует временных типов.


Игнорирование часовых поясов

Разница между двумя LocalDateTime не учитывает фактические смещения зоны.


Ожидание фиксированного количества дней в месяце

ChronoUnit.DAYS.between(
  LocalDate.parse('2023-02-01'),
  LocalDate.parse('2023-03-01')
);

Результат зависит от календарного месяца, а не фиксированного интервала.


Использование преобразований между типами

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

const instant = Instant.now();
const zoned = instant.atZone(ZoneId.of('UTC'));

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


Сравнение стратегий вычисления разницы

  • Period — анализ жизненных интервалов (возраст, стаж, календарные периоды)
  • Duration — измерение времени выполнения, задержек, таймингов
  • ChronoUnit — быстрые скалярные вычисления между датами
  • until — синтаксический сахар над ChronoUnit