Работа с датами в JavaScript: типичные ошибки и правильные решения (JavaScript)

Объект Date в JavaScript — базовый инструмент для работы с временем, но в нём скрыто несколько неочевидных особенностей. Ниже — частые ошибки и способы их избежать, с конкретными примерами кода.


Ошибка 1: вызов Date() без new

В чём проблема. Если написать const d = Date(), получится не объект даты, а строка с текущим временем. Такой результат нельзя использовать для вычислений или извлечения компонентов.

js
const d1 = Date();
console.log(typeof d1);        // "string"
console.log(d1 instanceof Date); // false

Правильное решение. Всегда создавайте экземпляр через new Date():

js
const d2 = new Date();
console.log(typeof d2);        // "object"
console.log(d2 instanceof Date); // true

Если нужна только временная метка (timestamp в миллисекундах), используйте Date.now() — это быстрее и понятнее, чем new Date().getTime().

js
const ts = Date.now(); // число (ms с 1970-01-01 UTC)

Ошибка 2: месяцы в числовом конструкторе начинаются с 0

В чём проблема. При создании даты через числа new Date(year, month, day) месяц передаётся как индекс: 0 — январь, 11 — декабрь. Это частый источник смещения на один месяц.

js
// Ошибочно: хотели 15 февраля 2024, но получили март
const wrong = new Date(2024, 2, 15);
console.log(wrong.toLocaleDateString('ru-RU')); // 15.03.2024

Правильное решение. Учитывайте смещение: monthIndex = monthNumber - 1.

js
const monthNumber = 2; // февраль
const correct = new Date(2024, monthNumber - 1, 15);
console.log(correct.toLocaleDateString('ru-RU')); // 15.02.2024

Ошибка 3: парсинг произвольных строковых форматов

В чём проблема. Конструктор new Date(string) чувствителен к формату строки. Поведение различается между браузерами, а нестандартные форматы могут давать Invalid Date или неверные результаты.

js
// Нестабильно: может не работать или давать разные результаты
const d = new Date('15/02/2024');
console.log(d.toString()); // зависит от реализации

Правильное решение. Используйте ISO 8601: YYYY-MM-DD или YYYY-MM-DDTHH:mm:ssZ.

js
const iso = new Date('2024-02-15T10:30:00Z');
console.log(iso.toISOString()); // "2024-02-15T10:30:00.000Z"

Для произвольных форматов лучше явно разбирать строку вручную или использовать библиотеку (например, date-fnsLuxon).


Ошибка 4: ожидание исключения при невалидной дате

В чём проблема. new Date('не дата') не выбрасывает ошибку. Вместо этого создаётся объект со статусом Invalid Date. При этом typeof всё равно возвращает "object".

js
const bad = new Date('not-a-date');
console.log(bad.toString());           // "Invalid Date"
console.log(Number.isNaN(bad.getTime())); // true

Правильное решение. Проверяйте валидность через Number.isNaN(date.getTime()).

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

Ошибка 5: сравнение объектов Date через ===

В чём проблема. Объекты сравниваются по ссылке, поэтому две даты с одинаковым временем не равны.

js
const a = new Date(2024, 0, 1);
const b = new Date(2024, 0, 1);
console.log(a === b); // false

Правильное решение. Сравнивайте временные метки:

js
console.log(+a === +b);           // true
console.log(a.getTime() === b.getTime()); // true

Для проверки «одна дата раньше другой»:

js
const start = new Date(2024, 0, 1);
const end = new Date(2024, 0, 10);
console.log(start < end); // true (работает благодаря приведению к числу)

Ошибка 6: путаница между локальным временем и UTC

В чём проблема. Методы getMonth()getDate()getHours() возвращают значения по локальному времени пользователя. Методы getUTCMonth()getUTCHours() — по UTC. Смешение этих подходов приводит к ошибкам при работе с таймзонами.

js
const d = new Date('2024-01-15T12:00:00Z'); // UTC
console.log(d.getHours());      // зависит от часового пояса браузера
console.log(d.getUTCHours());   // 12 (всегда)

Правильное решение.

  • Для хранения и передачи данных используйте UTC/ISO.
  • Для отображения пользователю — toLocaleString() с явным указанием timeZone.
js
const date = new Date('2024-01-15T12:00:00Z');
 
// Отображение в Москве
console.log(date.toLocaleString('ru-RU', { timeZone: 'Europe/Moscow' }));
 
// Отображение в Нью‑Йорке
console.log(date.toLocaleString('en-US', { timeZone: 'America/New_York' }));

Ошибка 7: некорректное изменение компонентов даты

В чём проблема. Методы вида setMonth()setDate() изменяют дату «на месте» и автоматически корректируют соседние компоненты. Неожиданное поведение возникает, если не учитывать эту автокоррекцию.

js
const d = new Date(2024, 0, 31); // 31 января
d.setMonth(1); // февраль 2024 имеет 29 дней
console.log(d.toLocaleDateString('ru-RU')); // 29.02.2024 (не 31.02)

Правильное решение. Если нужна предсказуемая логика (например, «плюс N дней»), лучше прибавлять миллисекунды:

js
function addDays(date, days) {
  const result = new Date(+date); // копия
  result.setTime(result.getTime() + days * 24 * 60 * 60 * 1000);
  return result;
}
 
const base = new Date(2024, 0, 31);
const plus10 = addDays(base, 10);
console.log(plus10.toLocaleDateString('ru-RU'));

Ошибка 8: расчёт разницы между датами без учёта миллисекунд

В чём проблема. Разность двух объектов Date — это число миллисекунд. Если забыть деление на коэффициент, результат будет неверным.

js
const start = new Date(2024, 0, 1);
const end = new Date(2024, 0, 11);
const diffMs = end - start; // миллисекунды
const diffDaysWrong = diffMs / (60 * 60 * 24); // забыли умножить на 1000
const diffDaysCorrect = diffMs / (1000 * 60 * 60 * 24);

Правильное решение. Явно используйте множитель 1000 для перевода в секунды/дни.

js
const secondsPerDay = 1000 * 60 * 60 * 24;
const days = Math.floor((end - start) / secondsPerDay);

Практические рекомендации

  • Хранение и передача: используйте ISO‑строки (toISOString()) или timestamp (Date.now()).
  • Отображение: применяйте toLocaleString() с параметром timeZone, чтобы избежать зависимости от настроек браузера.
  • Сложные сценарии (парсинг разных форматов, арифметические операции с учётом календаря, таймзоны): используйте проверенные библиотеки (date-fnsdayjsLuxon) вместо ручной реализации.
  • Валидация: всегда проверяйте isValidDate(date) перед использованием даты из ненадёжного источника.

Мини‑пример: безопасный разбор строки и отображение

js + html
<!DOCTYPE html>
<html lang="ru">
<head>
  <meta charset="UTF-8">
  <title>Работа с датой</title>
</head>
<body>
 
  <h2>Дата и время</h2>
  <div id="output"></div>
 
  <script>
    function isValidDate(date) {
      return date instanceof Date && !Number.isNaN(date.getTime());
    }
 
    function parseSafe(isoString) {
      const date = new Date(isoString);
      if (!isValidDate(date)) {
        throw new Error('Некорректная дата');
      }
      return date;
    }
 
    function formatForUser(date, locale = 'ru-RU', timeZone = 'Europe/Moscow') {
      return date.toLocaleString(locale, {
        year: 'numeric',
        month: 'long',
        day: 'numeric',
        hour: '2-digit',
        minute: '2-digit',
        timeZone,
      });
    }
 
    const input = '2024-03-10T14:30:00Z';
    const d = parseSafe(input);
    const formatted = formatForUser(d);
 
    // Вывод на экран в элемент с id="output"
    document.getElementById('output').textContent = formatted;
 
    // (Опционально) дублируем в консоль для проверки
    console.log(formatted);
  </script>
</body>
</html>

Этот подход минимизирует ошибки, связанные с форматами, таймзонами и валидностью данных.

Добавить комментарий