Node.js LinuxDocker Desktop на WindowsDocker Desktop на macOS

Как исправить неверный часовой пояс Node.js внутри контейнера Docker

  • Обновлено
  • Чтение ≈ 4 минуты

Краткий ответ

Приложение Node.js в контейнере может отображать локальное время не так, как ожидают пользователи: по умолчанию многие контейнеры используют UTC. При этом Date хранит момент времени независимо от зоны, toISOString() всегда показывает UTC, а Intl.DateTimeFormat может форматировать его в нужной зоне. Инструкция для расхождения отображаемого времени, а не для «исправления» абсолютных меток UTC.

В этой статье
  1. Что означает ошибка и когда она возникает
  2. Как отличить возможные причины
  3. Пошаговая диагностика и устранение
  4. Минимальный пример и проверка на тестовом проекте
  5. Проверка результата и действия при неуспехе

#Что означает ошибка и когда она возникает

Приложение Node.js в контейнере может отображать локальное время не так, как ожидают пользователи: по умолчанию многие контейнеры используют UTC. При этом Date хранит момент времени независимо от зоны, toISOString() всегда показывает UTC, а Intl.DateTimeFormat может форматировать его в нужной зоне. Инструкция для расхождения отображаемого времени, а не для «исправления» абсолютных меток UTC.

#Как отличить возможные причины

Возможная причинаКак узнатьЧто делать
Окружение контейнера использует UTCВ переменных окружения контейнера отсутствует TZ или она не соответствует ожидаемой зоне.Intl.DateTimeFormat().resolvedOptions().timeZone показывает UTC, хотя пользователи ожидают иной пояс.Явно задавать IANA timeZone при форматировании или согласованно TZ сервиса.
В коде перепутаны UTC и местное времяПриложение ожидает локальный час от new Date().toISOString(), который всегда выдаёт UTC.Метка заканчивается на Z, время расходится ровно на смещение.Оставлять хранение UTC, для экрана использовать Intl.DateTimeFormat(timeZone).
Приложение и база данных имеют разные правила зоныБаза или внешний сервис возвращает строку без зоны, и приложение воспринимает её как локальную.Сдвиг только для значений из БД, тогда как Date.now корректен.Уточнить формат и часовой пояс исходного значения, не прибавлять часы вручную.

#Пошаговая диагностика и устранение

  1. Уточните, какое именно время неверноеОткройте страницу приложения или журнал события и сравните одну и ту же дату с ожидаемым временем и UTC. Проверьте, есть ли у временной метки суффикс Z или явное смещение, например +03:00. Не меняйте системные часы хоста, если расхождение относится только к отображению.
  2. Откройте каталог своего Compose-проектаНа хосте откройте терминал и перейдите в каталог compose.yaml. Выполните docker compose ps, чтобы узнать название сервиса с Node.js. Для примера далее используется имя app; в своём проекте подставляйте точное имя из docker compose config --services. Docker Engine может требовать предоставленного администратором доступа; не используйте sudo автоматически.
  3. Посмотрите текущую зону из самого процесса Node.jsВ терминале проекта выполните docker compose exec app node -e "console.log(process.env.TZ, Intl.DateTimeFormat().resolvedOptions().timeZone, new Date().toISOString())". Замените app реальным сервисом. Вы увидите значение TZ или undefined, определённую Intl зону и UTC-строку с Z. Даже при правильной зоне ISO останется UTC — это нормально.
  4. Проверьте форматирование с явно указанной зонойДля проверки без изменения контейнера выполните команду из блока ниже с timeZone: "Europe/Tallinn" (это только пример допустимого имени IANA). Вывод должен показывать локальное время выбранной зоны. Если форматирование правильное, лучше передавать нужную зону пользователя прямо в форматтер, а не менять глобальные часы приложения.
  5. Найдите место, где рассчитывают или форматируют времяВ исходниках Node.js поищите toISOString(, toLocaleString(, Intl.DateTimeFormat, new Date( и преобразование строк из БД. Если на экран выводят toISOString, замените только отображение на локализованное через Intl.DateTimeFormat с явным timeZone. Сами UTC-метки событий в базе не переписывайте.
  6. Если нужна единая зона сервиса, задайте TZ контролируемоСоздайте резервную копию compose.yaml; в записи services: app: добавьте в environment параметр TZ: Europe/Tallinn либо собственный IANA идентификатор. Применяйте это только если по требованиям приложения весь процесс должен использовать одну зону. Выполните docker compose config -q для проверки; не вставляйте секреты в текст отчёта.
  7. Пересоздайте только нужный сервис в согласованное времяНа тестовом окружении выполните docker compose up -d app после проверки конфигурации: Compose может пересоздать контейнер, это на время прервёт запросы. На рабочем сервере согласуйте время и резервные копии. Если используется Kubernetes или собственный запуск docker run, меняйте переменные соответствующим способом, а не только в Compose.
  8. Проверьте смену летнего/зимнего времени и откатСнова выполните команду диагностики внутри app и проверьте форматирование двух заранее известных дат зимой и летом с указанной зоной IANA. Если форматирование неверно только в старом образе, проверьте версию Node.js/ICU и системную базу зон, не подменяйте смещения вручную. Для отката восстановите compose.yaml и пересоздайте сервис в согласованное окно.
  9. Команды для отдельных ситуацийВыберите только подходящий вариант. Это не одна последовательность: часть команд диагностирует проблему, а часть меняет настройки или данные.
    Выберите подходящий вариант — это не команды для последовательного выполнения
    Вариант 1В каталоге с compose.yaml на хосте.
    Linux · Терминал (Bash)Команда
    В каталоге с compose.yaml на хосте. Показывает сервисы и их фактические имена; нужны права работы с Docker, обычно предоставленные администратором.
    docker compose ps
    docker compose config --services
    Вариант 2Только чтение в работающем сервисе app с Node.js.
    Linux · Терминал (Bash)Команда
    Только чтение в работающем сервисе app с Node.js. app — имя из docker compose config --services. ISO-строка с Z всегда в UTC.
    docker compose exec app node -e "console.log(process.env.TZ, Intl.DateTimeFormat().resolvedOptions().timeZone, new Date().toISOString())"
    Вариант 3Europe/Tallinn — учебная зона IANA; замените на нужную пользователю.
    Linux · Терминал (Bash)Команда
    Europe/Tallinn — учебная зона IANA; замените на нужную пользователю. Не изменяет настройки контейнера.
    docker compose exec app node -e "console.log(new Intl.DateTimeFormat('ru-RU',{timeZone:'Europe/Tallinn',dateStyle:'full',timeStyle:'long'}).format(new Date()))"
    Вариант 4Первая команда проверяет конфигурацию без запуска.
    Linux · Терминал (Bash)С осторожностью
    Первая команда проверяет конфигурацию без запуска. Вторая может пересоздать сервис app и прервать соединения — только после резервной копии файла и в согласованное окно.
    docker compose config -q
    docker compose up -d app

    Команда может изменить состояние системы или её настройки. Проверьте параметры и последствия перед запуском.

#Минимальный пример и проверка на тестовом проекте

Текст
Только фрагмент для понимания environment; в своем compose.yaml сохраните настоящие image, command, ports и volumes. Не заменяйте весь проект этим примером.
services:
  app:
    image: node:24-alpine
    environment:
      TZ: Europe/Tallinn
Текст
Форматирование с явной зоной учитывает исторические и сезонные правила; вторая строка остаётся в UTC. Для другого города подставьте правильный IANA идентификатор.
const instant = new Date("2026-01-15T12:00:00Z");
const formatted = new Intl.DateTimeFormat("ru-RU", {
  timeZone: "Europe/Tallinn", dateStyle: "full", timeStyle: "short"
}).format(instant);
console.log(formatted);
console.log(instant.toISOString());

#Проверка результата и действия при неуспехе

Для выбранной даты время в интерфейсе соответствует нужной зоне, UTC-метки сохраняют точный момент, а зимняя и летняя дата отображаются с корректным смещением. Если приложение продолжает выдавать старую зону, проверьте, какой контейнер и процесс фактически обслуживает запрос, и перезапущен ли он после изменения.

Источники и обновления

Последнее обновление статьи: 2 ноября 2026. Версии программ и интерфейсы меняются — если шаги не совпадают с вашей версией, сверяйтесь с документацией производителя.