Работа с датой и временем в TypeScript: подробное руководство
Что это такое и зачем? (аналогия из реальной жизни)
Представь, что ты ведёшь дневник. Каждый день ты записываешь, что произошло: «Сегодня, 15 марта, я встретился с другом в 14:30». Если бы ты просто писал «встретился с другом», ты бы быстро запутался — когда именно это было? Вчера? На прошлой неделе? Дата и время — это как координаты в пространстве, только не географические, а временные.
В программировании работа с датой и временем нужна везде:
- Социальные сети — показывают «опубликовано 5 минут назад»
- Банковские приложения — фиксируют время перевода денег
- Календари — напоминают о встречах
- Игры — отсчитывают время до следующего хода
- Серверы — логируют ошибки с точной датой
Важно: Дата и время — это не просто «циферки на часах». Это сложная система, которая учитывает часовые пояса, високосные годы, летнее время и даже то, что в разных странах неделя может начинаться с разных дней!
В TypeScript (как и в JavaScript) работа с датами — это отдельная наука. Мы будем использовать встроенный объект Date, который умеет всё: от создания текущей даты до сложных математических операций с временем.
Теория: как это работает изнутри
В основе всех дат в JavaScript/TypeScript лежит Unix-время (или timestamp). Это количество миллисекунд, прошедших с 1 января 1970 года 00:00:00 по UTC (всемирное координированное время).
Почему именно 1970 год? Потому что тогда придумали Unix — операционную систему, которая стала основой для многих современных систем. Разработчики решили: «Пусть время отсчитывается от этой даты». И теперь весь мир программирования использует этот стандарт.
Что такое UTC? Это «мировое время» без привязки к часовому поясу. Раньше использовали GMT (Гринвич), но UTC точнее. Когда ты видишь время «14:00 UTC» — это время в Лондоне зимой. В Москве зимой будет 17:00 (UTC+3), а летом 18:00 (UTC+3, но с учётом летнего времени).
Объект Date внутри хранит одно единственное число — количество миллисекунд. Все остальные методы (получить год, месяц, день, часы) просто преобразуют это число в понятный человеку формат.
Запомни: Компьютеры любят числа, а люди — календари. Date — это переводчик между миром машин и миром людей.
В TypeScript есть ещё одна особенность — типизация. Если в JavaScript ты можешь случайно передать строку вместо числа и получить странный результат, то TypeScript предупредит тебя об ошибке на этапе написания кода.
Синтаксис в TypeScript: все формы и варианты
Создание объекта Date
Есть 4 основных способа создать дату:
| Способ | Пример | Результат |
|---|---|---|
| Текущая дата и время | new Date() |
Сейчас, в текущем часовом поясе |
| По миллисекундам | new Date(1700000000000) |
Дата, соответствующая timestamp |
| По строке | new Date("2024-03-15T14:30:00") |
Дата из строки (ISO формат) |
| По компонентам | new Date(2024, 2, 15, 14, 30, 0) |
15 марта 2024, 14:30:00 |
Внимание: Месяцы в JavaScript считаются с 0! Январь — это 0, февраль — 1, март — 2, декабрь — 11. Это одна из самых частых ошибок новичков.
Основные методы объекта Date
Все методы делятся на две группы: геттеры (получить значение) и сеттеры (установить значение).
| Метод | Описание | Пример |
|---|---|---|
getFullYear() |
Год (4 цифры) | 2024 |
getMonth() |
Месяц (0-11) | 2 (март) |
getDate() |
День месяца (1-31) | 15 |
getDay() |
День недели (0-6, где 0 — воскресенье) | 5 (пятница) |
getHours() |
Часы (0-23) | 14 |
getMinutes() |
Минуты (0-59) | 30 |
getSeconds() |
Секунды (0-59) | 0 |
getMilliseconds() |
Миллисекунды (0-999) | 0 |
getTime() |
Timestamp (миллисекунды с 1970) | 1710505800000 |
Для установки значений используются методы с приставкой set: setFullYear(), setMonth(), setDate() и так далее.
Форматирование даты
TypeScript (через JavaScript) предлагает несколько способов превратить дату в строку:
| Метод | Пример вывода |
|---|---|
toString() |
"Fri Mar 15 2024 14:30:00 GMT+0300 (Москва, стандартное время)" |
toDateString() |
"Fri Mar 15 2024" |
toTimeString() |
"14:30:00 GMT+0300 (Москва, стандартное время)" |
toISOString() |
"2024-03-15T11:30:00.000Z" (всегда UTC) |
toLocaleDateString() |
"15.03.2024" (зависит от локали) |
toLocaleTimeString() |
"14:30:00" (зависит от локали) |
Совет: Для отображения даты пользователю используй toLocaleDateString() — он автоматически подстраивается под формат страны пользователя.
Примеры кода — от простого к сложному
Пример 1: Получаем текущую дату и время
// Создаём объект с текущей датой и временем
const now: Date = new Date();
// Выводим в консоль в разных форматах
console.log("Текущая дата:", now.toString());
console.log("Только дата:", now.toDateString());
console.log("Только время:", now.toTimeString());
console.log("В формате ISO:", now.toISOString());
// Получаем отдельные компоненты
const year: number = now.getFullYear();
const month: number = now.getMonth() + 1; // +1, потому что месяцы с 0
const day: number = now.getDate();
const hours: number = now.getHours();
const minutes: number = now.getMinutes();
console.log(`Сегодня: ${day}.${month}.${year}, время: ${hours}:${minutes}`);
Объяснение: Мы создаём объект Date без аргументов — он автоматически берёт текущее время системы. Затем используем разные методы для форматирования. Обрати внимание: getMonth() возвращает 0-11, поэтому мы прибавляем 1 для удобства чтения.
Пример 2: Создаём конкретную дату
// Создаём дату 1 января 2025 года, 12:00:00
// Внимание: месяц 0 = январь!
const newYear: Date = new Date(2025, 0, 1, 12, 0, 0);
console.log("Новый год:", newYear.toLocaleDateString("ru-RU"));
console.log("День недели:", newYear.getDay()); // 3 = среда
// Проверяем, какой это день недели
const daysOfWeek: string[] = [
"Воскресенье", "Понедельник", "Вторник",
"Среда", "Четверг", "Пятница", "Суббота"
];
console.log(`1 января 2025 — это ${daysOfWeek[newYear.getDay()]}`);
Объяснение: Мы передаём 6 чисел: год, месяц (0-11), день (1-31), часы (0-23), минуты (0-59), секунды (0-59). Если не указать часы/минуты/секунды, они будут равны 0. Массив daysOfWeek помогает перевести числовой день недели в понятное название.
Пример 3: Вычисляем разницу между датами
// Дата начала курса
const startDate: Date = new Date(2024, 8, 1); // 1 сентября 2024
// Текущая дата
const currentDate: Date = new Date();
// Разница в миллисекундах
const diffMs: number = currentDate.getTime() - startDate.getTime();
// Переводим в дни (1 день = 24 * 60 * 60 * 1000 миллисекунд)
const diffDays: number = Math.floor(diffMs / (1000 * 60 * 60 * 24));
console.log(`С начала курса прошло ${diffDays} дней`);
// Если нужно в часах
const diffHours: number = Math.floor(diffMs / (1000 * 60 * 60));
console.log(`Или ${diffHours} часов`);
// Если нужно в минутах
const diffMinutes: number = Math.floor(diffMs / (1000 * 60));
console.log(`Или ${diffMinutes} минут`);
Объяснение: getTime() возвращает timestamp — количество миллисекунд. Вычитая один timestamp из другого, мы получаем разницу в миллисекундах. Затем делим на количество миллисекунд в дне, часе или минуте. Math.floor() округляет вниз, чтобы получить целое число.
Пример 4: Добавляем дни к дате
// Сегодняшняя дата
const today: Date = new Date();
// Хотим узнать дату через 7 дней
const nextWeek: Date = new Date(today);
nextWeek.setDate(today.getDate() + 7);
console.log(`Сегодня: ${today.toLocaleDateString("ru-RU")}`);
console.log(`Через неделю: ${nextWeek.toLocaleDateString("ru-RU")}`);
// Добавляем месяц
const nextMonth: Date = new Date(today);
nextMonth.setMonth(today.getMonth() + 1);
console.log(`Через месяц: ${nextMonth.toLocaleDateString("ru-RU")}`);
// Добавляем год
const nextYear: Date = new Date(today);
nextYear.setFullYear(today.getFullYear() + 1);
console.log(`Через год: ${nextYear.toLocaleDateString("ru-RU")}`);
Объяснение: Мы создаём копию даты с помощью new Date(today), чтобы не изменять оригинал. Затем используем сеттеры: setDate(), setMonth(), setFullYear(). JavaScript сам обрабатывает переход через границы месяцев и лет. Например, если сегодня 31 января и мы добавим 1 месяц, получим 28 февраля (или 29 в високосный год).
Пример 5: Сравнение дат
const date1: Date = new Date(2024, 5, 15); // 15 июня 2024
const date2: Date = new Date(2024, 5, 20); // 20 июня 2024
// Сравниваем напрямую (работает, потому что Date преобразуется в число)
if (date1 < date2) {
console.log("date1 раньше date2");
} else if (date1 > date2) {
console.log("date1 позже date2");
} else {
console.log("Даты равны");
}
// Проверяем, равна ли дата (так не работает!)
const date3: Date = new Date(2024, 5, 15);
console.log(date1 === date3); // false! Это разные объекты
// Правильный способ сравнения на равенство
console.log(date1.getTime() === date3.getTime()); // true
Объяснение: Операторы < и > работают с датами, потому что JavaScript преобразует объект Date в число (timestamp) при сравнении. Но оператор === сравнивает ссылки на объекты, а не значения. Поэтому для проверки равенства нужно сравнивать getTime().
Пример 6: Форматирование с помощью Intl.DateTimeFormat
const meetingDate: Date = new Date(2024, 11, 25, 15, 30); // 25 декабря 2024, 15:30
// Создаём форматтер для русского языка
const russianFormatter: Intl.DateTimeFormat = new Intl.DateTimeFormat("ru-RU", {
year: "numeric",
month: "long",
day: "numeric",
weekday: "long",
hour: "2-digit",
minute: "2-digit"
});
console.log(russianFormatter.format(meetingDate));
// Вывод: "среда, 25 декабря 2024 г., 15:30"
// Для английского языка
const englishFormatter: Intl.DateTimeFormat = new Intl.DateTimeFormat("en-US", {
year: "numeric",
month: "long",
day: "numeric",
weekday: "long",
hour: "2-digit",
minute: "2-digit"
});
console.log(englishFormatter.format(meetingDate));
// Вывод: "Wednesday, December 25, 2024 at 03:30 PM"
Объяснение: Intl.DateTimeFormat — это мощный инструмент для форматирования дат с учётом локали. Мы передаём язык (ru-RU, en-US) и настройки форматирования. month: "long" показывает полное название месяца, weekday: "long" — полное название дня недели.
Пример 7: Таймер обратного отсчёта
// Функция для расчёта оставшегося времени до указанной даты
function getTimeRemaining(endDate: Date): object {
const total: number = endDate.getTime() - new Date().getTime();
// Если время уже прошло
if (total <= 0) {
return { days: 0, hours: 0, minutes: 0, seconds: 0, total: 0 };
}
const seconds: number = Math.floor((total / 1000) % 60);
const minutes: number = Math.floor((total / 1000 / 60) % 60);
const hours: number = Math.floor((total / (1000 * 60 * 60)) % 24);
const days: number = Math.floor(total / (1000 * 60 * 60 * 24));
return { total, days, hours, minutes, seconds };
}
// Пример использования: сколько осталось до Нового года
const newYearDate: Date = new Date(2025, 0, 1);
const remaining: object = getTimeRemaining(newYearDate);
console.log(`До Нового года осталось:`);
console.log(`${remaining.days} дней`);
console.log(`${remaining.hours} часов`);
console.log(`${remaining.minutes} минут`);
console.log(`${remaining.seconds} секунд`);
Объяснение: Функция принимает конечную дату и вычисляет разницу с текущим моментом. Используем оператор % (остаток от деления) для получения часов, минут и секунд в пределах суток. Если время уже прошло, возвращаем нули.
Сравнение с Python: чем похоже и чем отличается
Если ты уже знаком с Python, тебе будет проще понять TypeScript. Но есть важные различия:
| Характеристика | TypeScript (JavaScript) | Python |
|---|---|---|
| Основной объект | Date |
datetime.datetime |
| Месяцы | С 0 (январь = 0) | С 1 (январь = 1) |
| Дни недели | 0 = воскресенье | 0 = понедельник (в datetime) |
| Timestamp | Миллисекунды | Секунды |
| Создание даты | new Date(2024, 2, 15) |
datetime(2024, 3, 15) |
| Форматирование | toLocaleDateString() |
strftime("%d.%m.%Y") |
| Работа с часовыми поясами | Сложно (нужна библиотека) | Есть pytz или zoneinfo |
| Типизация | Строгая (в TypeScript) | Динамическая |
Главное отличие: В Python работа с датами более интуитивна (месяцы с 1, дни недели с понедельника). В TypeScript нужно привыкнуть к «странностям» JavaScript, особенно к нумерации месяцев с 0.
Типичные ошибки и подводные камни
Ошибка 1: Месяцы с 0
// Неправильно:
const wrongDate: Date = new Date(2024, 1, 15); // Думаем, что январь
console.log(wrongDate.getMonth()); // 1 — это февраль!
// Правильно:
const correctDate: Date = new Date(2024, 0, 15); // Январь
console.log(correctDate.getMonth()); // 0
Ошибка 2: Сравнение объектов через ===
const a: Date = new Date(2024, 0, 1);
const b: Date = new Date(2024, 0, 1);
// Неправильно:
console.log(a === b); // false — разные объекты
// Правильно:
console.log(a.getTime() === b.getTime()); // true
Ошибка 3: Мутация исходной даты
const original: Date = new Date();
const modified: Date = original; // Это не копия, а ссылка!
modified.setDate(original.getDate() + 7);
console.log(original); // Изменился и original!
// Правильно — создаём копию:
const copy: Date = new Date(original);
copy.setDate(original.getDate() + 7);
Ошибка 4: Игнорирование часовых поясов
// Если пользователь в Нью-Йорке, а сервер в Москве
const userDate: Date = new Date("2024-03-15T14:00:00"); // Без часового пояса
console.log(userDate.toLocaleString("ru-RU")); // Может показать не то время
// Правильно — указывать часовой пояс:
const correctUserDate: Date = new Date("2024-03-15T14:00:00-04:00"); // Нью-Йорк
Ошибка 5: Високосные годы
// JavaScript корректно обрабатывает високосные годы
const leapYear: Date = new Date(2024, 1, 29); // 29 февраля 2024 — существует
console.log(leapYear.toDateString()); // "Thu Feb 29 2024"
const nonLeapYear: Date = new Date(2023, 1, 29); // 29 февраля 2023 — нет!
console.log(nonLeapYear.toDateString()); // "Thu Mar 01 2023" — автоматически перенеслось!
Важно: JavaScript автоматически корректирует даты. Если ты задашь 32 января, он покажет 1 февраля. Это удобно, но может привести к неожиданным результатам, если не знать об этом.
Лучшие практики и советы профессионалов
1. Всегда используй UTC для хранения
Храни даты в формате UTC (или timestamp) в базе данных. Преобразуй в локальное время только для отображения пользователю.
// Хранение
const eventDate: Date = new Date("2024-12-25T15:00:00Z"); // Z = UTC
// Отображение
console.log(eventDate.toLocaleString("ru-RU", { timeZone: "Europe/Moscow" }));
2. Используй библиотеки для сложных операций
Для серьёзных проектов используй date-fns или Luxon. Они решают проблемы с часовыми поясами и форматированием.
// Пример с date-fns (нужно установить)
// import { format, addDays, differenceInDays } from 'date-fns';
//
// const today = new Date();
// const formatted = format(today, 'dd.MM.yyyy');
// const tomorrow = addDays(today, 1);
// const diff = differenceInDays(tomorrow, today);
3. Создавай вспомогательные функции
// Функция для форматирования даты в российском формате
function formatRussianDate(date: Date): string {
return date.toLocaleDateString("ru-RU", {
year: "numeric",
month: "2-digit",
day: "2-digit"
});
}
// Функция для проверки, является ли дата сегодняшней
function isToday(date: Date): boolean {
const today: Date = new Date();
return date.toDateString() === today.toDateString();
}
// Использование
const someDate: Date = new Date(2024, 5, 15);
console.log(formatRussianDate(someDate)); // "15.06.2024"
console.log(isToday(someDate)); // false
4. Типизируй свои функции
// Всегда указывай типы для параметров и возвращаемых значений
function getAge(birthDate: Date): number {
const today: Date = new Date();
let age: number = today.getFullYear() - birthDate.getFullYear();
const monthDiff: number = today.getMonth() - birthDate.getMonth();
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) {
age--;
}
return age;
}
const myBirthday: Date = new Date(2000, 5, 15);
console.log(`Мой возраст: ${getAge(myBirthday)} лет`);
5. Избегай создания дат из строк без указания формата
// Плохо — может работать по-разному в разных браузерах
const badDate: Date = new Date("03/15/2024");
// Хорошо — ISO формат
const goodDate: Date = new Date("2024-03-15");
// Ещё лучше — явно указывать компоненты
const bestDate: Date = new Date(2024, 2, 15);
Вопросы для самопроверки
-
Вопрос: Почему месяцы в JavaScript начинаются с 0?
Ответ: Это историческое наследие языка C, где массивы индексируются с 0. Разработчики JavaScript решили, что месяцы — это как массив из 12 элементов, поэтому январь = 0, декабрь = 11.
-
Вопрос: Как получить текущий timestamp в миллисекундах?
Ответ:
Date.now()— самый быстрый способ. Илиnew Date().getTime(). -
Вопрос: Что произойдёт, если создать дату
new Date(2024, 1, 30)?Ответ: Получится 1 марта 2024 года, потому что в феврале 2024 года 29 дней (високосный год). JavaScript автоматически переносит дату на следующий месяц.
-
Вопрос: Как правильно сравнить две даты на равенство?
Ответ: Сравнивать нужно timestamp:
date1.getTime() === date2.getTime(). Оператор===сравнивает ссылки на объекты, а не значения. -
Вопрос: В чём разница между
toISOString()иtoLocaleDateString()?Ответ:
toISOString()возвращает дату в формате UTC (например, "2024-03-15T11:30:00.000Z"), аtoLocaleDateString()— в формате, принятом в регионе пользователя (например, "15.03.2024" для России). -
Вопрос: Как добавить 10 дней к текущей дате?
Ответ: Создать копию даты и использовать
setDate(currentDate.getDate() + 10). -
Вопрос: Почему не стоит хранить даты в виде строк?
Ответ: Строки занимают больше места, их сложнее сравнивать, они зависят от формата и часового пояса. Лучше хранить timestamp (число) или объект Date.
-
Вопрос: Как определить, является ли год високосным?
Ответ: Можно создать дату 29 февраля этого года и проверить, получился ли февраль:
new Date(year, 1, 29).getMonth() === 1. Если месяц остался февралём (1), то год високосный. -
Вопрос: Что такое UTC и зачем он нужен?
Ответ: UTC — всемирное координированное время, «нулевой» часовой пояс. Он нужен, чтобы у программистов была единая точка отсчёта, независимая от часовых поясов пользователей.
-
Вопрос: Как получить день недели в виде текста (понедельник, вторник)?
Ответ: Использовать
Intl.DateTimeFormatс опциейweekday: "long"или создать массив с названиями дней и обращаться по индексуgetDay().
Итог и что изучить дальше
Мы прошли большой путь: от понимания, что такое Unix-время и UTC, до создания полноценного таймера обратного отсчёта. Ты теперь знаешь:
- Как создавать даты разными способами
- Как получать и устанавливать компоненты даты
- Как форматировать даты для пользователя