pcp: время — сквозной naive LocalDateTime как локальное настенное время (без UTC)

Отказ от остаточной UTC-семантики: naive LocalDateTime трактуется как
локальное время единой зоны всех сервисов.

Backend (границы внешних систем):
- VisibilityPayloadParser, SyncSpacecraftFromNsiUseCase: ZoneOffset.UTC →
  ZoneId.systemDefault() для проекции внешних Instant/ISO-с-Z в naive.
- тесты границ перевёрнуты на systemDefault (под Europe/Moscow ждут +3).
- чистка UTC-формулировок в DTO-комментариях, мёртвых ссылок на
  docs/standards/DATETIME_UTC.md.

Frontend (pcp-tgu-ops-ui):
- utils/utcTime.ts → utils/localTime.ts: parseLocal/formatLocal*/toLocalIso/
  toLocalInputValue с локальной семантикой (без дописывания Z и timeZone:UTC).
- замена getUTC*/Date.UTC/toISOString-сериализации на локальные аналоги.
- убраны пользовательские подписи «UTC» в редакторе/таймлайне.
- удалён utcTime.guard.test.ts (форсил обратный UTC-инвариант); тест-фикстуры
  сделаны зоно-независимыми. tsc + vitest зелёные под TZ=UTC и TZ=Europe/Moscow.

Включает также прочие незакоммиченные правки рабочего дерева.
This commit is contained in:
Дмитрий Соловьев
2026-06-04 09:50:39 +03:00
parent 5049395911
commit 294b17757d
123 changed files with 556 additions and 535 deletions
@@ -12,9 +12,9 @@ object CloneMode {
data class MissionCloneRequest(
/** [CloneMode.REPLACE] — полный клон; [CloneMode.FORK_FORWARD] — только включения, перекрывающиеся с окном. */
val mode: String,
/** Начало окна нового плана (UTC naive). Обязательно для fork-forward. */
/** Начало окна нового плана (naive, локальное время). Обязательно для fork-forward. */
val windowStart: LocalDateTime? = null,
/** Конец окна нового плана (UTC naive). Обязательно для fork-forward. */
/** Конец окна нового плана (naive, локальное время). Обязательно для fork-forward. */
val windowStop: LocalDateTime? = null
)
@@ -24,8 +24,8 @@ data class MissionCloneResponse(val newMissionId: UUID)
data class MissionBlankRequest(
/** КА (= noradId). */
val satelliteId: Long,
/** Начало окна миссии (UTC naive). */
/** Начало окна миссии (naive, локальное время). */
val missionStart: LocalDateTime,
/** Конец окна миссии (UTC naive). */
/** Конец окна миссии (naive, локальное время). */
val missionStop: LocalDateTime
)
@@ -15,7 +15,6 @@ import space.nstart.pcp.pcp_types_lib.message.KafkaMessage
import space.nstart.pcp.pcp_types_lib.message.PcpKafkaEvent
import java.nio.charset.StandardCharsets
import java.time.LocalDateTime
import java.time.ZoneOffset
import java.util.UUID
@Component
@@ -60,7 +59,7 @@ class BookedSlotsEventPublisher(
val event = BookedSlotsStatusChangedEvent(
bookedSlotIds = normalizedIds,
status = status,
occurredAt = LocalDateTime.now(ZoneOffset.UTC),
occurredAt = LocalDateTime.now(),
sourceService = applicationName,
missionId = missionId,
modeId = modeId,
@@ -62,7 +62,6 @@ import space.nstart.pcp.pcp_types_lib.dto.requests.slots.BookedSlotStatus
import space.nstart.pcp.pcp_types_lib.dto.satellite.mission.SatelliteModeResponseDTO
import java.time.Duration
import java.time.LocalDateTime
import java.time.ZoneOffset
import java.util.UUID
import kotlin.math.PI
import kotlin.math.abs
@@ -433,7 +432,7 @@ class MissionPlaningService(
overrideIntervalEnd: LocalDateTime? = null,
comPlanSnapshotId: Long? = null
): MissionSurveyCalculationResponseDTO {
val startedAt = LocalDateTime.now(ZoneOffset.UTC)
val startedAt = LocalDateTime.now()
logger.info(
"Расчет плана съемки stage 5 по persisted snapshot этапа 4 для миссии {}: requestedComPlanSnapshotId={}",
missionId,
@@ -521,7 +520,7 @@ class MissionPlaningService(
)
if (survs.isEmpty()) {
val finishedAt = LocalDateTime.now(ZoneOffset.UTC)
val finishedAt = LocalDateTime.now()
return MissionSurveyCalculationResponseDTO(
missionId = missionId,
completed = true,
@@ -566,7 +565,7 @@ class MissionPlaningService(
modeBookings.size
)
val finishedAt = LocalDateTime.now(ZoneOffset.UTC)
val finishedAt = LocalDateTime.now()
return MissionSurveyCalculationResponseDTO(
missionId = missionId,
completed = true,