Библиотека js-joda работает с иммутабельными типами даты и времени,
полностью отделяя календарные вычисления от представления в виде
системного времени JavaScript. В отличие от встроенного объекта
Date, который хранит момент времени в миллисекундах UTC,
js-joda оперирует строго типизированными сущностями:
LocalDate, LocalTime,
LocalDateTime, Instant,
ZonedDateTime.
Конвертация в Date всегда сводится к одному ключевому
моменту: получение абсолютного времени (Instant) и его
преобразование в миллисекунды эпохи Unix.
Instant — это универсальное представление точки на
временной шкале в UTC без привязки к часовому поясу.
Именно Instant является центральным звеном при
конвертации в Date.
const jsDate = instant.toDate();
Метод toDate() доступен для Instant (и
некоторых других типов через зависимости js-joda) и возвращает
стандартный Date.
const jsDate = new Date(instant.toEpochMilli());
Такой вариант используется, когда требуется явный контроль над числовым представлением времени.
ZonedDateTime содержит дату, время и временную зону, что
делает его наиболее полным типом для конвертации.
const jsDate = zonedDateTime.toDate();
Это самый корректный и безопасный способ, поскольку учитывается смещение временной зоны.
const jsDate = new Date(zonedDateTime.toInstant().toEpochMilli());
Логика преобразования:
DateLocalDateTime не содержит информации о временной зоне,
поэтому прямого однозначного преобразования в Date не
существует.
Необходима явная привязка к зоне:
const jsDate = localDateTime
.atZone(zoneId)
.toInstant()
.toDate();
Без указания зоны результат будет зависеть от выбранного
ZoneId, например:
import { ZoneId } from '@js-joda/core';
const zone = ZoneId.of('Europe/Berlin');
LocalDate содержит только календарную дату без
времени.
Для преобразования требуется выбрать момент начала дня:
const jsDate = localDate
.atStartOfDay(zoneId)
.toInstant()
.toDate();
Можно задать конкретное время:
const jsDate = localDate
.atTime(12, 0)
.atZone(zoneId)
.toInstant()
.toDate();
LocalTime не содержит даты, поэтому преобразование
требует фиктивной даты:
const jsDate = localTime
.atDate(localDate)
.atZone(zoneId)
.toInstant()
.toDate();
Без комбинирования с датой объект Date получить
невозможно.
Во всех типах, которые могут быть сведены к Instant,
доступны методы работы с эпохой Unix.
const millis = instant.toEpochMilli();
const date = new Date(millis);
const seconds = instant.getEpochSecond();
const date = new Date(seconds * 1000);
Использование секунд требует явного умножения, так как
Date оперирует миллисекундами.
Хотя задача касается преобразования в Date, важно
учитывать обратный путь для полноты модели данных.
import { Instant } from '@js-joda/core';
const instant = Instant.ofEpochMilli(date.getTime());
Дальнейшее преобразование:
const zoned = instant.atZone(zoneId);
const localDateTime = zoned.toLocalDateTime();
const localDate = zoned.toLocalDate();
Ошибки при преобразовании чаще всего возникают из-за игнорирования временной зоны.
Instant — всегда UTC, не зависит от зоныDate — также хранит UTC, но отображается в локальной
зоне среды выполненияLocalDate/LocalDateTime — не содержат информации о
зонеLocalDate / LocalDateTime
↓ + ZoneId
ZonedDateTime
↓
Instant
↓
Date
При переходе из js-joda в Date возможна потеря
данных:
LocalDate теряет информацию о времениLocalTime теряет датуZonedDateTime теряет исходную зону (остается только
момент времени)Date не хранит структурированную календарную
модельconst date = localDate
.atStartOfDay(zoneId)
.toInstant()
.toDate();
const date = zonedDateTime.toInstant().toDate();
const date = LocalDateTime.parse(input)
.atZone(ZoneId.of('UTC'))
.toInstant()
.toDate();
// некорректно
new Date(localDateTime.toString());
// некорректно
localDate.toDate(); // не существует
new Date(instant.getEpochSecond()); // ошибка: секунды вместо миллисекунд
Instant как промежуточного слоя
минимизирует ошибкиZoneId делает поведение
предсказуемым.toDate() предпочтителен там, где он
доступен