Объект Date — это момент во времени
Объект Date в JavaScript — это конкретный момент во времени. Внутри он хранится как обычное число: количество миллисекунд, прошедших с 1 января 1970 года по UTC (так называемая «эпоха Unix»). Всё остальное — годы, месяцы, дни, часовые пояса, форматирование — это лишь надстройка над этим числом.
now.getTime() — это «сырое» число миллисекунд. Всё, что умеет Date — сравнивать, прибавлять дни, форматировать — сводится к манипуляциям над этим числом и последующей его интерпретации.
Держите эту модель в голове. Date — это не «14 марта в Париже». Это универсальный момент времени, который можно отобразить как 14 марта в Париже или как 13 марта в Лос-Анджелесе — всё зависит от часового пояса, через который вы на него смотрите.
Создание объекта Date в JavaScript
Есть четыре основных способа создать Date:
Два важных момента:
- Конструктор с отдельными аргументами использует месяцы с нуля.
2— это март, а январь —0. Это вечный источник ошибок «на единицу»: впрочем, во всём остальном API месяцы тоже нумеруются с нуля, так что хотя бы внутренне это согласовано. new Date("2026-03-14")(без времени) парсится как полночь по UTC. Аnew Date("2026-03-14T09:30")(безZв конце) — уже как локальное время. Эта асимметрия — классическая ловушка.
Если нужно просто «сейчас» в виде числа, лучше использовать Date.now() — так вы избежите создания лишнего объекта:
Date.now() — то, что нужно, когда надо замерить прошедшее время, поставить таймаут и вообще в любых ситуациях, где календарная арифметика не требуется.
Как получить части даты в JavaScript
Получив объект Date, компоненты из него достают через геттеры. У каждого геттера есть два варианта: по локальному времени и по UTC.
Локальные геттеры зависят от машины, на которой выполняется код. Если вы храните или сравниваете даты между пользователями и серверами, явно берите UTC — иначе будете ловить фантомные баги. Правило простое: для всего, что уходит в базу или в логи, используйте UTC-геттеры; для всего, что показываете человеку, — локальные.
Не используйте getYear(). Это легаси-метод, который возвращает year - 1900 и существует только ради обратной совместимости. Всегда берите getFullYear().
Форматирование даты для человека
Не полагайтесь на date.toString() там, где результат важен — вывод зависит от локали и движка. Есть два форматтера, которые стоит знать.
Для стандартной машиночитаемой строки подойдёт toISOString():
Именно такой формат стоит использовать для логов, хранения в JSON и передачи по сети. Он всегда в UTC и не допускает разночтений.
А чтобы показать дату пользователю, возьмите Intl.DateTimeFormat или методы toLocale*, которые построены поверх него:
Intl.DateTimeFormat умеет работать с локалями, часовыми поясами и любыми комбинациями полей, которые вам только понадобятся. Используйте его вместо ручной сборки строк вида ${year}-${month}-${day} — именно в такой склейке вручную и плодятся баги со сдвигом месяца на единицу.
Сравнение дат в JavaScript
Два объекта Date, указывающие на один и тот же момент времени, не будут равны по === — этот оператор проверяет идентичность объектов, а не их значение. Поэтому сравнивать нужно таймстемпы:
Для сортировки операторы сравнения работают напрямую — они приводят даты к числам:
Вычитание дат даёт разницу в миллисекундах. Чтобы получить дни, делим на 1000 * 60 * 60 * 24. В первый раз лучше выписать эту константу полностью — со временем число 86_400_000 начнёшь узнавать с первого взгляда.
Арифметика дат в JavaScript
Метода addDays в JavaScript нет. Идиоматичный способ — использовать setDate, setMonth и им подобные: они спокойно принимают значения за пределами обычного диапазона и сами корректно «переносят» их:
Пара моментов, на которые стоит обратить внимание:
new Date(date)создаёт копию даты.setDateменяет объект на месте, поэтому всегда сначала копируйте — иначе вы измените значение, переданное снаружи.setDate(35)в месяце с 31 днём автоматически перекинет дату на следующий месяц. То же самое сsetMonth(14)— год сдвинется вперёд. Благодаря этому арифметика дат на практике оказывается проще, чем кажется.
Если задача посложнее — рабочие дни, повторяющиеся события, длительности с учётом разной длины месяцев — берите библиотеку (date-fns, Luxon или будущий API Temporal). Писать собственную календарную логику дальше «прибавить пару дней» — это болото, из которого потом не выбраться.
Часовые пояса: суровая правда
Часовые пояса — главный источник багов при работе с датой и временем в javascript. Правила, которые стоит вбить себе в голову:
Dateхранит момент времени в UTC. Часовой пояс применяется только тогда, когда вы достаёте из объекта отдельные части или форматируете его.getHours(),getDate()и им подобные методы используют локальный часовой пояс машины, на которой выполняется код. У сервера и браузера он часто отличается.new Date("2026-03-14")(только дата) парсится как UTC.new Date("2026-03-14T00:00")(время без указания зоны) — как локальное время.new Date(2026, 2, 14)(по частям) — тоже локальное.
Если нужно вывести дату в конкретном часовом поясе, передайте timeZone в Intl.DateTimeFormat:
Один и тот же момент времени, но два разных представления. Сам объект Date при этом никак не меняется.
Небольшой рабочий пример
Соберём всё вместе — напишем функцию, которая показывает, сколько времени прошло с какого-то события:
Таймштамп на входе — человекочитаемая строка на выходе. Так устроено 90% кода, работающего с датами в реальных проектах: вычли два момента времени, разделили на нужную единицу, округлили, отформатировали.
Что стоит запомнить
Date— это момент времени в UTC. Часовые пояса появляются только при чтении и форматировании.Date.now()— для таймстампов,new Date()— для работы с календарём.toISOString()— для хранения и логов,Intl.DateTimeFormat— для показа пользователю.- Сравнивайте через
getTime()либо операторами</>. Никакого===. - Месяцы нумеруются с нуля. И помните про ловушку с парсингом строк без времени.
- Для серьёзной арифметики дат берите библиотеку.
Дальше: URL и строки запроса
Даты часто живут в URL — фильтр по диапазону, таймстамп в query-параметре. Разбирать и собирать URL вручную так же рискованно, как форматировать даты руками, и в стандартной библиотеке для этого есть объект URL, который делает всё аккуратно. Об этом — в следующей главе.
Часто задаваемые вопросы
Как получить текущую дату в JavaScript?
Вызовите new Date() без аргументов — получите объект Date с моментом, когда сработал конструктор. Если нужен просто числовой timestamp (миллисекунды с 1970 года), берите Date.now() — это быстрее и не создаёт лишний объект.
Как сравнить две даты в JavaScript?
Сравнивайте timestamp'ы, а не сами объекты Date. Работает a.getTime() < b.getTime(), а также просто a < b — оператор < приведёт даты к числу. А вот a === b не сработает: === сравнивает ссылки на объекты, поэтому две разные переменные Date с одним и тем же моментом никогда не будут строго равны.
Как отформатировать дату в JavaScript?
Для вывода пользователю используйте Intl.DateTimeFormat или date.toLocaleDateString() — они нормально работают с локалями и таймзонами. Для машинного формата подойдёт date.toISOString(), он возвращает стандартную строку вида 2026-03-14T09:30:00.000Z. А вот date.toString() для хранения лучше не использовать — формат зависит от локали.
Почему дата в JavaScript сдвинулась на один день?
Почти всегда это проблема с таймзоной. new Date('2026-03-14') парсится как полночь по UTC, а date.getDate() возвращает день уже в локальной таймзоне — и это может оказаться вчерашний день. Используйте getUTCDate() для UTC-дня или создавайте дату через new Date(year, month, day) — такой конструктор сразу работает в локальном времени.