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
@@ -28,7 +28,6 @@ import space.nstart.pcp.slots_service.service.SatelliteCatalogService
import space.nstart.pcp.slots_service.service.SatellitePdcmService
import space.nstart.pcp.slots_service.service.SlotService
import java.time.LocalDateTime
import java.time.ZoneOffset
import java.time.format.DateTimeFormatter
import java.time.format.DateTimeParseException
import java.time.format.ResolverStyle
@@ -104,7 +103,7 @@ class CatalogRestController(
private fun parseSendInitialConditionsTime(time: String?): LocalDateTime {
val normalizedTime = time?.trim()?.takeIf { it.isNotEmpty() }
?: return LocalDateTime.now(ZoneOffset.UTC)
?: return LocalDateTime.now()
return try {
LocalDateTime.parse(normalizedTime, sendInitialConditionsTimeFormatter)
@@ -7,7 +7,7 @@ import java.time.LocalDateTime
* UI DTO for the bookings page. It intentionally lives in ui-service because the page combines
* data from slots-service, request-service and satellite-catalog-service without changing their APIs.
*
* Времена ([LocalDateTime]) — UTC, naive ISO без зоны (см. docs/standards/DATETIME_UTC.md).
* Времена ([LocalDateTime]) — naive ISO без зоны (локальное настенное время, без UTC-семантики).
*/
data class BookedSlotViewDTO(
val bookedSlotId: Long,
@@ -3,7 +3,7 @@ package space.nstart.pcp.slots_service.dto.groupstate
import java.time.LocalDateTime
/*
* Времена ([LocalDateTime]) — UTC, naive ISO без зоны (см. docs/standards/DATETIME_UTC.md).
* Времена ([LocalDateTime]) — naive ISO без зоны (локальное настенное время, без UTC-семантики).
*/
data class GroupStateAvailabilityDTO(
@@ -4,7 +4,7 @@ import space.nstart.pcp.pcp_types_lib.dto.ballistics.AscNodeDTO
import java.time.LocalDateTime
/*
* Времена ([LocalDateTime]) — UTC, naive ISO без зоны (см. docs/standards/DATETIME_UTC.md).
* Времена ([LocalDateTime]) — naive ISO без зоны (локальное настенное время, без UTC-семантики).
*/
data class SatellitePdcmAvailabilityDTO(
@@ -3,7 +3,7 @@ package space.nstart.pcp.slots_service.dto.tleanalysis
import java.time.LocalDateTime
/*
* Времена ([LocalDateTime], в т.ч. epoch) — UTC, naive ISO без зоны (см. docs/standards/DATETIME_UTC.md).
* Времена ([LocalDateTime], в т.ч. epoch) — naive ISO без зоны (локальное настенное время, без UTC-семантики).
*/
data class TleAnalysisSatelliteDTO(
@@ -3,7 +3,7 @@ package space.nstart.pcp.slots_service.dto.tlecomparison
import java.time.LocalDateTime
/*
* Времена ([LocalDateTime], epoch/time) — UTC, naive ISO без зоны (см. docs/standards/DATETIME_UTC.md).
* Времена ([LocalDateTime], epoch/time) — naive ISO без зоны (локальное настенное время, без UTC-семантики).
*/
class TleTextRequestDTO(
@@ -426,7 +426,7 @@ private val docReqCovName ="req_cov_"
timeStop: LocalDateTime? = null
): Iterable<CZMLEntity> {
val tstart = timeStart ?: LocalDateTime.now(ZoneOffset.UTC)
val tstart = timeStart ?: LocalDateTime.now()
val tstop = timeStop ?: tstart.plusDays(1)
var vuz = ballisticsService.ascNodes(id, tstart, tstop).collectList().block()
vuz = vuz?.filter { it.time >= tstart && it.time <= tstop }?.sortedBy { it.time }
@@ -20,7 +20,6 @@ import space.nstart.pcp.slots_service.czml.CZMLPolygon
import space.nstart.pcp.slots_service.czml.CZMLPosition
import tools.jackson.databind.ObjectMapper
import java.time.LocalDateTime
import java.time.ZoneOffset
@Service
class CoverageSchemeService(webClientBuilderProvider: ObjectProvider<WebClient.Builder>) {
@@ -186,8 +185,8 @@ class CoverageSchemeModeRowDTO(
val entityId: String = "",
val sequence: Int = 0,
val satelliteId: Long = 0,
val tn: LocalDateTime = LocalDateTime.now(ZoneOffset.UTC),
val tk: LocalDateTime = LocalDateTime.now(ZoneOffset.UTC),
val tn: LocalDateTime = LocalDateTime.now(),
val tk: LocalDateTime = LocalDateTime.now(),
val roll: Double? = null,
val revolutionSign: RevolutionSign? = null,
val contour: String = "",
@@ -7,7 +7,6 @@ import space.nstart.pcp.slots_service.dto.pdcm.SatellitePdcmAvailabilityDTO
import space.nstart.pcp.slots_service.dto.pdcm.SatellitePdcmDetailsDTO
import space.nstart.pcp.slots_service.dto.pdcm.SatellitePdcmSummaryDTO
import java.time.LocalDateTime
import java.time.ZoneOffset
@Service
class SatellitePdcmService(
@@ -15,7 +14,7 @@ class SatellitePdcmService(
private val ballisticsService: BallisticsService
) {
fun satelliteSummaries(): List<SatellitePdcmSummaryDTO> = satelliteSummaries(LocalDateTime.now(ZoneOffset.UTC))
fun satelliteSummaries(): List<SatellitePdcmSummaryDTO> = satelliteSummaries(LocalDateTime.now())
fun satelliteSummaries(referenceTime: LocalDateTime): List<SatellitePdcmSummaryDTO> {
val satellites = satelliteCatalogService.allSatellites()
@@ -40,7 +39,7 @@ class SatellitePdcmService(
}
fun satelliteDetails(satelliteId: Long): SatellitePdcmDetailsDTO =
satelliteDetails(satelliteId, LocalDateTime.now(ZoneOffset.UTC))
satelliteDetails(satelliteId, LocalDateTime.now())
fun satelliteDetails(satelliteId: Long, referenceTime: LocalDateTime): SatellitePdcmDetailsDTO {
val satellites = satelliteCatalogService.allSatellites()