📘 TypeScript

Работа с датой и временем в 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);

Вопросы для самопроверки

  1. Вопрос: Почему месяцы в JavaScript начинаются с 0?

    Ответ: Это историческое наследие языка C, где массивы индексируются с 0. Разработчики JavaScript решили, что месяцы — это как массив из 12 элементов, поэтому январь = 0, декабрь = 11.

  2. Вопрос: Как получить текущий timestamp в миллисекундах?

    Ответ: Date.now() — самый быстрый способ. Или new Date().getTime().

  3. Вопрос: Что произойдёт, если создать дату new Date(2024, 1, 30)?

    Ответ: Получится 1 марта 2024 года, потому что в феврале 2024 года 29 дней (високосный год). JavaScript автоматически переносит дату на следующий месяц.

  4. Вопрос: Как правильно сравнить две даты на равенство?

    Ответ: Сравнивать нужно timestamp: date1.getTime() === date2.getTime(). Оператор === сравнивает ссылки на объекты, а не значения.

  5. Вопрос: В чём разница между toISOString() и toLocaleDateString()?

    Ответ: toISOString() возвращает дату в формате UTC (например, "2024-03-15T11:30:00.000Z"), а toLocaleDateString() — в формате, принятом в регионе пользователя (например, "15.03.2024" для России).

  6. Вопрос: Как добавить 10 дней к текущей дате?

    Ответ: Создать копию даты и использовать setDate(currentDate.getDate() + 10).

  7. Вопрос: Почему не стоит хранить даты в виде строк?

    Ответ: Строки занимают больше места, их сложнее сравнивать, они зависят от формата и часового пояса. Лучше хранить timestamp (число) или объект Date.

  8. Вопрос: Как определить, является ли год високосным?

    Ответ: Можно создать дату 29 февраля этого года и проверить, получился ли февраль: new Date(year, 1, 29).getMonth() === 1. Если месяц остался февралём (1), то год високосный.

  9. Вопрос: Что такое UTC и зачем он нужен?

    Ответ: UTC — всемирное координированное время, «нулевой» часовой пояс. Он нужен, чтобы у программистов была единая точка отсчёта, независимая от часовых поясов пользователей.

  10. Вопрос: Как получить день недели в виде текста (понедельник, вторник)?

    Ответ: Использовать Intl.DateTimeFormat с опцией weekday: "long" или создать массив с названиями дней и обращаться по индексу getDay().

Итог и что изучить дальше

Мы прошли большой путь: от понимания, что такое Unix-время и UTC, до создания полноценного таймера обратного отсчёта. Ты теперь знаешь:

  • Как создавать даты разными способами
  • Как получать и устанавливать компоненты даты
  • Как форматировать даты для пользователя