Формат времени в UTC
This commit is contained in:
@@ -20,7 +20,7 @@ import type { MapPolygon, Tgu2DMapScene } from "../tgu-map-2d/model/mapTypes";
|
||||
import type { Tgu2DMapSelection } from "../tgu-map-2d/model/mapSelectionTypes";
|
||||
import { Tgu2DMapView } from "../tgu-map-2d/Tgu2DMapView";
|
||||
import { MapObjectInspector } from "../tgu-map-2d/MapObjectInspector";
|
||||
import { formatLocalDateTime, parseLocal, toLocalInputValue, toLocalIso } from "../../utils/localTime";
|
||||
import { formatLocalDateTime, parseLocal, toLocalInputValue, toUtcIso } from "../../utils/localTime";
|
||||
import { formatVolume } from "../../utils/dataVolume";
|
||||
|
||||
type TguComplexPlanTabProps = {
|
||||
@@ -173,7 +173,7 @@ export function TguComplexPlanTab({ range, platforms }: TguComplexPlanTabProps)
|
||||
let cancelled = false;
|
||||
setRequestsLoading(true);
|
||||
setRequestsError(undefined);
|
||||
fetchRequestsForMap({ endFrom: toLocalIso(fromMs), beginTo: toLocalIso(toMs) })
|
||||
fetchRequestsForMap({ endFrom: toUtcIso(fromMs), beginTo: toUtcIso(toMs) })
|
||||
.then((result) => {
|
||||
if (!cancelled) setRequestItems(result.items);
|
||||
})
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
import { abandonDraft, createDraft, fetchDraftPlans } from "../../api/tguPlanApi";
|
||||
import { TguApiError } from "../../model/tguTypes";
|
||||
import type { TguPlanUi } from "../../model/timelineTypes";
|
||||
import { parseLocal, toLocalIso } from "../../utils/localTime";
|
||||
import { parseLocal, toUtcIso } from "../../utils/localTime";
|
||||
import { Tgu2DMapView } from "../tgu-map-2d/Tgu2DMapView";
|
||||
import { parseWktPolygon } from "../tgu-map-2d/geometry/wktParser";
|
||||
import { buildStationMarkers } from "../tgu-map-2d/Tgu2DMapLayers";
|
||||
@@ -360,8 +360,8 @@ export function TguEditorTab({ selectedSpacecraftId, selectedPlan, appliedRange,
|
||||
useEffect(() => {
|
||||
const controller = new AbortController();
|
||||
void fetchRequestsForMap({
|
||||
endFrom: toLocalIso(window.fromMs),
|
||||
beginTo: toLocalIso(window.toMs)
|
||||
endFrom: toUtcIso(window.fromMs),
|
||||
beginTo: toUtcIso(window.toMs)
|
||||
}).then((result) => {
|
||||
if (!controller.signal.aborted) {
|
||||
setRequestItems(result.items);
|
||||
@@ -458,7 +458,7 @@ export function TguEditorTab({ selectedSpacecraftId, selectedPlan, appliedRange,
|
||||
void buildSurveyRoute({
|
||||
satelliteId: numericNorad,
|
||||
// Окно задано явно (начало/конец) — строим вперёд от начала на выбранную длительность.
|
||||
time: toLocalIso(routeWindow.startMs),
|
||||
time: toUtcIso(routeWindow.startMs),
|
||||
durationSeconds,
|
||||
roll: routeRoll,
|
||||
captureAngle: DEFAULT_CAPTURE_ANGLE_DEG,
|
||||
@@ -617,9 +617,9 @@ export function TguEditorTab({ selectedSpacecraftId, selectedPlan, appliedRange,
|
||||
noradId: noradId ? Number(noradId) : 0,
|
||||
stationId: Number.isFinite(stationNum) ? stationNum : 0,
|
||||
revolution: work.revolution ?? 0,
|
||||
onStart: { time: toLocalIso(work.startMs).slice(0, 19), elevation: 0, azimuth: 0, range: 0 },
|
||||
onMaximum: { time: toLocalIso(midMs).slice(0, 19), elevation: 0, azimuth: 0, range: 0 },
|
||||
onStop: { time: toLocalIso(work.endMs).slice(0, 19), elevation: 0, azimuth: 0, range: 0 },
|
||||
onStart: { time: toUtcIso(work.startMs), elevation: 0, azimuth: 0, range: 0 },
|
||||
onMaximum: { time: toUtcIso(midMs), elevation: 0, azimuth: 0, range: 0 },
|
||||
onStop: { time: toUtcIso(work.endMs), elevation: 0, azimuth: 0, range: 0 },
|
||||
duration: Math.max(1, (work.endMs - work.startMs) / 1000)
|
||||
};
|
||||
editDropPrefillRef.current = { zone, surveyIds: work.surveyIds ?? [] };
|
||||
@@ -744,15 +744,15 @@ export function TguEditorTab({ selectedSpacecraftId, selectedPlan, appliedRange,
|
||||
if (editWork) await deleteEditedModeFromDraft(draftId, draftExistedBefore, editWork, "SURVEY");
|
||||
savedRoute = await saveSurveyRoute(draftId, {
|
||||
satelliteId: numericNorad,
|
||||
time: toLocalIso(routeWindow.startMs),
|
||||
time: toUtcIso(routeWindow.startMs),
|
||||
durationSeconds: routeDurationSeconds,
|
||||
roll: routeRoll,
|
||||
captureAngle: DEFAULT_CAPTURE_ANGLE_DEG,
|
||||
revolution: selectedShootPass.revolution ?? 0,
|
||||
anchor: "FORWARD",
|
||||
// Окно выбранного плана — чтобы при автосоздании миссии/плана задать корректный интервал.
|
||||
planStart: toLocalIso(selectedPlan.startMs),
|
||||
planEnd: toLocalIso(selectedPlan.endMs)
|
||||
planStart: toUtcIso(selectedPlan.startMs),
|
||||
planEnd: toUtcIso(selectedPlan.endMs)
|
||||
});
|
||||
setSurveyRoute(savedRoute);
|
||||
// Ждём перечитывания planModes ДО снятия editingWorkId ниже: иначе на миг editingModeId
|
||||
@@ -853,8 +853,8 @@ export function TguEditorTab({ selectedSpacecraftId, selectedPlan, appliedRange,
|
||||
zoneStop: selectedZone.onStop.time.slice(0, 19),
|
||||
surveyModeIds: [...selectedDropSurveyIds],
|
||||
// Окно выбранного плана — для автосоздания миссии/плана с корректным интервалом.
|
||||
planStart: toLocalIso(selectedPlan.startMs),
|
||||
planEnd: toLocalIso(selectedPlan.endMs)
|
||||
planStart: toUtcIso(selectedPlan.startMs),
|
||||
planEnd: toUtcIso(selectedPlan.endMs)
|
||||
});
|
||||
savedDropId = savedDrop.id;
|
||||
savedDropVolumeMb = savedDrop.volumeMb;
|
||||
|
||||
@@ -13,7 +13,7 @@ import { Tgu2DMapSidebar } from "./Tgu2DMapSidebar";
|
||||
import { Tgu2DMapToolbar } from "./Tgu2DMapToolbar";
|
||||
import { Tgu2DMapView } from "./Tgu2DMapView";
|
||||
import { getTgu2DMapIntervalState, TGU_2D_MAP_INTERVAL_WARNING } from "./tgu2DMapInterval";
|
||||
import { parseLocal, toLocalInputValue, toLocalIso } from "../../utils/localTime";
|
||||
import { parseLocal, toLocalInputValue, toUtcIso } from "../../utils/localTime";
|
||||
|
||||
type Tgu2DMapTabProps = {
|
||||
range: TimelineRange;
|
||||
@@ -82,8 +82,8 @@ export function Tgu2DMapTab({
|
||||
try {
|
||||
const result = await fetchRequestsForMap(
|
||||
{
|
||||
endFrom: toLocalIso(fromMs),
|
||||
beginTo: toLocalIso(toMs)
|
||||
endFrom: toUtcIso(fromMs),
|
||||
beginTo: toUtcIso(toMs)
|
||||
},
|
||||
(loaded, total) => {
|
||||
setRequestsState((current) => ({ ...current, loadedCount: loaded, totalItems: total }));
|
||||
|
||||
@@ -22,10 +22,10 @@ describe("futureZrvOptions", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it("produces naive (no zone) start/stop strings", () => {
|
||||
it("produces UTC start/stop strings", () => {
|
||||
const [option] = futureZrvOptions([zrv("2026-06-03T13:00:00", "2026-06-03T13:10:00")], NOW);
|
||||
expect(option.start).toBe("2026-06-03T13:00:00");
|
||||
expect(option.stop).toBe("2026-06-03T13:10:00");
|
||||
expect(option.start).toBe(new Date(2026, 5, 3, 13, 0, 0).toISOString());
|
||||
expect(option.stop).toBe(new Date(2026, 5, 3, 13, 10, 0).toISOString());
|
||||
expect(option.kppId).toBe("KPP-1");
|
||||
});
|
||||
});
|
||||
@@ -33,11 +33,11 @@ describe("futureZrvOptions", () => {
|
||||
describe("buildChainPlanRequest", () => {
|
||||
const start: ZrvOption = {
|
||||
id: "a", startMs: Date.UTC(2026, 5, 3, 13, 0, 0), endMs: Date.UTC(2026, 5, 3, 13, 10, 0),
|
||||
start: "2026-06-03T13:00:00", stop: "2026-06-03T13:10:00", kppId: "KPP-1"
|
||||
start: "2026-06-03T13:00:00.000Z", stop: "2026-06-03T13:10:00.000Z", kppId: "KPP-1"
|
||||
};
|
||||
const end: ZrvOption = {
|
||||
id: "b", startMs: Date.UTC(2026, 5, 3, 15, 0, 0), endMs: Date.UTC(2026, 5, 3, 15, 10, 0),
|
||||
start: "2026-06-03T15:00:00", stop: "2026-06-03T15:10:00", kppId: "KPP-2"
|
||||
start: "2026-06-03T15:00:00.000Z", stop: "2026-06-03T15:10:00.000Z", kppId: "KPP-2"
|
||||
};
|
||||
|
||||
it("spans from start zone begin to end zone finish and keeps the start kpp", () => {
|
||||
@@ -45,16 +45,16 @@ describe("buildChainPlanRequest", () => {
|
||||
expect(req).toEqual({
|
||||
spacecraftId: "25544",
|
||||
basePlanId: "base-1",
|
||||
windowStart: "2026-06-03T13:00:00",
|
||||
windowStop: "2026-06-03T15:10:00",
|
||||
windowStart: "2026-06-03T13:00:00.000Z",
|
||||
windowStop: "2026-06-03T15:10:00.000Z",
|
||||
windowKppId: "KPP-1"
|
||||
});
|
||||
});
|
||||
|
||||
it("allows a single-zone window (start == end zone)", () => {
|
||||
const req = buildChainPlanRequest("25544", null, start, start);
|
||||
expect(req.windowStart).toBe("2026-06-03T13:00:00");
|
||||
expect(req.windowStop).toBe("2026-06-03T13:10:00");
|
||||
expect(req.windowStart).toBe("2026-06-03T13:00:00.000Z");
|
||||
expect(req.windowStop).toBe("2026-06-03T13:10:00.000Z");
|
||||
expect(req.basePlanId).toBeNull();
|
||||
});
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { parseLocal, toLocalIso } from "../../utils/localTime";
|
||||
import { parseLocal, toUtcIso } from "../../utils/localTime";
|
||||
import type { CreateChainPlanRequest, ZrvWindowResponse } from "../../api/tguPlanApi";
|
||||
|
||||
/**
|
||||
* Опция выбора ЗРВ в диалоге «Создать план». Из стартовой зоны берутся начало окна и КПП, из
|
||||
* конечной — конец окна. `start`/`stop` — naive (ISO без зоны, локальное), как ждёт `LocalDateTime` бэка;
|
||||
* конечной — конец окна. `start`/`stop` — UTC ISO строки для API;
|
||||
* `startMs`/`endMs` — для сортировки/сравнения и подсветки на таймлайне.
|
||||
*/
|
||||
export type ZrvOption = {
|
||||
@@ -11,9 +11,9 @@ export type ZrvOption = {
|
||||
id: string;
|
||||
startMs: number;
|
||||
endMs: number;
|
||||
/** Naive (локальное) начала зоны. */
|
||||
/** UTC ISO начала зоны. */
|
||||
start: string;
|
||||
/** Naive (локальное) конца зоны. */
|
||||
/** UTC ISO конца зоны. */
|
||||
stop: string;
|
||||
kppId: string;
|
||||
};
|
||||
@@ -28,8 +28,8 @@ export function futureZrvOptions(windows: ZrvWindowResponse[], nowMs: number): Z
|
||||
id: `${zrv.kppId}|${zrv.startTime}|${zrv.endTime}`,
|
||||
startMs,
|
||||
endMs,
|
||||
start: toNaive(startMs),
|
||||
stop: toNaive(endMs),
|
||||
start: toApiUtc(startMs),
|
||||
stop: toApiUtc(endMs),
|
||||
kppId: zrv.kppId
|
||||
};
|
||||
})
|
||||
@@ -60,6 +60,6 @@ export function buildChainPlanRequest(
|
||||
};
|
||||
}
|
||||
|
||||
function toNaive(ms: number): string {
|
||||
return toLocalIso(ms);
|
||||
function toApiUtc(ms: number): string {
|
||||
return toUtcIso(ms);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import type { RequestAtPoint } from "./RequestDetailsPanel";
|
||||
import type { Tgu2DMapSelection } from "../tgu-map-2d/model/mapSelectionTypes";
|
||||
import type { GeoPoint, MapPolygon } from "../tgu-map-2d/model/mapTypes";
|
||||
import { generateUuid } from "../../utils/uuid";
|
||||
import { parseLocal, toLocalInputValue, toLocalIso } from "../../utils/localTime";
|
||||
import { parseLocal, toLocalInputValue, toUtcIso } from "../../utils/localTime";
|
||||
import { DateTime24Field } from "../../components/timeFields";
|
||||
import { RequestCreatePanel, type RequestFormSubmit } from "./RequestCreatePanel";
|
||||
import { RequestDetailsPanel } from "./RequestDetailsPanel";
|
||||
@@ -82,7 +82,7 @@ export function TguRequestsTab({ range, invalidRange, platforms }: TguRequestsTa
|
||||
setRequestsState((current) => ({ ...current, loading: true, error: undefined, loadedCount: 0 }));
|
||||
try {
|
||||
const result = await fetchRequestsForMap(
|
||||
{ endFrom: toLocalIso(fromMs), beginTo: toLocalIso(toMs) },
|
||||
{ endFrom: toUtcIso(fromMs), beginTo: toUtcIso(toMs) },
|
||||
(loaded, total) => setRequestsState((current) => ({ ...current, loadedCount: loaded, totalItems: total }))
|
||||
);
|
||||
setRequestsState((current) => ({
|
||||
@@ -204,8 +204,8 @@ export function TguRequestsTab({ range, invalidRange, platforms }: TguRequestsTa
|
||||
name: form.name,
|
||||
geometry,
|
||||
importance: form.importance,
|
||||
beginDateTime: toLocalIso(parseLocal(form.beginDateTime)),
|
||||
endDateTime: toLocalIso(parseLocal(form.endDateTime)),
|
||||
beginDateTime: toUtcIso(parseLocal(form.beginDateTime)),
|
||||
endDateTime: toUtcIso(parseLocal(form.endDateTime)),
|
||||
kpp: form.kpp,
|
||||
highPriorityTransmit: form.highPriorityTransmit,
|
||||
optics: form.optics,
|
||||
|
||||
@@ -23,7 +23,7 @@ import type { Tgu2DMapLayersState } from "../tgu-map-2d/model/mapLayerTypes";
|
||||
import type { Tgu2DMapSelection } from "../tgu-map-2d/model/mapSelectionTypes";
|
||||
import { MapObjectInspector } from "../tgu-map-2d/MapObjectInspector";
|
||||
import { Tgu2DMapView } from "../tgu-map-2d/Tgu2DMapView";
|
||||
import { formatLocalDateTimeSeconds, parseLocal, toLocalInputValue, toLocalIso } from "../../utils/localTime";
|
||||
import { formatLocalDateTimeSeconds, parseLocal, toLocalInputValue, toUtcIso } from "../../utils/localTime";
|
||||
import { SlotDaySelector, type SlotDay } from "./SlotDaySelector";
|
||||
import { PassStepper } from "./PassStepper";
|
||||
import { groupPasses } from "./slotPasses";
|
||||
@@ -469,7 +469,7 @@ export function TguSlotsTab({ range }: TguSlotsTabProps) {
|
||||
setRequestsLoading(true);
|
||||
setRequestsError(undefined);
|
||||
try {
|
||||
const result = await fetchRequestsForMap({ endFrom: toLocalIso(fromMs), beginTo: toLocalIso(toMs) });
|
||||
const result = await fetchRequestsForMap({ endFrom: toUtcIso(fromMs), beginTo: toUtcIso(toMs) });
|
||||
setRequestItems(result.items);
|
||||
setRequestsTotal(result.totalItems);
|
||||
setRequestsTruncated(result.truncated);
|
||||
@@ -730,8 +730,8 @@ export function TguSlotsTab({ range }: TguSlotsTabProps) {
|
||||
const result = await fetchRequestSlots(
|
||||
{
|
||||
requestId: selectedRequestId,
|
||||
timeStart: toLocalIso(parsedRange.fromMs),
|
||||
timeStop: toLocalIso(parsedRange.toMs),
|
||||
timeStart: toUtcIso(parsedRange.fromMs),
|
||||
timeStop: toUtcIso(parsedRange.toMs),
|
||||
revSign: revSign || undefined,
|
||||
// cov=true (режим покрытия) ровно когда задана стратегия — это путь «Подобрать
|
||||
// покрытие». «Показать все слоты» идёт без стратегии и получает полный набор кандидатов,
|
||||
|
||||
Reference in New Issue
Block a user