⚡ JavaScript

Работа с датой и временем в JavaScript (Node.js): подробное руководство

Что это такое и зачем? (аналогия из реальной жизни)

Представьте, что вы — организатор школьного лагеря. У вас есть расписание: завтрак в 8:30, обед в 13:00, отбой в 21:00. Вам нужно знать, сколько времени осталось до обеда, через сколько дней закончится смена, или в какой день недели будет родительский день. Всё это — работа с датой и временем.

В программировании дата и время — это не просто цифры на календаре. Это инструмент для:

  • Логирования — записи, когда произошло событие (например, пользователь зарегистрировался 15 марта 2025 года в 14:32)
  • Планирования — запуска задач по расписанию (отправить письмо через 2 дня)
  • Расчётов — сколько дней прошло между двумя событиями
  • Форматирования — показать дату в понятном виде: «Сегодня, 10 апреля» или «10.04.2025»
Важно: В JavaScript (и Node.js) работа с датой и временем — одна из самых частых задач. Без неё не обходится ни одно серьёзное приложение: от простого будильника до сложной системы бронирования билетов.

Теория: как это работает изнутри

В основе всего лежит временная метка (timestamp) — это количество миллисекунд, прошедших с 1 января 1970 года 00:00:00 по UTC. Эта дата называется «эпоха Unix» (Unix Epoch).

Почему именно 1970 год? Потому что операционная система Unix была создана в конце 1960-х, и разработчики выбрали эту дату как точку отсчёта. С тех пор все компьютеры в мире «договорились» считать время именно так.

В JavaScript дата и время представлены объектом Date. Внутри него хранится одно число — количество миллисекунд от эпохи Unix. Всё остальное (год, месяц, день, час, минута, секунда) вычисляется из этого числа.

Важно понимать разницу между:

  • UTC (Coordinated Universal Time) — всемирное координированное время. Это «базовое» время, от которого считаются все часовые пояса.
  • Локальное время — время в вашем часовом поясе. Например, Москва — UTC+3.
Запомните: Внутри компьютера все даты хранятся в UTC. Когда вы видите дату на экране — это уже преобразованное в ваш часовой пояс значение.

Синтаксис в JavaScript (Node.js): все формы и варианты

Создание объекта Date

В JavaScript есть 4 основных способа создать объект даты:

Способ Пример Результат
Текущая дата и время new Date() Текущий момент времени
Из миллисекунд new Date(1700000000000) Дата, соответствующая этим миллисекундам
Из строки new Date("2025-03-15T14:30:00") Дата, разобранная из строки
Из компонентов new Date(2025, 2, 15, 14, 30, 0) Дата из года, месяца, дня и т.д.
Внимание: Месяцы в JavaScript нумеруются с 0! Январь — 0, Февраль — 1, Декабрь — 11. Это одна из самых частых ошибок новичков.

Основные методы объекта Date

Все методы делятся на две группы: «получить» (get) и «установить» (set).

Метод Что возвращает Диапазон значений
getFullYear() Год (4 цифры) 1970 — 9999
getMonth() Месяц (0-11) 0 (январь) — 11 (декабрь)
getDate() День месяца (1-31) 1 — 31
getDay() День недели (0-6) 0 (воскресенье) — 6 (суббота)
getHours() Часы (0-23) 0 — 23
getMinutes() Минуты (0-59) 0 — 59
getSeconds() Секунды (0-59) 0 — 59
getMilliseconds() Миллисекунды (0-999) 0 — 999
getTime() Миллисекунды с эпохи Unix Очень большое число

Для UTC-версий методов добавьте «UTC» в название: getUTCFullYear(), getUTCMonth() и т.д.

Методы для установки работают аналогично: setFullYear(), setMonth(), setDate() и т.д.

Форматирование дат

Встроенные методы для преобразования даты в строку:

Метод Пример вывода
toString() "Sat Mar 15 2025 14:30:00 GMT+0300"
toDateString() "Sat Mar 15 2025"
toTimeString() "14:30:00 GMT+0300"
toISOString() "2025-03-15T11:30:00.000Z"
toLocaleDateString() "15.03.2025" (зависит от локали)
toLocaleTimeString() "14:30:00" (зависит от локали)

Примеры кода — от простого к сложному

Пример 1: Получаем текущую дату и время

// Создаём объект с текущей датой и временем
const now = new Date();

// Выводим в консоль
console.log("Текущая дата и время:", now);

// Получаем отдельные компоненты
const year = now.getFullYear();      // 2025
const month = now.getMonth() + 1;    // 3 (прибавляем 1, т.к. месяцы с 0)
const day = now.getDate();           // 15
const hours = now.getHours();        // 14
const minutes = now.getMinutes();    // 30

console.log(`Сегодня: ${day}.${month}.${year}`);
console.log(`Сейчас: ${hours}:${minutes}`);

Объяснение: Мы создаём объект Date без аргументов — он автоматически фиксирует текущий момент времени. Затем получаем каждый компонент отдельно. Обратите внимание: к месяцу мы прибавляем 1, потому что JavaScript считает январь нулевым месяцем.

Пример 2: Создаём конкретную дату

// Создаём дату 1 января 2025 года, 12:00
const newYear = new Date(2025, 0, 1, 12, 0, 0);

console.log("Новый год:", newYear);

// Проверяем день недели
const dayOfWeek = newYear.getDay(); // 3 (среда, т.к. 0 - воскресенье)

// Массив с названиями дней недели
const weekDays = ['Воскресенье', 'Понедельник', 'Вторник', 'Среда', 'Четверг', 'Пятница', 'Суббота'];

console.log(`1 января 2025 года — это ${weekDays[dayOfWeek]}`);

Объяснение: Мы передаём в конструктор Date 6 чисел: год, месяц (0 — январь), день, часы, минуты, секунды. Метод getDay() возвращает номер дня недели, где 0 — воскресенье. Используем массив для преобразования числа в название.

Пример 3: Вычисляем разницу между датами

// Дата начала и конца события
const start = new Date(2025, 2, 10); // 10 марта 2025
const end = new Date(2025, 2, 20);   // 20 марта 2025

// Разница в миллисекундах
const diffMs = end.getTime() - start.getTime();

// Переводим в дни (1 день = 24 часа * 60 минут * 60 секунд * 1000 мс)
const diffDays = diffMs / (1000 * 60 * 60 * 24);

console.log(`Разница между датами: ${diffDays} дней`); // 10 дней

// Более точный способ с округлением
const diffDaysRounded = Math.round(diffMs / (1000 * 60 * 60 * 24));
console.log(`Округлённая разница: ${diffDaysRounded} дней`);

Объяснение: Мы получаем timestamp каждой даты через getTime(), вычитаем один из другого и делим на количество миллисекунд в одном дне. Math.round() округляет результат до целого числа.

Пример 4: Форматирование даты для пользователя

const date = new Date(2025, 2, 15, 14, 30, 0);

// Стандартное форматирование
console.log("ISO формат:", date.toISOString());
// "2025-03-15T11:30:00.000Z" (в UTC!)

// Локальное форматирование (зависит от системы)
console.log("Локальная дата:", date.toLocaleDateString('ru-RU'));
// "15.03.2025"

console.log("Локальное время:", date.toLocaleTimeString('ru-RU'));
// "14:30:00"

// Полный формат с опциями
const options = {
  year: 'numeric',
  month: 'long',
  day: 'numeric',
  weekday: 'long',
  hour: '2-digit',
  minute: '2-digit'
};

console.log("Красивый формат:", date.toLocaleDateString('ru-RU', options));
// "суббота, 15 марта 2025 г., 14:30"

Объяснение: toLocaleDateString() и toLocaleTimeString() позволяют форматировать дату под конкретную страну. Вторым аргументом можно передать объект с опциями для точной настройки формата.

Пример 5: Работа с часовыми поясами

// Создаём дату в UTC
const utcDate = new Date(Date.UTC(2025, 2, 15, 12, 0, 0));
console.log("UTC время:", utcDate.toISOString());
// "2025-03-15T12:00:00.000Z"

// Получаем компоненты в UTC
console.log("UTC часы:", utcDate.getUTCHours()); // 12

// Получаем компоненты в локальном времени (Москва UTC+3)
console.log("Локальные часы:", utcDate.getHours()); // 15

// Смещение часового пояса в минутах
const timezoneOffset = utcDate.getTimezoneOffset();
console.log(`Смещение от UTC: ${timezoneOffset} минут`);
// Для Москвы: -180 (UTC+3 = -180 минут)

Объяснение: Date.UTC() создаёт дату в UTC, игнорируя локальный часовой пояс. Методы с префиксом getUTC возвращают значения в UTC, а обычные — в локальном времени. getTimezoneOffset() показывает разницу в минутах между локальным временем и UTC.

Пример 6: Таймер обратного отсчёта

// Функция для расчёта времени до указанной даты
function getTimeRemaining(targetDate) {
  const now = new Date();
  const total = targetDate.getTime() - now.getTime();
  
  // Если дата уже прошла
  if (total <= 0) {
    return { days: 0, hours: 0, minutes: 0, seconds: 0, total: 0 };
  }
  
  // Расчёт компонентов
  const seconds = Math.floor((total / 1000) % 60);
  const minutes = Math.floor((total / 1000 / 60) % 60);
  const hours = Math.floor((total / (1000 * 60 * 60)) % 24);
  const days = Math.floor(total / (1000 * 60 * 60 * 24));
  
  return { total, days, hours, minutes, seconds };
}

// Целевая дата: 1 июня 2025 года
const target = new Date(2025, 5, 1);
const remaining = getTimeRemaining(target);

console.log(`До 1 июня 2025 осталось:`);
console.log(`${remaining.days} дней`);
console.log(`${remaining.hours} часов`);
console.log(`${remaining.minutes} минут`);
console.log(`${remaining.seconds} секунд`);

Объяснение: Функция getTimeRemaining() вычисляет разницу между текущим моментом и целевой датой. Используем Math.floor() для округления вниз и оператор % (остаток от деления) для получения остатка после выделения дней, часов и минут.

Пример 7: Парсинг даты из строки

// Разные форматы строк с датами
const date1 = new Date("2025-03-15"); // ISO формат
const date2 = new Date("March 15, 2025"); // Американский формат
const date3 = new Date("15/03/2025"); // Европейский формат (может не сработать!)

console.log("ISO:", date1);
console.log("Американский:", date2);
console.log("Европейский:", date3); // Может вернуть Invalid Date!

// Надёжный способ: разобрать строку вручную
function parseDate(dateString) {
  // Ожидаем формат "DD.MM.YYYY"
  const parts = dateString.split('.');
  if (parts.length !== 3) {
    return new Date(NaN); // Некорректная дата
  }
  
  const day = parseInt(parts[0], 10);
  const month = parseInt(parts[1], 10) - 1; // Месяцы с 0
  const year = parseInt(parts[2], 10);
  
  return new Date(year, month, day);
}

const myDate = parseDate("15.03.2025");
console.log("Разобранная дата:", myDate);
// "Sat Mar 15 2025 00:00:00 GMT+0300"

Объяснение: Встроенный парсинг дат из строк может работать непредсказуемо в разных браузерах и средах. Лучше написать свою функцию, которая разбирает строку по частям и создаёт дату через конструктор с компонентами.

Сравнение с Python: чем похоже и чем отличается

Характеристика JavaScript (Node.js) Python
Основной модуль/объект Date (встроенный объект) datetime (модуль)
Создание текущей даты new Date() datetime.now()
Создание конкретной даты new Date(2025, 2, 15) datetime(2025, 3, 15)
Нумерация месяцев С 0 (январь = 0) С 1 (январь = 1)
Форматирование toLocaleDateString() strftime()
Работа с часовыми поясами Сложная (нужны библиотеки) Встроенная (pytz)
Арифметика дат Ручные расчёты через timestamp timedelta

Ключевые отличия:

  • В Python месяцы нумеруются с 1, что более логично для человека
  • Python имеет встроенный модуль timedelta для удобной работы с интервалами времени
  • JavaScript требует сторонних библиотек (например, moment.js или date-fns) для сложных операций с часовыми поясами
  • В Python форматирование через strftime() более гибкое и стандартизированное
Совет: Если вы переходите с Python на JavaScript, запомните главное правило: в JS месяцы с 0, а все операции с датами требуют больше ручного кода.

Типичные ошибки и подводные камни

Ошибка 1: Забываем про нумерацию месяцев с 0

// ❌ Неправильно
const wrongDate = new Date(2025, 3, 15); // Это апрель, а не март!
console.log(wrongDate.getMonth()); // 3 (апрель)

// ✅ Правильно
const correctDate = new Date(2025, 2, 15); // Март (месяц 2)
console.log(correctDate.getMonth()); // 2 (март)

Ошибка 2: Сравнение дат через ==

const date1 = new Date(2025, 2, 15);
const date2 = new Date(2025, 2, 15);

// ❌ Неправильно
console.log(date1 == date2); // false! Сравниваются ссылки на объекты

// ✅ Правильно
console.log(date1.getTime() == date2.getTime()); // true
console.log(date1 - date2 === 0); // true (оператор - преобразует в числа)

Ошибка 3: Мутабельность объектов Date

const original = new Date(2025, 2, 15);
const copy = original; // ❌ Это не копия, а ссылка!

copy.setDate(20);
console.log(original.getDate()); // 20 — исходный объект тоже изменился!

// ✅ Правильное копирование
const realCopy = new Date(original.getTime());
realCopy.setDate(25);
console.log(original.getDate()); // 20 — исходный не изменился

Ошибка 4: Игнорирование часовых поясов

// Создаём дату из строки
const date = new Date("2025-03-15"); // Парсится как UTC!
console.log(date.getHours()); // 3 (если вы в Москве, UTC+3)

// ✅ Чтобы получить полночь по локальному времени:
const localDate = new Date(2025, 2, 15);
console.log(localDate.getHours()); // 0

Ошибка 5: Неправильный расчёт разницы в днях

const start = new Date(2025, 2, 10);
const end = new Date(2025, 2, 20);

// ❌ Неправильно: не учитываем переход на летнее время
const diffDays = (end - start) / (1000 * 60 * 60 * 24);
// Может быть 9.95833... из-за перевода часов

// ✅ Правильно: используем UTC для расчётов
const utcStart = Date.UTC(2025, 2, 10);
const utcEnd = Date.UTC(2025, 2, 20);
const safeDiff = (utcEnd - utcStart) / (1000 * 60 * 60 * 24);
console.log(safeDiff); // 10 — всегда точно

Лучшие практики и советы профессионалов

1. Используйте библиотеки для сложных задач

Для серьёзных проектов используйте date-fns или luxon. Они решают проблемы с часовыми поясами, форматированием и локализацией.

2. Храните даты в UTC

В базах данных и API всегда передавайте даты в UTC. Преобразуйте в локальное время только при отображении пользователю.

3. Используйте ISO 8601 для обмена данными

Формат 2025-03-15T14:30:00.000Z — международный стандарт. Он однозначен и легко парсится.

4. Избегайте встроенного парсинга строк

Конструктор new Date(string) работает по-разному в разных браузерах. Всегда разбирайте строки вручную или используйте библиотеки.

5. Округляйте результаты вычислений

При расчёте разницы в днях используйте Math.round() или Math.floor(), чтобы избежать проблем с плавающей точкой.

6. Проверяйте валидность дат

function isValidDate(date) {
  return date instanceof Date && !isNaN(date.getTime());
}

const invalid = new Date("не дата");
console.log(isValidDate(invalid)); // false
console.log(isValidDate(new Date())); // true

7. Используйте Intl.DateTimeFormat для форматирования

const formatter = new Intl.DateTimeFormat('ru-RU', {
  year: 'numeric',
  month: 'long',
  day: 'numeric',
  weekday: 'long'
});

console.log(formatter.format(new Date(2025, 2, 15)));
// "суббота, 15 марта 2025 г."

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

  1. Вопрос: Что такое «эпоха Unix» и почему она важна?

    Ответ: Это 1 января 1970 года 00:00:00 UTC. Все даты в JavaScript хранятся как количество миллисекунд, прошедших с этого момента.

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

    Ответ: Это историческое решение, связанное с языком Java, от которого JavaScript унаследовал много синтаксиса. В массивах индексация тоже начинается с 0.

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

    Ответ: Нужно сравнить их timestamp: date1.getTime() === date2.getTime() или date1 - date2 === 0.

  4. Вопрос: В чём разница между getDate() и getDay()?

    Ответ: getDate() возвращает день месяца (1-31), а getDay() — день недели (0-6, где 0 — воскресенье).

  5. Вопрос: Как создать дату, которая представляет полночь 1 января 2025 года по московскому времени?

    Ответ: new Date(2025, 0, 1) — это создаст дату с учётом локального часового пояса.

  6. Вопрос: Почему new Date("2025-03-15") может дать неожиданный результат?

    Ответ: Строка в формате ISO парсится как UTC, поэтому время будет 00:00 UTC, что в Москве соответствует 03:00 ночи.

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

    Ответ: const future = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000); или использовать setDate(now.getDate() + 7).

  8. Вопрос: Что такое Date.UTC() и чем он отличается от new Date()?

    Ответ: Date.UTC() возвращает timestamp (число) для указанной даты в UTC, не создавая объект Date. Это полезно для расчётов без учёта часового пояса.

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

    Ответ: Использовать date instanceof Date && !isNaN(date.getTime()).

  10. Вопрос: В чём преимущество библиотеки date-fns перед встроенным Date?

    Ответ: date-fns предоставляет удобные функции для форматирования, манипуляций с датами, работы с часовыми поясами и локализации, избавляя от рутинного кода.

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

Мы разобрали основы работы с датой и временем в JavaScript (Node.js). Теперь вы знаете:

  • Как создавать объекты Date разными способами
  • Как получать и устанавливать компоненты даты
  • Как форматировать даты для пользователя
  • Как выполнять расчёты с датами
  • Какие ошибки чаще всего допускают новички
  • Как правильно работать с часовыми поясами

Что изучить дальше:

  1. Библиотеки для работы с датами:
    • date-fns — лёгкая библиотека с множеством функций
    • luxon — современная библиотека от создателей moment.js
    • dayjs — минималистичная альтернатива
  2. Работа с календарями: создание календарных сеток, планировщиков задач
  3. Таймеры и интервалы: setTimeout(), setInterval() для выполнения кода через определённое время
  4. Сериализация дат: как правильно сохранять даты в JSON, базы данных
  5. Международная локализация: адаптация форматов дат под разные страны
Главный совет: Не пытайтесь запомнить все методы Date. Запомните основные принципы (timestamp, UTC, нумерация месяцев с 0) и всегда держите под рукой документацию. Практика — лучший способ освоить работу с датами.

Помните: дата и время — это не просто числа. Это инструмент, который делает ваши программы «живыми» и привязанными к реальному миру. Удачи в