Введение
Работа с датами и временем — одна из самых частых, но и самых коварных задач в веб-разработке. Стандартный объект Date в JavaScript, несмотря на свою универсальность, имеет ряд ограничений: отсутствие встроенной поддержки часовых поясов (кроме UTC и локального времени), неудобные методы для форматирования и сложности с вычислением разницы между датами. В этой статье мы разберём продвинутые техники работы с датами в среде Node.js, которые помогут вам писать надёжный и понятный код.
1. Intl.DateTimeFormat: Мощное форматирование без сторонних библиотек
Современный JavaScript предоставляет встроенный API Intl.DateTimeFormat, который позволяет гибко форматировать даты и время с учётом локали. Это идеальный инструмент, если вам не нужна вся мощь сторонних библиотек.
Базовое использование
Создайте экземпляр форматтера, передав локаль и объект с опциями:
const date = new Date('2024-12-25T10:30:00Z');
// Форматирование для русской локали
const ruFormatter = new Intl.DateTimeFormat('ru-RU', {
year: 'numeric',
month: 'long',
day: 'numeric',
weekday: 'long',
hour: '2-digit',
minute: '2-digit',
timeZone: 'Europe/Moscow'
});
console.log(ruFormatter.format(date));
// Вывод: "среда, 25 декабря 2024 г. в 13:30"
Работа с часовыми поясами
Ключевое преимущество Intl.DateTimeFormat — поддержка часовых поясов через опцию timeZone. Вы можете отображать одну и ту же дату в разных поясах:
const date = new Date();
const formatters = {
'Нью-Йорк': new Intl.DateTimeFormat('en-US', { timeZone: 'America/New_York', dateStyle: 'full', timeStyle: 'long' }),
'Токио': new Intl.DateTimeFormat('ja-JP', { timeZone: 'Asia/Tokyo', dateStyle: 'full', timeStyle: 'long' }),
'Лондон': new Intl.DateTimeFormat('en-GB', { timeZone: 'Europe/London', dateStyle: 'full', timeStyle: 'long' })
};
for (const [city, fmt] of Object.entries(formatters)) {
console.log(`${city}: ${fmt.format(date)}`);
}
Форматирование диапазонов дат
API также умеет форматировать диапазоны с помощью метода formatRange:
const start = new Date('2024-01-01');
const end = new Date('2024-01-05');
const rangeFormatter = new Intl.DateTimeFormat('ru-RU', {
month: 'long',
day: 'numeric'
});
console.log(rangeFormatter.formatRange(start, end));
// Вывод: "1–5 января"
2. Манипуляции с датами: прибавление, вычитание и разница
Стандартный Date плохо подходит для арифметики. Рассмотрим два подхода: чистый JavaScript и популярные библиотеки.
Чистый JavaScript: работа с миллисекундами
Самый простой способ — перевести дату в Unix timestamp (миллисекунды с 1 января 1970 года) и выполнить операции:
function addDays(date, days) {
const result = new Date(date);
result.setTime(result.getTime() + days * 24 * 60 * 60 * 1000);
return result;
}
function diffDays(date1, date2) {
const msDiff = Math.abs(date2.getTime() - date1.getTime());
return Math.floor(msDiff / (1000 * 60 * 60 * 24));
}
const today = new Date('2024-10-01');
const future = addDays(today, 30);
console.log(future); // 2024-10-31
console.log(diffDays(today, future)); // 30
Недостаток: не учитывает переход на летнее время, високосные секунды и разную длину месяцев.
Библиотека date-fns: функциональный и лёгкий подход
date-fns — модульная библиотека, в которой каждая функция — отдельный модуль. Это позволяет не раздувать бандл:
// Установка: npm install date-fns
const { addDays, subMonths, differenceInDays, format } = require('date-fns');
const { ru } = require('date-fns/locale');
const startDate = new Date(2024, 0, 15); // 15 января 2024
const plusWeek = addDays(startDate, 7);
const minusQuarter = subMonths(startDate, 3);
console.log(format(plusWeek, 'dd.MM.yyyy', { locale: ru })); // 22.01.2024
console.log(format(minusQuarter, 'dd MMMM yyyy', { locale: ru })); // 15 октября 2024
const endDate = new Date(2024, 11, 31);
console.log(`Дней между датами: ${differenceInDays(endDate, startDate)}`); // 351
Библиотека Luxon: объектно-ориентированный подход
Luxon от создателя Moment.js предлагает более удобный API с иммутабельными объектами:
// Установка: npm install luxon
const { DateTime, Interval } = require('luxon');
// Создание даты с часовым поясом
const dt = DateTime.fromISO('2024-03-15T14:30:00', { zone: 'Europe/Moscow' });
console.log(dt.toFormat('dd.MM.yyyy HH:mm')); // 15.03.2024 14:30
// Манипуляции
const future = dt.plus({ months: 2, days: 5 });
console.log(future.toFormat('dd.MM.yyyy')); // 20.05.2024
// Разница между датами
const start = DateTime.fromISO('2024-01-01');
const end = DateTime.fromISO('2024-12-31');
const diff = end.diff(start, ['months', 'days']);
console.log(`Разница: ${diff.months} месяцев и ${diff.days} дней`); // Разница: 11 месяцев и 30 дней
// Проверка, входит ли дата в интервал
const interval = Interval.fromDateTimes(start, end);
console.log(interval.contains(DateTime.fromISO('2024-06-15'))); // true
3. Работа с часовыми поясами и Unix timestamp
Одна из самых частых ошибок — путаница между UTC и локальным временем. Рассмотрим, как правильно работать с часовыми поясами на сервере.
Получение текущего времени в UTC и определённом поясе
const { DateTime } = require('luxon');
// Текущее время в UTC
const utcNow = DateTime.utc();
console.log(utcNow.toISO()); //