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:
@@ -1,5 +1,6 @@
|
||||
import type { RequestMapItem } from "../../model/requestTypes";
|
||||
import type { MapPolygon } from "./model/mapTypes";
|
||||
import { formatUtcDateTime, parseUtc } from "../../utils/utcTime";
|
||||
|
||||
export type RequestAtPoint = { polygon: MapPolygon; item: RequestMapItem };
|
||||
|
||||
@@ -95,18 +96,18 @@ export function Tgu2DMapRequestsPanel({ requests, selectedPolygonId, onHover, on
|
||||
}
|
||||
|
||||
function formatDateTime(value: string): string {
|
||||
return new Date(value).toLocaleString("ru-RU");
|
||||
return formatUtcDateTime(parseUtc(value));
|
||||
}
|
||||
|
||||
function formatDateRange(begin: string, end: string): string {
|
||||
const b = new Date(begin);
|
||||
const e = new Date(end);
|
||||
const b = new Date(parseUtc(begin));
|
||||
const e = new Date(parseUtc(end));
|
||||
const sameDay =
|
||||
b.getFullYear() === e.getFullYear() &&
|
||||
b.getMonth() === e.getMonth() &&
|
||||
b.getDate() === e.getDate();
|
||||
const dateOpts: Intl.DateTimeFormatOptions = { day: "2-digit", month: "2-digit" };
|
||||
const timeOpts: Intl.DateTimeFormatOptions = { hour: "2-digit", minute: "2-digit" };
|
||||
b.getUTCFullYear() === e.getUTCFullYear() &&
|
||||
b.getUTCMonth() === e.getUTCMonth() &&
|
||||
b.getUTCDate() === e.getUTCDate();
|
||||
const dateOpts: Intl.DateTimeFormatOptions = { timeZone: "UTC", day: "2-digit", month: "2-digit" };
|
||||
const timeOpts: Intl.DateTimeFormatOptions = { timeZone: "UTC", hour: "2-digit", minute: "2-digit" };
|
||||
const beginStr = `${b.toLocaleDateString("ru-RU", dateOpts)} ${b.toLocaleTimeString("ru-RU", timeOpts)}`;
|
||||
const endStr = sameDay
|
||||
? b.toLocaleTimeString("ru-RU", timeOpts)
|
||||
|
||||
@@ -3,6 +3,7 @@ import { filterPlatforms, platformLabel } from "../tgu-planning/tguTimelineMappe
|
||||
import { TGU_2D_MAP_LAYERS, type Tgu2DMapLayerKey, type Tgu2DMapLayersState } from "./model/mapLayerTypes";
|
||||
import type { Tgu2DMapLayerAvailability } from "./Tgu2DMapLayers";
|
||||
import type { RequestsState } from "./Tgu2DMapTab";
|
||||
import { parseUtc } from "../../utils/utcTime";
|
||||
|
||||
const DAY_MS = 24 * 60 * 60 * 1000;
|
||||
|
||||
@@ -46,7 +47,7 @@ export function Tgu2DMapSidebar({
|
||||
|
||||
const quickToday = () => {
|
||||
const now = new Date();
|
||||
const fromMs = new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();
|
||||
const fromMs = Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate());
|
||||
onRequestsQuickRange(fromMs, fromMs + DAY_MS);
|
||||
};
|
||||
|
||||
@@ -60,8 +61,8 @@ export function Tgu2DMapSidebar({
|
||||
onRequestsQuickRange(fromMs, fromMs + 30 * DAY_MS);
|
||||
};
|
||||
|
||||
const requestsFromMs = new Date(requestsState.fromValue).getTime();
|
||||
const requestsToMs = new Date(requestsState.toValue).getTime();
|
||||
const requestsFromMs = parseUtc(requestsState.fromValue);
|
||||
const requestsToMs = parseUtc(requestsState.toValue);
|
||||
const requestsRangeInvalid =
|
||||
!Number.isFinite(requestsFromMs) ||
|
||||
!Number.isFinite(requestsToMs) ||
|
||||
|
||||
@@ -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":
|
||||
|
||||
Reference in New Issue
Block a user