#Что происходит и как распознать неисправность
Строка 2026-03-01 — календарная дата; в современных JavaScript new Date("2026-03-01") интерпретируется как полночь UTC, и при выводе в отрицательном часовом поясе день может стать предыдущим. Строка с Z — конкретный UTC-момент. Для дней рождения и сроков без времени лучше хранить саму дату, а не «момент».
#Как отличить причины
| Возможная причина | Как узнать | Что делать |
|---|---|---|
| Разные зоны чтения и отображенияДата создана как полночь UTC, а отображена по времени браузера. | На компьютерах в разных часовых поясах разные дни. | Храните календарную дату строкой YYYY-MM-DD. |
| toISOString() переводит время в UTCРазработчик получает ISO UTC из локальной полуночи. | toISOString().slice(0,10) даёт соседний день. | Для локальных полей берите календарные компоненты, а не UTC. |
API возвращает момент без offsetВ дате-времени отсутствуют Z или +hh:mm. | Клиенты по-разному трактуют исходное назначение. | Определите договорённость о временной зоне API. |
#Пошаговая диагностика и исправление
- Сохраните проблемное значение и ожидаемый деньОткройте страницу, нажмите F12 → «Консоль», выведите исходную ISO-строку до создания Date и зафиксируйте, что она означает: день в календаре или точный момент события. Не исправляйте код, пока не ясно значение; для срока платежа и дня рождения это разные задачи.
- Сравните UTC и местную датуВ консоли браузера выполните
new Date("2026-03-01").toISOString()иnew Date("2026-03-01").toString(). Первая строка содержитZи UTC, вторая — местную зону. Если календарный день отличается, причина найдена; на компьютере с UTC+3 пример может не проявиться, проверьте тестом с нужным часовым поясом. - Для календарной даты не создавайте объект Date без нуждыЕсли поле означает только дату без времени, храните
YYYY-MM-DD, валидируйте формат и показывайте части напрямую либо средствами локализации без перехода между часовыми поясами. Не применяйте к нейtoISOString(); UTC там не имеет предметного смысла. Для date-input можно использовать его строковое.value. - Если нужен локальный Date, создавайте его из компонентовИз строки YYYY-MM-DD выделите год, месяц, день и используйте
new Date(year, month - 1, day)только тогда, когда требуется локальная дата. Месяц в конструкторе начинается с нуля. Сравните компоненты getFullYear/getMonth/getDate с исходными значениями; если важен только день, строка всё равно надёжнее. - Для события с временем храните реальный моментЕсли API передаёт событие во времени, потребуйте ISO-строку с
Zили явным смещением, например2026-11-02T07:00:00+02:00. Разбирайте через new Date и отображайте локально, указав пользователю часовую зону. Для преобразования в UTC используйте toISOString сознательно, а не как формат «даты без времени». - Проверьте сервер и базу данныхОткройте обработчик API и проверьте, не удаляет ли он смещение и не срезает ли
T...Zпри сериализации. В тестовом ответе сравните сырой JSON и то, что получает браузер. Если значение календарное, не переводите его на сервере через UTC; если момент — передавайте зону явно. - Проверьте граничные сутки в нескольких поясахВ тестах используйте UTC−8, UTC и UTC+3 (через средства эмуляции часового пояса, если доступны), а также дату рядом с переводом часов. День рождения должен оставаться тем же календарным днём; запись события должна соответствовать одному моменту. Если после изменений нарушились сохранённые даты, откатите преобразование и уточните семантику старых данных.
#Команды и примеры для проверки
const calendarDate = "2026-03-01";
const [year, month, day] = calendarDate.split("-").map(Number);
const localDate = new Date(year, month - 1, day);
console.log(calendarDate); // день как значение
console.log(localDate.getDate()); // 1 в локальной зоне
console.log(new Date(calendarDate).toISOString()); // полночь UTC#Как проверить результат и что делать дальше
Календарная дата больше не меняется от часового пояса браузера; точные события отображаются в выбранной зоне с правильным днём. Смешивание двух видов значения неизбежно даёт ошибки на границах суток.