294b17757d
Отказ от остаточной 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. Включает также прочие незакоммиченные правки рабочего дерева.
66 lines
2.6 KiB
TypeScript
66 lines
2.6 KiB
TypeScript
import { parseLocal, toLocalIso } from "../../utils/localTime";
|
|
import type { CreateChainPlanRequest, ZrvWindowResponse } from "../../api/tguPlanApi";
|
|
|
|
/**
|
|
* Опция выбора ЗРВ в диалоге «Создать план». Из стартовой зоны берутся начало окна и КПП, из
|
|
* конечной — конец окна. `start`/`stop` — naive (ISO без зоны, локальное), как ждёт `LocalDateTime` бэка;
|
|
* `startMs`/`endMs` — для сортировки/сравнения и подсветки на таймлайне.
|
|
*/
|
|
export type ZrvOption = {
|
|
/** Детерминированный id зоны (для value в select и key). */
|
|
id: string;
|
|
startMs: number;
|
|
endMs: number;
|
|
/** Naive (локальное) начала зоны. */
|
|
start: string;
|
|
/** Naive (локальное) конца зоны. */
|
|
stop: string;
|
|
kppId: string;
|
|
};
|
|
|
|
/** Будущие ЗРВ-зоны (строго позже nowMs), по возрастанию начала. */
|
|
export function futureZrvOptions(windows: ZrvWindowResponse[], nowMs: number): ZrvOption[] {
|
|
return windows
|
|
.map((zrv) => {
|
|
const startMs = parseLocal(zrv.startTime);
|
|
const endMs = parseLocal(zrv.endTime);
|
|
return {
|
|
id: `${zrv.kppId}|${zrv.startTime}|${zrv.endTime}`,
|
|
startMs,
|
|
endMs,
|
|
start: toNaive(startMs),
|
|
stop: toNaive(endMs),
|
|
kppId: zrv.kppId
|
|
};
|
|
})
|
|
.filter((option) => option.startMs > nowMs)
|
|
.sort((a, b) => a.startMs - b.startMs);
|
|
}
|
|
|
|
/**
|
|
* Собирает тело запроса создания плана новой цепочки из выбора оператора. Окно — от начала стартовой
|
|
* зоны до конца конечной; `basePlanId` — предыстория (или null для цепочки без истории). Бросает,
|
|
* если конец окна не позже начала (конечная зона раньше стартовой).
|
|
*/
|
|
export function buildChainPlanRequest(
|
|
spacecraftId: string,
|
|
basePlanId: string | null,
|
|
start: ZrvOption,
|
|
end: ZrvOption
|
|
): CreateChainPlanRequest {
|
|
if (end.endMs <= start.startMs) {
|
|
throw new Error("Конец окна должен быть позже начала: выберите конечную ЗРВ не раньше стартовой.");
|
|
}
|
|
return {
|
|
spacecraftId,
|
|
basePlanId,
|
|
windowStart: start.start,
|
|
windowStop: end.stop,
|
|
windowKppId: start.kppId
|
|
};
|
|
}
|
|
|
|
function toNaive(ms: number): string {
|
|
return toLocalIso(ms);
|
|
}
|