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:
Дмитрий Соловьев
2026-06-02 16:53:48 +03:00
parent faf264e416
commit fb0332a030
12 changed files with 304 additions and 73 deletions
@@ -15,6 +15,7 @@ import { Tgu2DMapStationPanel } from "./Tgu2DMapStationPanel";
import { Tgu2DMapToolbar } from "./Tgu2DMapToolbar";
import { Tgu2DMapView } from "./Tgu2DMapView";
import { getTgu2DMapIntervalState, TGU_2D_MAP_INTERVAL_WARNING } from "./tgu2DMapInterval";
import { parseUtc, toUtcInputValue } from "../../utils/utcTime";
type Tgu2DMapTabProps = {
range: TimelineRange;
@@ -37,8 +38,8 @@ export type RequestsState = {
function buildInitialRequestsState(range: TimelineRange): RequestsState {
return {
fromValue: formatDateTimeLocal(range.fromMs),
toValue: formatDateTimeLocal(range.toMs),
fromValue: toUtcInputValue(range.fromMs),
toValue: toUtcInputValue(range.toMs),
items: [],
loading: false,
loadedCount: 0,
@@ -77,8 +78,8 @@ export function Tgu2DMapTab({
const mapInvalidRange = invalidRange || mapInterval.tooLarge;
const loadRequests = useCallback(async (fromValue: string, toValue: string) => {
const fromMs = new Date(fromValue).getTime();
const toMs = new Date(toValue).getTime();
const fromMs = parseUtc(fromValue);
const toMs = parseUtc(toValue);
if (!Number.isFinite(fromMs) || !Number.isFinite(toMs) || fromMs >= toMs) return;
setRequestsState((current) => ({ ...current, loading: true, error: undefined, loadedCount: 0 }));
@@ -123,8 +124,8 @@ export function Tgu2DMapTab({
}, []);
const setRequestsQuickRange = useCallback((fromMs: number, toMs: number) => {
const fromValue = formatDateTimeLocal(fromMs);
const toValue = formatDateTimeLocal(toMs);
const fromValue = toUtcInputValue(fromMs);
const toValue = toUtcInputValue(toMs);
setRequestsState((current) => ({ ...current, fromValue, toValue }));
void loadRequests(fromValue, toValue);
}, [loadRequests]);
@@ -296,6 +297,7 @@ export function Tgu2DMapTab({
function formatDate(ms: number): string {
return new Date(ms).toLocaleString("ru-RU", {
timeZone: "UTC",
day: "2-digit",
month: "2-digit",
hour: "2-digit",
@@ -303,16 +305,6 @@ function formatDate(ms: number): string {
});
}
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}`;
}
function layerLabel(layer: Tgu2DMapLayerKey): string {
switch (layer) {
case "tracks":