Объясните, как контракт HTTP должен описывать даты-время, чтобы интеграционный тест выявлял расхождение часовых поясов между клиентом и сервером.
Контракт должен явно задавать семантику значения: это конкретный момент времени или локальная дата-время. Для момента времени следует передавать часовой пояс через смещение или использовать нормализованное значение в UTC с однозначным обозначением зоны; локальную дату-время без зоны нельзя считать полноценным описанием момента.
Интеграционный тест должен отправить значение через реальный HTTP-контур, проверить его сериализацию на стороне клиента и затем убедиться, что сервер интерпретировал тот же момент, а не локальное время своей среды.
Распределённые системы работают на машинах с разными часовыми поясами, настройками перехода на летнее время и локалями. Если передавать дату-время без информации о зоне, одна и та же строка может обозначать разные моменты у клиента и сервера.
Стандартизированные форматы, включая представление даты-времени со смещением по правилам RFC 3339, появились как способ устранить неоднозначность сериализации и обмена временными значениями между системами. Сам формат не решает проблему автоматически: контракт также должен определить смысл поля, допустимую точность и правила преобразования.
Предположим, клиент формирует значение события в часовом поясе UTC+3, а сервер разбирает строку как локальное время в UTC. Если зона не указана, сервер может сохранить или обработать событие на три часа раньше фактического момента.
Такой дефект особенно опасен для сроков действия токенов, платежей, расписаний, дедлайнов и последовательности событий. Тесты могут проходить на машине разработчика, если окружения случайно используют один часовой пояс, но завершаться неверными результатами в CI или продакшене.
Сначала контракт должен разделить два разных типа данных:
Для полей-моментов обычно выбирают единый формат: клиент передаёт дату-время с зоной, а сервер после разбора приводит её к UTC для хранения и сравнения. Важно не удалять смещение до проверки и не полагаться на часовой пояс операционной системы.
Контракт также должен определить допустимую точность, например секунды или миллисекунды, правила округления и поведение при отсутствии зоны или некорректном значении. Интеграционный тест должен запускаться в окружениях с разными часовыми поясами либо явно фиксировать зоны клиента и сервера, чтобы проверять именно преобразование через границу системы.
Недостаточно проверить только строковый формат. Нужно сравнить смысл значения: после отправки время должно соответствовать ожидаемому моменту в UTC или эквивалентному абсолютному представлению. Отдельный тест должен проверять переход на летнее время, если система принимает локальные даты и зависит от региональных правил.
Компромисс нормализации к UTC — потеря исходной зоны, которая иногда нужна для отображения пользователю или повторного расчёта локального расписания. Поэтому для таких сценариев часто хранят и момент в UTC, и исходную бизнес-зону, не смешивая их семантику.
Сервис бронирования принимал время начала встречи без указания зоны. Локально интеграционные тесты проходили, потому что тестовый клиент и сервер работали в одном часовом поясе. После переноса сервера в другой регион бронирования стали начинаться на несколько часов раньше.
Рассматривались три варианта. Первый — зафиксировать одинаковый часовой пояс на всех машинах; это быстро, но скрывает дефект и делает систему зависимой от настроек окружения. Второй — передавать только UTC; это однозначно для момента, но не подходит, если пользователь задаёт именно местное время в конкретном регионе. Третий — явно разделить момент начала и бизнес-часовой пояс.
Выбрали третий вариант: контракт стал требовать однозначный момент для исполнения, а зона пользователя сохранялась отдельно для отображения и будущих операций с расписанием. Интеграционные тесты начали запускать с разными часовыми поясами и проверять эквивалентность момента, а не совпадение исходной строки. Ошибка стала воспроизводимой до выката.
Для конкретного момента смещение обычно достаточно: оно однозначно определяет положение времени относительно UTC. Но для будущего расписания одного смещения может быть недостаточно, потому что правила перехода на летнее время меняются в зависимости от региона и даты.
Например, значение с фиксированным смещением не сообщает, нужно ли в дальнейшем применять правила конкретной зоны. Поэтому для повторяющихся событий или пользовательских расписаний контракту может понадобиться идентификатор зоны, а не только смещение.
Регулярное выражение может подтвердить форму строки, но не её смысл. Оно не проверяет, что клиент и сервер одинаково интерпретируют смещение, сохраняют точность и преобразуют значение в тот же момент.
Интеграционная проверка должна пройти через фактическую сериализацию, HTTP-транспорт, разбор и сохранение значения. Затем нужно сравнить нормализованный момент или наблюдаемое бизнес-поведение, например рассчитанный срок действия.
В контракте нужно явно определить обязательность зоны и ожидаемый отказ сервера. Тест отправляет дату-время без зоны и проверяет не только код ошибки, но и то, что значение не было молча принято как локальное время сервера.
Это защищает от скрытой зависимости от окружения. Если система допускает локальное значение, контракт должен назвать источник зоны: отдельное поле, настройки пользователя или фиксированную бизнес-зону; неявный выбор зоны операционной системы является плохим основанием для интеграционного контракта.