Возврат к модели «настенных часов»: одна ISO-строка без зоны
("2026-05-29T01:00:00") от БД до экрана, никто не привязывает её к
часовому поясу, сдвигаться нечему.
Бэкенд — снять зону с границы API:
- DTO OffsetDateTime/Instant -> LocalDateTime и убрана конвертация
atOffset(ZoneOffset.UTC)/toUtcOffset() в фабриках и мапперах:
tgu PlanResponse/NewChainDTOs/TguPlanningDTO, request RequestApiDtos,
complex ComplexPlanRun*DTO, dynamic ObservationParametersDebugDTO.
- Удалён толерантный Jackson-слой: PcpTimeModule(+autoconfig, ServiceLoader,
imports), PcpTimeModuleV3, LocalDateTimeTimeZoneSerializer; снята их
регистрация в Camunda/Rest/dynamic JacksonConfig. Штатный JSR-310
(JavaTimeModule + WRITE_DATES_AS_TIMESTAMPS=false) оставлен.
- Kafka-конверт KafkaMessage.time сериализуется ISO без офсета.
- mission-planing Mode/SurveyMode/DropMode entity и репозитории/сервис
переведены на LocalDateTime; колонки БД остаются timestamptz (Hibernate
мапит на naive без миграции — прецедент MissionEntity/PlanEntity).
- RequestServiceClient (dynamic) синхронизирован по типу с request-service.
Анти-регрессия: LocalDateTime.now(ZoneOffset.UTC) для серверных штампов
оставлен (выбор настенных часов UTC, не зона на проводе).
Фронт — снять офсет с egress: toUtcIso/фильтры отдают naive без Z
(после удаления толерантного десериализатора штатный парсер падал бы на Z).
Дисплейные хелперы (показ «как пришло», без сдвига) сохранены.
Тесты: контрактные ждут naive ISO; удалены UTC-артефакты (нормализация
офсетов) и тесты прослоек. Удалён docs/standards/DATETIME_UTC.md.
Операционно перед выкатом: сервисы катить согласованно; Kafka-топики с
ранее опубликованным офсетом +00:00 дренировать/проиграть.
WebFlux в tgu был фиктивным: блокирующая JPA, контроллеры оборачивали
блокирующие вызовы в Mono.fromCallable{}.subscribeOn(boundedElastic()),
WebClient .block()-ался сразу. Единственный WebFlux-only сервис флота.
- build: подключён pcp-types-lib (тянет starter-web → servlet), springdoc
webflux-ui → webmvc-ui; starter-webflux оставлен только ради WebClient
- контроллеры возвращают значения напрямую (без Mono-обёрток)
- удалено локальное Jackson-2 зеркало PcpTimeModule.kt → модуль из либы
в CamundaJacksonConfig; Jackson-3 зеркало остаётся (Boot 4 держит
Jackson 3 на HTTP-границе и для MVC), WebFluxJacksonConfig → RestJacksonConfig
KafkaMessage.time: LocalDateTime.now() -> now(ZoneOffset.UTC);
LocalDateTimeTimeZoneSerializer: atZone(systemDefault()) -> atOffset(UTC).
Было: локальное now() + systemDefault давали на проводе "...+03:00", и
потребитель (паттерн ...SSS[xxx] в LocalDateTime) дропал зону -> сдвиг на пояс.
Стало: "...+00:00" — инстант тот же, но стенные часы совпадают с UTC, сдвиг ушёл.
Тест LocalDateTimeTimeZoneSerializerTest: UTC-офсет без сдвига + round-trip.
DATETIME_UTC.md: баг перенесён в «Исправленные».
PcpTimeModule (libs/pcp-types-lib): толерантные Jackson-десериализаторы
LocalDateTime/OffsetDateTime/Instant — любой потребитель читает оба формата
на проводе и нормализует к UTC. Только чтение, формат записи не меняется.
Подключение фронтом по флоту:
- Spring Boot auto-configuration + AutoConfiguration.imports — Boot-маппер
всех 12 сервисов на pcp-types-lib (без правок в сервисах);
- Jackson SPI (META-INF/services) — ручные мапперы с findAndRegisterModules();
- точечно: dynamic-plan/JacksonConfig (ручной ObjectMapper);
- tgu (WebFlux, не зависит от либы) — локальное зеркало модуля в
CamundaJacksonConfig (Jackson-2 путь).
Зачем: микросервисы деплоятся независимо; с толерантностью продюсера можно
флипать LocalDateTime -> OffsetDateTime (Z) в любом порядке без синхронного
деплоя — потребитель не падает на парсинге офсета.
Тесты: PcpTimeModuleTest (оба формата -> один UTC-момент + авто-подхват SPI).
Доки: DATETIME_UTC.md (раздел 2bis + найденные баги формата), CHECKLIST.md.
PlanResponse (pcp-tgu-service) и зависящий от него TguPlanResponse (pcp-ui-service)
переведены с naive LocalDateTime на OffsetDateTime в UTC → на проводе явная зона Z.
Домен/БД (PlannedPlanEntity/CalculatedPlan) остаются naive LocalDateTime (= UTC);
конвертация только на границе API (atOffset(ZoneOffset.UTC)). Потребитель один
(ui-service.TguPlanningService), обновлён синхронно; фронт parseUtc совместим с Z.
- контрактные тесты обновлены на Z (PlanResponseContractTest, TguPlanningDtoContractTest);
- тестовые фикстуры/ассерты приведены к OffsetDateTime/Z (PlanQueryServiceTest,
CatalogControllerTest, TguPlanningServiceTest);
- docs/standards/DATETIME_UTC.md: отражена миграция и обновлена таблица сервисов.
Фронт (pcp-tgu-ops-ui):
- тест-страж utcTime.guard.test.ts: запрещает Date.parse / new Date(string) /
toLocale* без timeZone вне utils/utcTime; тестовые фикстуры переведены на parseUtc.
Бэкенд:
- UTC-now: LocalDateTime.now() -> LocalDateTime.now(ZoneOffset.UTC) по всем сервисам
(entity-дефолты, воркеры, use case'ы) и в тестовых фикстурах.
- Конвертации к UTC (баги на не-UTC JVM, каждая с регресс-тестом в не-UTC поясе):
- tgu: VisibilityPayloadParser, SyncSpacecraftFromNsiUseCase (Instant -> UTC LocalDateTime);
- mission-planing: MissionPlaningService (OffsetDateTime -> UTC через toUtcLocalDateTime);
- dynamic-plan: RequestServiceClient (OffsetDateTime -> UTC);
- slots: SlotRepositoryImpl (getObject(LocalDateTime) вместо getTimestamp().toLocalDateTime()).
- Контрактные сериализационные тесты формата на проводе (naive без зоны / Z):
PlanResponse (tgu), RequestResponseDto (request, эталон), TguPlanning DTO (ui).
- KDoc-инвариант «=UTC» на UI-facing DTO (tgu PlanResponse, ui-service DTO вкладки ТГУ).
Документация: docs/standards/DATETIME_UTC.md (соглашение, таблица сервисов, под-задачи на миграцию к Z),
docs/standards/CHECKLIST.md (пункт про UTC на API).