Летнее время

Летнее время (Daylight Saving Time, DST) представляет собой сезонное изменение местного времени, при котором часы переводятся вперёд или назад на один час. В разных странах и регионах правила перехода могут отличаться, а сам переход способен влиять на арифметику дат, сравнение временных интервалов и планирование задач.

В JavaScript работа с датами выполняется через объект Date, который опирается на системную временную зону окружения. Day.js использует этот же механизм, добавляя слой удобного API и плагинов, но не устраняя фундаментальную зависимость от таймзоны операционной системы.


Смещение часового пояса и DST

Основной механизм, через который проявляется летнее время, — это изменение UTC-смещения (offset).

UTC-offset — разница между локальным временем и UTC в минутах.

Пример поведения:

  • зимой: UTC+2 → offset = -120
  • летом: UTC+3 → offset = -180

Изменение смещения происходит автоматически в зависимости от даты, если регион поддерживает переход на летнее время.


Как Day.js хранит время

Day.js не хранит дату как объект с «таймзоной» в классическом смысле. Внутреннее представление — это timestamp (количество миллисекунд с Unix Epoch).

import dayjs from 'dayjs';

const d = dayjs('2026-07-01T12:00:00');

Ключевой момент: вся информация о DST применяется только при интерпретации или форматировании даты относительно локальной среды.


Изменение offset в зависимости от сезона

Day.js позволяет получить текущее смещение через utcOffset().

import dayjs from 'dayjs';

const winter = dayjs('2026-01-15');
const summer = dayjs('2026-07-15');

console.log(winter.utcOffset());
console.log(summer.utcOffset());

Если регион поддерживает летнее время, значения будут отличаться.

Разница offset фактически и есть признак DST:

const diff = summer.utcOffset() - winter.utcOffset();

Определение перехода на летнее время

В Day.js нет отдельного встроенного метода вроде isDST(). Поведение определяется косвенно через сравнение offset в разные периоды года.

Пример логики:

import dayjs from 'dayjs';

function isDST(date) {
  const jan = dayjs(date).month(0).date(1);
  const jul = dayjs(date).month(6).date(1);

  const janOffset = jan.utcOffset();
  const julOffset = jul.utcOffset();

  return date.utcOffset() === Math.min(janOffset, julOffset);
}

Смысл подхода:

  • зимой обычно больше offset (меньше смещение к UTC)
  • летом offset уменьшается (часы «переведены вперёд»)

Проблемы арифметики дат при переходе на летнее время

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

const start = dayjs('2026-03-29T01:30:00');
const result = start.add(2, 'hour');

Если в этот день происходит переход на летнее время, часть локального времени может быть пропущена (например, 02:00–03:00 не существует).

Это приводит к эффектам:

  • неожиданный сдвиг результата
  • «перепрыгивание» через несуществующий час
  • различие между локальной и UTC-арифметикой

Работа через UTC как способ устранения DST-аномалий

Наиболее стабильная модель — выполнение вычислений в UTC.

import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc';

dayjs.extend(utc);

const t1 = dayjs.utc('2026-03-29T00:30:00');
const t2 = t1.add(3, 'hour');

console.log(t2.format());

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

  • отсутствуют переходы летнего времени
  • арифметика линейна
  • одинаковое поведение во всех регионах

Таймзоны и Day.js timezone plugin

Для работы с именованными временными зонами используется плагин timezone:

import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc';
import timezone from 'dayjs/plugin/timezone';

dayjs.extend(utc);
dayjs.extend(timezone);

Пример:

const tokyo = dayjs().tz('Asia/Tokyo');
const berlin = dayjs().tz('Europe/Berlin');

При этом DST учитывается автоматически внутри данных временных зон (при наличии соответствующих правил IANA).


Сравнение локального времени и UTC при DST

При переходе на летнее время локальные даты могут не соответствовать интуитивному ожиданию.

const a = dayjs('2026-06-01T12:00:00');
const b = a.utc();

console.log(a.format());
console.log(b.format());

Разница между значениями всегда равна текущему offset, который может изменяться в течение года.


Неоднозначные и несуществующие локальные времена

Во время перехода на летнее время возникают два типа проблемных интервалов:

Пропущенное время

Например:

  • 02:00–03:00 не существует

При создании даты:

dayjs('2026-03-29T02:30:00');

движок может автоматически сместить время вперёд на ближайшее допустимое значение.


Дублируемое время

При обратном переходе:

  • 01:00–02:00 может встречаться дважды

Это приводит к неоднозначности интерпретации локального времени без явного UTC или timezone контекста.


Использование UTC-offset как диагностического инструмента

Изменение offset позволяет выявлять сезонные сдвиги:

function detectOffsetChanges(year) {
  const months = Array.from({ length: 12 }, (_, i) =>
    dayjs(`${year}-${String(i + 1).padStart(2, '0')}-01`).utcOffset()
  );

  return months;
}

Результат показывает, в какие месяцы происходит переход.


Планирование задач с учётом летнего времени

При расчёте повторяющихся интервалов (cron-подобные задачи, расписания) DST приводит к смещению фактического времени выполнения.

Типичный сценарий:

  • задача выполняется ежедневно в 03:00
  • при переходе на летнее время интервал может сдвинуться на 04:00
  • или один запуск может быть пропущен

Для стабильности применяется:

  • UTC-расписание
  • фиксированные timestamp-интервалы
  • использование timezone с явной привязкой к IANA-правилам

Поведение форматирования при DST

Форматирование даты в Day.js зависит от локальной среды:

dayjs().format('YYYY-MM-DD HH:mm Z');

Суффикс Z отражает текущее смещение, которое может изменяться в зависимости от сезона.


Сравнение дат в разных сезонах

Сравнение дат без учёта UTC может давать неожиданные результаты:

const d1 = dayjs('2026-01-01T12:00:00');
const d2 = dayjs('2026-07-01T12:00:00');

console.log(d2.diff(d1, 'hour'));

Результат включает влияние изменения offset между датами, если операции выполняются в локальном времени.


Практическая модель работы с DST

Для предсказуемого поведения обычно выделяются три уровня обработки:

  • хранение: UTC timestamp
  • вычисления: UTC или timezone-aware слой
  • отображение: локальная временная зона пользователя

Такая модель исключает влияние летнего времени на бизнес-логику и оставляет DST только на уровне презентации.