pcp-tgu-ops-ui: единый UTC во времени; дефолтный диапазон Планы
Время бэка (naive LocalDateTime и OffsetDateTime) разбиралось через new Date(string)/Date.parse, из-за чего naive-строки трактовались как локальное время браузера: вкладки расходились по времени, а путь создания заявки сдвигал введённое время на офсет. Ввёл единую точку utils/utcTime (parseUtc/formatUtc*/toUtcInputValue/toUtcIso) и перевёл на неё вкладки «Планы», «Заявки», «Карта 2D» — чтение, пикеры диапазона, создание заявки. Плюс на вкладке «Планы» график по умолчанию открывается на интервал сейчас−30 мин … сейчас+3 дня. docs/tasks/TASK_datetime-utc-consistency.md — ТЗ на сверку всей группы сервисов pcp на единообразие времени.
This commit is contained in:
@@ -2,6 +2,7 @@ import type { TguPlanDecision } from "../../model/tguTypes";
|
||||
import type { TguPlanUi } from "../../model/timelineTypes";
|
||||
import { platformLabel } from "./tguTimelineMapper";
|
||||
import { statusStyle } from "./tguStatus";
|
||||
import { formatUtcDateTime, parseUtc } from "../../utils/utcTime";
|
||||
|
||||
type TguPlanDetailsProps = {
|
||||
plan?: TguPlanUi;
|
||||
@@ -90,7 +91,7 @@ function Detail({ label, value, mono }: { label: string; value?: string; mono?:
|
||||
}
|
||||
|
||||
function formatDateTime(value: string): string {
|
||||
return new Date(value).toLocaleString("ru-RU");
|
||||
return formatUtcDateTime(parseUtc(value));
|
||||
}
|
||||
|
||||
function formatDuration(ms: number): string {
|
||||
|
||||
@@ -17,13 +17,15 @@ import {
|
||||
mapPlans,
|
||||
operationalSpacecraftIds
|
||||
} from "./tguTimelineMapper";
|
||||
import { parseUtc, toUtcInputValue } from "../../utils/utcTime";
|
||||
|
||||
const DAY_MS = 24 * 60 * 60 * 1000;
|
||||
const MINUTE_MS = 60 * 1000;
|
||||
|
||||
export function TguPlanningPage() {
|
||||
const initialRange = useMemo(defaultRange, []);
|
||||
const [fromValue, setFromValue] = useState(formatDateTimeLocal(initialRange.fromMs));
|
||||
const [toValue, setToValue] = useState(formatDateTimeLocal(initialRange.toMs));
|
||||
const [fromValue, setFromValue] = useState(toUtcInputValue(initialRange.fromMs));
|
||||
const [toValue, setToValue] = useState(toUtcInputValue(initialRange.toMs));
|
||||
const [appliedRange, setAppliedRange] = useState<TimelineRange>(initialRange);
|
||||
const [rawPlatforms, setRawPlatforms] = useState<TguPlatform[]>([]);
|
||||
const [plans, setPlans] = useState<TguPlanUi[]>([]);
|
||||
@@ -45,8 +47,8 @@ export function TguPlanningPage() {
|
||||
|
||||
const parsedInputRange = useMemo(
|
||||
() => ({
|
||||
fromMs: new Date(fromValue).getTime(),
|
||||
toMs: new Date(toValue).getTime()
|
||||
fromMs: parseUtc(fromValue),
|
||||
toMs: parseUtc(toValue)
|
||||
}),
|
||||
[fromValue, toValue]
|
||||
);
|
||||
@@ -133,8 +135,8 @@ export function TguPlanningPage() {
|
||||
const quickRange = (hours: number) => {
|
||||
const fromMs = startOfCurrentDayMs();
|
||||
const toMs = fromMs + hours * 60 * 60 * 1000;
|
||||
setFromValue(formatDateTimeLocal(fromMs));
|
||||
setToValue(formatDateTimeLocal(toMs));
|
||||
setFromValue(toUtcInputValue(fromMs));
|
||||
setToValue(toUtcInputValue(toMs));
|
||||
setAppliedRange({ fromMs, toMs });
|
||||
setPageError(undefined);
|
||||
setDecisionNotice(undefined);
|
||||
@@ -253,23 +255,13 @@ export function TguPlanningPage() {
|
||||
}
|
||||
|
||||
function defaultRange(): TimelineRange {
|
||||
const fromMs = startOfCurrentDayMs();
|
||||
return { fromMs, toMs: fromMs + DAY_MS };
|
||||
const now = Date.now();
|
||||
return { fromMs: now - 30 * MINUTE_MS, toMs: now + 3 * DAY_MS };
|
||||
}
|
||||
|
||||
function startOfCurrentDayMs(): number {
|
||||
const now = new Date();
|
||||
return new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();
|
||||
}
|
||||
|
||||
function formatDateTimeLocal(ms: number): string {
|
||||
const date = new Date(ms);
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, "0");
|
||||
const day = String(date.getDate()).padStart(2, "0");
|
||||
const hours = String(date.getHours()).padStart(2, "0");
|
||||
const minutes = String(date.getMinutes()).padStart(2, "0");
|
||||
return `${year}-${month}-${day}T${hours}:${minutes}`;
|
||||
return Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate());
|
||||
}
|
||||
|
||||
function historyDaysForRange(range: TimelineRange): number {
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
} from "./tguTimelineLayout";
|
||||
import { platformLabel } from "./tguTimelineMapper";
|
||||
import { statusStyle } from "./tguStatus";
|
||||
import { formatUtcDate, formatUtcDateTime, formatUtcTime, parseUtc } from "../../utils/utcTime";
|
||||
|
||||
type TguTimelineProps = {
|
||||
rows: TimelineRow[];
|
||||
@@ -193,13 +194,13 @@ function TimelineArrow({ from, to }: { from: TimelinePlanSegment; to: TimelinePl
|
||||
}
|
||||
|
||||
function formatTickDate(ms: number): string {
|
||||
return new Date(ms).toLocaleDateString("ru-RU", { day: "2-digit", month: "2-digit" });
|
||||
return formatUtcDate(ms);
|
||||
}
|
||||
|
||||
function formatTickTime(ms: number): string {
|
||||
return new Date(ms).toLocaleTimeString("ru-RU", { hour: "2-digit", minute: "2-digit" });
|
||||
return formatUtcTime(ms);
|
||||
}
|
||||
|
||||
function formatDateTime(value: string): string {
|
||||
return new Date(value).toLocaleString("ru-RU");
|
||||
return formatUtcDateTime(parseUtc(value));
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { TguPlan, TguPlatform } from "../../model/tguTypes";
|
||||
import type { TguPlanUi, TguPlatformUi, TimelineRange, TimelineRow } from "../../model/timelineTypes";
|
||||
import { parseUtc } from "../../utils/utcTime";
|
||||
|
||||
const PROBLEM_STATUSES = new Set(["REJECTED", "START_AMBIGUOUS"]);
|
||||
|
||||
@@ -26,8 +27,8 @@ export function mapPlans(plans: TguPlan[], platforms: TguPlatform[]): TguPlanUi[
|
||||
|
||||
return plans
|
||||
.map((plan) => {
|
||||
const startMs = new Date(plan.startTime).getTime();
|
||||
const endMs = new Date(plan.endTime).getTime();
|
||||
const startMs = parseUtc(plan.startTime);
|
||||
const endMs = parseUtc(plan.endTime);
|
||||
const platform = platformByNorad.get(plan.spacecraftId);
|
||||
return {
|
||||
...plan,
|
||||
|
||||
Reference in New Issue
Block a user