В стандартном объекте Date в JavaScript одновременно
смешиваются несколько понятий:
Такой подход часто приводит к ошибкам:
const date = new Date('2025-03-10');
console.log(date.toString());
console.log(date.toISOString());
В разных часовых поясах результат может отличаться. Особенно проблемными становятся:
Библиотека js-joda реализует подход из Java java.time,
где каждая сущность времени представлена отдельным типом.
Это фундаментальный принцип библиотеки:
Такое разделение устраняет большую часть ошибок, связанных с временем.
LocalDate хранит исключительно календарную дату:
Без:
const { LocalDate } = require('@js-joda/core');
const date = LocalDate.of(2025, 5, 24);
console.log(date.toString());
Результат:
2025-05-24
LocalDate подходит для:
Дата не зависит от временной зоны.
const date = LocalDate.parse('2025-05-24');
console.log(date);
Во всех странах это будет одна и та же дата.
LocalTime хранит:
Без даты и временной зоны.
const { LocalTime } = require('@js-joda/core');
const time = LocalTime.of(14, 30, 15);
console.log(time.toString());
Результат:
14:30:15
Подходит для:
LocalDateTime объединяет:
Но всё ещё не содержит временную зону.
const { LocalDateTime } = require('@js-joda/core');
const dateTime = LocalDateTime.of(
2025,
5,
24,
14,
30
);
console.log(dateTime.toString());
Результат:
2025-05-24T14:30
Это локальное представление времени.
Например:
«24 мая 2025 года в 14:30»
Но без информации:
Одно и то же локальное время может существовать в разных зонах одновременно.
Например:
2025-05-24T14:30
может означать:
Это разные реальные моменты времени.
Поэтому LocalDateTime нельзя использовать как абсолютную
временную метку.
ZonedDateTime хранит:
const {
ZonedDateTime,
ZoneId
} = require('@js-joda/core');
require('@js-joda/timezone');
const zoned = ZonedDateTime.of(
2025,
5,
24,
14,
30,
0,
0,
ZoneId.of('Asia/Almaty')
);
console.log(zoned.toString());
Результат:
2025-05-24T14:30+05:00[Asia/Almaty]
Временная зона — это не просто смещение.
Неверный подход:
+05:00
Полноценная зона содержит:
Например:
Europe/Berlin
America/New_York
Asia/Tokyo
В библиотеке разделяются два понятия.
Фиксированное смещение:
+05:00
-03:00
UTC
Пример:
const { ZoneOffset } = require('@js-joda/core');
const offset = ZoneOffset.of('+05:00');
Полноценная временная зона:
const { ZoneId } = require('@js-joda/core');
const zone = ZoneId.of('Europe/Berlin');
ZoneId учитывает:
Instant представляет точку времени в UTC.
Это самый точный и безопасный тип для:
const { Instant } = require('@js-joda/core');
const instant = Instant.now();
console.log(instant.toString());
Пример:
2025-05-24T09:20:15.120Z
Суффикс Z означает UTC.
const {
LocalDate,
LocalTime
} = require('@js-joda/core');
const date = LocalDate.of(2025, 5, 24);
const time = LocalTime.of(14, 30);
const dateTime = date.atTime(time);
console.log(dateTime.toString());
const {
LocalDateTime,
ZoneId
} = require('@js-joda/core');
require('@js-joda/timezone');
const local = LocalDateTime.parse(
'2025-05-24T14:30'
);
const zoned = local.atZone(
ZoneId.of('Asia/Almaty')
);
console.log(zoned.toString());
const instant = zoned.toInstant();
console.log(instant.toString());
Дата рождения — это календарная дата.
Неверный подход:
Instant.parse('1995-08-10T00:00:00Z')
Проблемы:
Правильный тип:
LocalDate
Допустим:
Магазин открывается в 09:00.
Это локальное время, а не глобальный момент.
Если хранить как Instant, то после смены часового пояса
возникнут ошибки.
Правильный тип:
LocalTime
Пользователю нужен локальный формат времени.
Instant не содержит:
Поэтому для UI обычно выполняется преобразование:
const zoned = instant.atZone(
ZoneId.of('Asia/Almaty')
);
Для поддержки зон требуется:
require('@js-joda/timezone');
Без этого доступны только фиксированные offset-зоны.
const {
ZonedDateTime,
ZoneId
} = require('@js-joda/core');
require('@js-joda/timezone');
const tokyo = ZonedDateTime.now(
ZoneId.of('Asia/Tokyo')
);
const berlin = tokyo.withZoneSameInstant(
ZoneId.of('Europe/Berlin')
);
console.log(tokyo.toString());
console.log(berlin.toString());
Сохраняет реальный момент времени.
Меняется локальное отображение.
const berlin = tokyo.withZoneSameInstant(
ZoneId.of('Europe/Berlin')
);
Сохраняет локальное время.
Меняется фактический момент времени.
const berlin = tokyo.withZoneSameLocal(
ZoneId.of('Europe/Berlin')
);
Это крайне важное различие.
DST (Daylight Saving Time) создаёт множество сложностей.
Например, время может:
Во время перехода на летнее время некоторые часы исчезают.
Пример:
2025-03-30 02:30
в некоторых странах не существует.
js-joda умеет корректно обрабатывать такие случаи.
При возврате с летнего времени одно и то же локальное время встречается дважды.
Например:
2025-10-26 02:30
может соответствовать двум разным Instant.
Встроенный Date:
Пример проблемы:
const date = new Date();
date.setHours(date.getHours() + 1);
Объект изменяется напрямую.
Все объекты библиотеки immutable.
Пример:
const {
LocalDate
} = require('@js-joda/core');
const original = LocalDate.parse('2025-05-24');
const modified = original.plusDays(5);
console.log(original.toString());
console.log(modified.toString());
Результат:
2025-05-24
2025-05-29
Исходный объект не изменился.
Разделение типов помогает:
| Задача | Тип |
|---|---|
| День рождения | LocalDate |
| Время открытия | LocalTime |
| Локальное событие | LocalDateTime |
| Международное событие | ZonedDateTime |
| Timestamp в БД | Instant |
Обычно хранится:
Instant
или UTC timestamp.
Используются:
InstantZonedDateTimeИспользуются:
LocalDateLocalTimeZonedDateTimeПользователь вводит:
const local = LocalDateTime.of(
2025,
5,
24,
19,
0
);
const zoned = local.atZone(
ZoneId.of('Asia/Almaty')
);
const instant = zoned.toInstant();
instant.toString()
Например:
2025-05-24T14:00:00Z
const berlinTime = instant.atZone(
ZoneId.of('Europe/Berlin')
);
Каждый пользователь увидит своё локальное время.
Это один из главных принципов современной работы со временем.
То, как время воспринимает человек:
Типы:
LocalDateLocalTimeLocalDateTimeТо, как время хранится системой:
Тип:
Instant
Ошибка:
2025-05-24T09:00Z
при попытке представить:
«магазин открывается в 09:00»
После смены зоны время станет неверным.
Например:
2025-05-24T18:00
Непонятно:
Тип объекта показывает смысл данных.
Меньше скрытых преобразований.
Поведение не зависит от локальной среды.
Система легче развивается при международной работе.