Библиотека Moment.js долгое время использовалась как стандарт де-факто для работы с датами и временем в JavaScript. Однако её архитектура основана на изменяемых объектах и «монолитном» подходе к API, что приводит к проблемам масштабируемости, предсказуемости и поддержки в современных приложениях.
js-joda представляет собой реализацию подхода Java Time API (JSR-310) в JavaScript, ориентированную на строгую типизацию концепций времени, неизменяемость и композиционность. Переход между этими библиотеками требует пересмотра привычных паттернов работы с датами.
Ключевые отличия:
Moment.js использует единую сущность moment, которая
объединяет дату, время, таймзону и операции над ними. В js-joda каждое
понятие выделено отдельно:
LocalDate — дата без времениLocalTime — время без датыLocalDateTime — дата и время без зоныZonedDateTime — дата, время и часовой поясInstant — момент времени в UTCТакой подход исключает неоднозначности, возникающие при смешивании контекстов.
Пример различия:
// Moment.js
const m = moment("2025-01-01T10:00:00");
// js-joda
const dateTime = LocalDateTime.parse("2025-01-01T10:00:00");
Moment.js изменяет существующий объект:
const m = moment();
m.add(1, 'day');
В js-joda каждое изменение возвращает новый объект:
const today = LocalDate.now();
const tomorrow = today.plusDays(1);
Следствие архитектуры:
Moment.js:
moment("2024-06-01");
js-joda:
LocalDate.parse("2024-06-01");
Moment.js:
moment();
js-joda:
LocalDate.now();
Moment.js:
moment().year();
moment().month();
moment().date();
js-joda:
const date = LocalDate.now();
date.year();
date.monthValue();
date.dayOfMonth();
Moment.js:
moment().add(10, 'days');
moment().subtract(2, 'months');
js-joda:
const d = LocalDate.now();
d.plusDays(10);
d.minusMonths(2);
Moment.js изменяет объект, js-joda возвращает новый экземпляр. Это критично при переносе бизнес-логики, завязанной на цепочечные вызовы.
Moment.js активно использует шаблоны:
moment().format("YYYY-MM-DD HH:mm");
js-joda требует явного форматтера:
import { DateTimeFormatter } from '@js-joda/core';
const formatter = DateTimeFormatter.ofPattern('yyyy-MM-dd HH:mm');
LocalDateTime.now().format(formatter);
Особенности перехода:
YYYY →
yyyy)Moment.js часто использует plugin moment-timezone:
moment.tz("2024-01-01", "Europe/Moscow");
js-joda выделяет зоны в отдельный тип:
import { ZonedDateTime, ZoneId } from '@js-joda/core';
const zone = ZoneId.of("Europe/Moscow");
const zdt = ZonedDateTime.now(zone);
Ключевое отличие:
Moment.js:
moment.utc();
js-joda:
Instant.now();
Для преобразования:
const zdt = ZonedDateTime.now();
const instant = zdt.toInstant();
Moment.js использует duration и
moment.duration:
moment.duration(5, 'days');
js-joda:
import { Duration } from '@js-joda/core';
Duration.ofDays(5);
Для дат используется Period:
import { Period } from '@js-joda/core';
Period.ofMonths(2);
Разделение важно:
Duration — точное время (секунды, наносекунды)Period — календарные единицы (дни, месяцы, годы)Moment.js:
momentA.isBefore(momentB);
momentA.isAfter(momentB);
js-joda:
dateA.isBefore(dateB);
dateA.isAfter(dateB);
Дополнительно:
dateA.equals(dateB);
Moment.js допускает гибкий парсинг:
moment("01/02/2024", "DD/MM/YYYY");
js-joda требует явного форматтера:
const formatter = DateTimeFormatter.ofPattern('dd/MM/yyyy');
LocalDate.parse("01/02/2024", formatter);
Следствие:
const d = LocalDate.now();
d.plusDays(1); // результат не сохраняется
Корректно:
const d2 = d.plusDays(1);
YYYY-MM-DD // неверно для js-joda
Корректно:
yyyy-MM-dd
Moment.js часто позволяет неявные преобразования типов, js-joda требует явных конверсий между сущностями:
LocalDateTime.now().toLocalDate();
LocalDate.now().atStartOfDay();
Логика работы со временем выносится в отдельный модуль, исключая прямое использование Moment.js в бизнес-слое.
На переходном этапе возможно сосуществование:
import moment from 'moment';
import { LocalDate } from '@js-joda/core';
const legacy = moment();
const modern = LocalDate.now();
| Moment.js | js-joda |
|---|---|
| moment() | LocalDateTime.now() |
| moment().add() | plusDays / plusMonths |
| moment().format() | format(formatter) |
| moment.utc() | Instant.now() |
| moment.tz() | ZonedDateTime + ZoneId |
| moment.duration() | Duration / Period |
Переход приводит к изменению логики:
Такой подход уменьшает количество скрытых ошибок, связанных с датами, особенно в распределённых системах и приложениях с несколькими часовыми поясами