Летнее время (DST, Daylight Saving Time) — механизм сезонного перевода часов, при котором локальное время смещается вперёд или назад. В большинстве регионов перевод выполняется два раза в год:
Для программирования это создаёт ряд проблем:
Luxon предоставляет встроенные механизмы для корректной работы с такими сценариями.
Luxon использует:
DateTime для работы с датой и временем;Intl браузера или Node.js;Пример создания даты в конкретной зоне:
import { DateTime } from "luxon";
const dt = DateTime.now().setZone("Europe/Berlin");
console.log(dt.toString());
Именно временная зона определяет:
Для проверки используется свойство isInDST.
const dt = DateTime.now().setZone("Europe/Berlin");
console.log(dt.isInDST);
Результат:
true
или:
false
Это особенно полезно при:
Свойство offset возвращает смещение относительно UTC в
минутах.
const winter = DateTime.fromISO("2025-01-15", {
zone: "Europe/Berlin"
});
const summer = DateTime.fromISO("2025-07-15", {
zone: "Europe/Berlin"
});
console.log(winter.offset);
console.log(summer.offset);
Результат:
60
120
Зимой используется UTC+1, летом — UTC+2.
UTC не подвержен летнему времени.
const utc = DateTime.now().toUTC();
console.log(utc.isInDST);
Результат:
false
Работа в UTC позволяет избежать большинства проблем DST:
const createdAt = DateTime.utc();
Типичный подход:
Во время весеннего перевода часов часть локального времени исчезает.
Например:
02:00 → 03:00
Промежуток между 02:00 и 02:59 не существует.
const dt = DateTime.fromObject(
{
year: 2025,
month: 3,
day: 30,
hour: 2,
minute: 30
},
{
zone: "Europe/Berlin"
}
);
console.log(dt.toString());
Luxon автоматически корректирует значение:
2025-03-30T03:30:00.000+02:00
Время 02:30 не существовало, поэтому произошло смещение
вперёд.
Некоторые операции могут приводить к невалидным объектам.
Для проверки используется:
dt.isValid
Пример:
if (!dt.isValid) {
console.log(dt.invalidReason);
}
Дополнительные свойства:
console.log(dt.invalidExplanation);
Это особенно важно при:
Осенью часы переводятся назад:
03:00 → 02:00
Из-за этого промежуток времени повторяется дважды.
Например:
02:30
может относиться:
const dt = DateTime.fromISO(
"2025-10-26T02:30",
{
zone: "Europe/Berlin"
}
);
console.log(dt.toString());
Luxon выбирает одно из возможных значений согласно правилам временной зоны.
Метод offsetNameShort возвращает краткое название
зоны.
const dt = DateTime.now().setZone("Europe/Berlin");
console.log(dt.offsetNameShort);
Возможный результат:
CET
или:
CEST
Полное название:
console.log(dt.offsetNameLong);
Например:
Central European Summer Time
Метод setZone() позволяет переводить время между
зонами.
const utc = DateTime.utc();
const berlin = utc.setZone("Europe/Berlin");
const tokyo = utc.setZone("Asia/Tokyo");
console.log(berlin.toString());
console.log(tokyo.toString());
Luxon автоматически учитывает DST для каждой зоны.
По умолчанию Luxon сохраняет абсолютный момент времени.
Иногда требуется сохранить именно локальное время.
Для этого используется:
keepLocalTime: true
Пример:
const dt = DateTime.fromISO(
"2025-07-10T10:00",
{
zone: "UTC"
}
);
const converted = dt.setZone(
"Europe/Berlin",
{
keepLocalTime: true
}
);
console.log(converted.toString());
Это изменяет интерпретацию времени, а не сам момент времени.
DST влияет на вычисления продолжительности.
const dt = DateTime.fromISO(
"2025-03-30T00:00",
{
zone: "Europe/Berlin"
}
);
const result = dt.plus({ hours: 24 });
console.log(result.toString());
Во время перехода на летнее время сутки могут длиться 23 часа.
plus({ days: 1 }) и plus({ hours: 24 })Это один из важнейших аспектов работы с DST.
dt.plus({ days: 1 })
Luxon перемещает дату на следующий календарный день.
dt.plus({ hours: 24 })
Добавляется фиксированное количество часов.
При DST результаты могут различаться.
const dt = DateTime.fromISO(
"2025-03-29T12:00",
{
zone: "Europe/Berlin"
}
);
console.log(
dt.plus({ days: 1 }).toString()
);
console.log(
dt.plus({ hours: 24 }).toString()
);
Во время смены времени итоговые часы могут отличаться.
Метод diff() учитывает DST автоматически.
const start = DateTime.fromISO(
"2025-03-29T12:00",
{
zone: "Europe/Berlin"
}
);
const end = DateTime.fromISO(
"2025-03-30T12:00",
{
zone: "Europe/Berlin"
}
);
const diff = end.diff(start, "hours");
console.log(diff.toObject());
Результат:
{ hours: 23 }
Хотя календарно прошёл один день.
Для надёжных вычислений часто используется схема:
Пример:
const local = DateTime.now().setZone("Europe/Berlin");
const utc = local.toUTC();
const updated = utc.plus({ hours: 24 });
const result = updated.setZone("Europe/Berlin");
console.log(result.toString());
Такой подход минимизирует ошибки при DST.
DST особенно опасен для:
Задача должна запускаться ежедневно в 02:30.
Но:
Наиболее надёжный подход:
const timestamp = DateTime.utc();
Преимущества:
Для событий календаря важно хранить:
Пример:
{
time: "2025-07-10T09:00",
zone: "Europe/Berlin"
}
Это позволяет корректно учитывать будущие переходы DST.
Нежелательно использовать:
UTC+2
Правильнее:
Europe/Berlin
Потому что offset меняется в зависимости от сезона.
Luxon напрямую не предоставляет API для получения даты следующего перехода, однако это можно вычислить вручную.
Пример:
let dt = DateTime.now().setZone("Europe/Berlin");
const currentOffset = dt.offset;
while (dt.offset === currentOffset) {
dt = dt.plus({ days: 1 });
}
console.log(dt.toString());
const dt = DateTime.now().setZone("Europe/Berlin");
console.log(dt.isValid);
Если зона не поддерживается:
const invalid = DateTime.now().setZone("Invalid/Zone");
console.log(invalid.isValid);
ISO-формат может содержать offset:
2025-07-10T10:00:00+02:00
или временную зону:
2025-07-10T10:00:00 Europe/Berlin
Luxon корректно интерпретирует DST:
const dt = DateTime.fromISO(
"2025-07-10T10:00:00+02:00"
);
console.log(dt.toString());
const dt = DateTime.now().toUTC();
console.log(dt.toISO());
Результат:
2025-07-10T08:00:00.000Z
const local = DateTime.now()
.setZone("Europe/Berlin");
console.log(local.toISO());
Результат:
2025-07-10T10:00:00.000+02:00
startOf() и endOf() во время DSTМетоды:
startOf()
endOf()
учитывают особенности временной зоны.
Пример:
const dt = DateTime.fromISO(
"2025-03-30",
{
zone: "Europe/Berlin"
}
);
console.log(dt.startOf("day").toString());
console.log(dt.endOf("day").toString());
Продолжительность суток может отличаться от 24 часов.
Две даты могут иметь:
Пример:
const a = DateTime.fromISO(
"2025-07-10T10:00:00+02:00"
);
const b = DateTime.fromISO(
"2025-07-10T08:00:00Z"
);
console.log(a.toMillis() === b.toMillis());
Результат:
true
Luxon предоставляет объект Interval.
import { Interval } from "luxon";
const start = DateTime.fromISO(
"2025-03-29T12:00",
{
zone: "Europe/Berlin"
}
);
const end = DateTime.fromISO(
"2025-03-30T12:00",
{
zone: "Europe/Berlin"
}
);
const interval = Interval.fromDateTimes(start, end);
console.log(interval.length("hours"));
Результат:
23
При тестировании рекомендуется:
2025-03-30
или:
2025-10-26
для Европы.
Проблема:
2025-07-10 10:00
без указания зоны неоднозначно.
Плохо:
UTC+2
Хорошо:
Europe/Berlin
Опасный подход:
timestamp + 86400000
Из-за DST сутки могут быть:
Во время осеннего перехода одно и то же локальное время встречается дважды.
Это может вызывать:
next = current.plus({ hours: 24 });
Из-за DST время уведомления может смещаться.
next = current.plus({ days: 1 });
Так сохраняется календарное локальное время.
const meeting = {
title: "Standup",
localTime: "2025-07-10T09:00",
zone: "Europe/Berlin"
};
При отображении:
const dt = DateTime.fromISO(
meeting.localTime,
{
zone: meeting.zone
}
);
console.log(dt.toString());
После перехода на зимнее время событие всё равно останется в
09:00 локального времени.
plus({ days: 1 }) для календарных
событий;isValid;