Введение: Почему работа с датами в TypeScript требует особого внимания
Работа с датами и временем — одна из самых частых и в то же время коварных задач в веб-разработке. В TypeScript, который является строго типизированной надстройкой над JavaScript, объект Date унаследовал все "прелести" своего прародителя: неинтуитивные месяцы (отсчёт с нуля), мутабельность методов и проблемы с часовыми поясами. Однако, используя строгую типизацию и современные библиотеки, вы можете писать безопасный, предсказуемый и читаемый код для работы с временем.
В этой статье мы разберём всё, что нужно знать разработчику: от базового создания дат до продвинутой работы с таймзонами и форматированием. Вы узнаете, как избежать типичных ошибок и какие инструменты (встроенные и сторонние) лучше всего подходят для TypeScript-проектов.
1. Основы: Объект Date и его типизация в TypeScript
Создание и базовые методы
В TypeScript, как и в JavaScript, для работы с датами используется встроенный объект Date. TypeScript предоставляет для него готовые типы, поэтому дополнительных определений не требуется.
// Создание текущей даты
const now: Date = new Date();
console.log(now); // 2024-01-15T10:30:00.000Z
// Создание из строки (ISO 8601)
const specificDate: Date = new Date('2024-12-31T23:59:59');
// Создание из компонентов (год, месяц [0-11], день, часы...)
const birthday: Date = new Date(1990, 0, 15); // 15 января 1990
// Внимание: месяц 0 = Январь!
Проблема с мутабельностью
Методы объекта Date изменяют исходный объект. Это может привести к неожиданным багам. TypeScript не может это предотвратить на уровне типов, но вы можете использовать иммутабельные подходы.
const date = new Date('2024-01-01');
date.setDate(date.getDate() + 5); // мутация! date теперь 6 января
// Лучше создавать новый объект:
const newDate = new Date(date.getTime() + 5 * 24 * 60 * 60 * 1000);
Типизация Unix Timestamp
Часто даты передаются как числа (миллисекунды с 1970 года). Используйте type alias для ясности:
type UnixTimestamp = number;
function formatTimestamp(ts: UnixTimestamp): string {
return new Date(ts).toISOString();
}
const myTimestamp: UnixTimestamp = 1705315200000;
console.log(formatTimestamp(myTimestamp));
2. Форматирование и парсинг дат
Встроенные методы форматирования
JavaScript предоставляет базовые методы для локализованного вывода. В TypeScript они работают "из коробки":
const date = new Date('2024-06-15T14:30:00');
// Локализованные строки
console.log(date.toLocaleDateString('ru-RU')); // "15.06.2024"
console.log(date.toLocaleTimeString('en-US')); // "2:30:00 PM"
console.log(date.toLocaleString('de-DE')); // "15.6.2024, 14:30:00"
// ISO-строка (удобно для API)
console.log(date.toISOString()); // "2024-06-15T11:30:00.000Z" (всегда UTC)
Парсинг строк: опасности
Парсинг строк через конструктор new Date(str) может давать неожиданные результаты в разных браузерах. Лучше использовать Date.parse() с явной проверкой или сторонние библиотеки.
// Ненадёжный способ
const parsed = new Date('2024/01/15'); // может работать нестабильно
// Надёжный способ: ISO 8601
const safeParse = (dateStr: string): Date | null => {
const ts = Date.parse(dateStr);
return isNaN(ts) ? null : new Date(ts);
};
const result = safeParse('2024-01-15T10:00:00Z');
if (result) {
console.log(result.toISOString());
}
3. Продвинутые манипуляции и сравнение
Сравнение дат
Даты можно сравнивать напрямую через операторы сравнения, но для точного сравнения (равенства) нужно приводить к числу:
const date1 = new Date('2024-01-01');
const date2 = new Date('2024-01-01');
// Неправильно: сравниваются ссылки
console.log(date1 === date2); // false
// Правильно: сравниваем timestamp
console.log(date1.getTime() === date2.getTime()); // true
// Проверка "раньше/позже"
console.log(date1 < new Date('2025-01-01')); // true
Добавление и вычитание времени
TypeScript не предоставляет удобных методов для арифметики дат. Приходится работать с миллисекундами:
function addDays(date: Date, days: number): Date {
const result = new Date(date);
result.setDate(result.getDate() + days);
return result;
}
function addMonths(date: Date, months: number): Date {
const result = new Date(date);
result.setMonth(result.getMonth() + months);
return result;
}
const today = new Date();
const nextWeek = addDays(today, 7);
const nextQuarter = addMonths(today, 3);
Расчёт разницы между датами
function daysBetween(date1: Date, date2: Date): number {
const diffTime = Math.abs(date2.getTime() - date1.getTime());
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
return diffDays;
}
const start = new Date('2024-01-01');
const end = new Date('2024-12-31');
console.log(daysBetween(start, end)); // 365
4. Лучшие практики: библиотеки для TypeScript
Для серьёзных проектов встроенного Date часто недостаточно. Вот две лучшие библиотеки, которые отлично работают с TypeScript:
date-fns (рекомендуется)
Модульная, функциональная, иммутабельная. Идеальна для TypeScript благодаря отличной поддержке типов.
import { format, addDays, differenceInDays, isAfter } from 'date-fns';
const date = new Date('2024-06-15');
console.log(format(date, 'dd.MM.yyyy')); // "15.06.2024"
console.log(addDays(date, 10)); // 25 июня
console.log(differenceIn