From e22effc63f8c83c736825c216b460f902f53d66c Mon Sep 17 00:00:00 2001 From: Anton Standrik Date: Tue, 2 Dec 2025 13:46:07 +0300 Subject: [PATCH] feat: use same duration functions --- src/components/VDiskInfo/VDiskInfo.tsx | 4 +- src/components/VDiskPopup/VDiskPopup.tsx | 4 +- src/containers/Operations/columns.tsx | 11 ++---- .../TopicStats/ConsumersTopicStats.tsx | 6 +-- .../Diagnostics/Consumers/columns/columns.tsx | 8 ++-- .../Partitions/columns/columns.tsx | 12 +++--- ...tUptime.test.ts => formatDuration.test.ts} | 38 +++++++++---------- src/utils/dataFormatters/dataFormatters.ts | 12 +++--- 8 files changed, 46 insertions(+), 49 deletions(-) rename src/utils/dataFormatters/__test__/{formatUptime.test.ts => formatDuration.test.ts} (63%) diff --git a/src/components/VDiskInfo/VDiskInfo.tsx b/src/components/VDiskInfo/VDiskInfo.tsx index 3a9d285756..055ebf6c53 100644 --- a/src/components/VDiskInfo/VDiskInfo.tsx +++ b/src/components/VDiskInfo/VDiskInfo.tsx @@ -7,8 +7,8 @@ import {getPDiskPagePath, useVDiskPagePath} from '../../routes'; import {EVDiskState} from '../../types/api/vdisk'; import {cn} from '../../utils/cn'; import { + formatDurationSeconds, formatStorageValuesToGb, - formatUptimeInSeconds, } from '../../utils/dataFormatters/dataFormatters'; import {createVDiskDeveloperUILink} from '../../utils/developerUI/developerUI'; import {getSeverityColor} from '../../utils/disks/helpers'; @@ -165,7 +165,7 @@ export function VDiskInfo({ }); } if (!isNil(ReplicationSecondsRemaining)) { - const timeRemaining = formatUptimeInSeconds(ReplicationSecondsRemaining); + const timeRemaining = formatDurationSeconds(ReplicationSecondsRemaining); if (timeRemaining) { rightColumn.push({ label: vDiskInfoKeyset('replication-time-remaining'), diff --git a/src/components/VDiskPopup/VDiskPopup.tsx b/src/components/VDiskPopup/VDiskPopup.tsx index 929face6cc..315c5df0b5 100644 --- a/src/components/VDiskPopup/VDiskPopup.tsx +++ b/src/components/VDiskPopup/VDiskPopup.tsx @@ -9,7 +9,7 @@ import {EFlag} from '../../types/api/enums'; import {EVDiskState} from '../../types/api/vdisk'; import {cn} from '../../utils/cn'; import {EMPTY_DATA_PLACEHOLDER} from '../../utils/constants'; -import {formatUptimeInSeconds} from '../../utils/dataFormatters/dataFormatters'; +import {formatDurationSeconds} from '../../utils/dataFormatters/dataFormatters'; import {createVDiskDeveloperUILink} from '../../utils/developerUI/developerUI'; import {getStateSeverity} from '../../utils/disks/calculateVDiskSeverity'; import { @@ -236,7 +236,7 @@ const prepareVDiskData = ( } if (!isNil(ReplicationSecondsRemaining)) { - const timeRemaining = formatUptimeInSeconds(ReplicationSecondsRemaining); + const timeRemaining = formatDurationSeconds(ReplicationSecondsRemaining); if (timeRemaining) { vdiskData.push({ name: vDiskPopupKeyset('label_remaining'), diff --git a/src/containers/Operations/columns.tsx b/src/containers/Operations/columns.tsx index 625392fc53..6db4452c34 100644 --- a/src/containers/Operations/columns.tsx +++ b/src/containers/Operations/columns.tsx @@ -1,4 +1,3 @@ -import {duration} from '@gravity-ui/date-utils'; import {Ban, CircleStop} from '@gravity-ui/icons'; import type {Column as DataTableColumn} from '@gravity-ui/react-data-table'; import {ActionTooltip, Flex, Icon, Text} from '@gravity-ui/uikit'; @@ -8,9 +7,9 @@ import {CellWithPopover} from '../../components/CellWithPopover/CellWithPopover' import {operationsApi} from '../../store/reducers/operations'; import type {IndexBuildMetadata, OperationKind, TOperation} from '../../types/api/operations'; import {EStatusCode} from '../../types/api/operations'; -import {EMPTY_DATA_PLACEHOLDER, HOUR_IN_SECONDS, SECOND_IN_MS} from '../../utils/constants'; +import {EMPTY_DATA_PLACEHOLDER} from '../../utils/constants'; import createToast from '../../utils/createToast'; -import {formatDateTime} from '../../utils/dataFormatters/dataFormatters'; +import {formatDateTime, formatDurationMs} from '../../utils/dataFormatters/dataFormatters'; import {parseProtobufTimestampToMs} from '../../utils/timeParsers'; import {COLUMNS_NAMES, COLUMNS_TITLES} from './constants'; @@ -158,10 +157,8 @@ export function getColumns({ durationValue = Date.now() - createTime; } - const durationFormatted = - durationValue > HOUR_IN_SECONDS * SECOND_IN_MS - ? duration(durationValue).format('hh:mm:ss') - : duration(durationValue).format('mm:ss'); + const safeDurationMs = durationValue < 0 ? 0 : durationValue; + const durationFormatted = formatDurationMs(safeDurationMs); return row.end_time ? durationFormatted diff --git a/src/containers/Tenant/Diagnostics/Consumers/TopicStats/ConsumersTopicStats.tsx b/src/containers/Tenant/Diagnostics/Consumers/TopicStats/ConsumersTopicStats.tsx index ad5fc97be8..58ad2cc9ee 100644 --- a/src/containers/Tenant/Diagnostics/Consumers/TopicStats/ConsumersTopicStats.tsx +++ b/src/containers/Tenant/Diagnostics/Consumers/TopicStats/ConsumersTopicStats.tsx @@ -1,7 +1,7 @@ import {SpeedMultiMeter} from '../../../../../components/SpeedMultiMeter'; import type {IPreparedTopicStats} from '../../../../../types/store/topic'; import {cn} from '../../../../../utils/cn'; -import {formatMsToUptime} from '../../../../../utils/dataFormatters/dataFormatters'; +import {formatDurationMs} from '../../../../../utils/dataFormatters/dataFormatters'; import './ConsumersTopicStats.scss'; @@ -21,11 +21,11 @@ export const ConsumersTopicStats = ({data}: ConsumersTopicStatsProps) => { }, { label: 'Write lag', - value: formatMsToUptime(partitionsWriteLag || 0), + value: formatDurationMs(partitionsWriteLag || 0), }, { label: 'Write idle time', - value: formatMsToUptime(partitionsIdleTime || 0), + value: formatDurationMs(partitionsIdleTime || 0), }, ]; diff --git a/src/containers/Tenant/Diagnostics/Consumers/columns/columns.tsx b/src/containers/Tenant/Diagnostics/Consumers/columns/columns.tsx index f9569d138e..5aac1d4936 100644 --- a/src/containers/Tenant/Diagnostics/Consumers/columns/columns.tsx +++ b/src/containers/Tenant/Diagnostics/Consumers/columns/columns.tsx @@ -9,7 +9,7 @@ import {getTenantPath} from '../../../../../routes'; import {TENANT_DIAGNOSTICS_TABS_IDS} from '../../../../../store/reducers/tenant/constants'; import type {IPreparedConsumerData} from '../../../../../types/store/topic'; import {cn} from '../../../../../utils/cn'; -import {formatMsToUptime} from '../../../../../utils/dataFormatters/dataFormatters'; +import {formatDurationMs} from '../../../../../utils/dataFormatters/dataFormatters'; import {TenantTabsGroups} from '../../../TenantPages'; import {ReadLagsHeader} from '../Headers'; import { @@ -57,7 +57,7 @@ export const columns: Column[] = [ CONSUMERS_READ_LAGS_SUB_COLUMNS_IDS.WRITE_LAG ], align: DataTable.RIGHT, - render: ({row}) => formatMsToUptime(row.writeLag), + render: ({row}) => formatDurationMs(row.writeLag), }, { name: CONSUMERS_READ_LAGS_SUB_COLUMNS_IDS.READ_LAG, @@ -65,7 +65,7 @@ export const columns: Column[] = [ CONSUMERS_READ_LAGS_SUB_COLUMNS_IDS.READ_LAG ], align: DataTable.RIGHT, - render: ({row}) => formatMsToUptime(row.readLag), + render: ({row}) => formatDurationMs(row.readLag), }, { name: CONSUMERS_READ_LAGS_SUB_COLUMNS_IDS.READ_IDLE_TIME, @@ -73,7 +73,7 @@ export const columns: Column[] = [ CONSUMERS_READ_LAGS_SUB_COLUMNS_IDS.READ_IDLE_TIME ], align: DataTable.RIGHT, - render: ({row}) => formatMsToUptime(row.readIdleTime), + render: ({row}) => formatDurationMs(row.readIdleTime), }, ], }, diff --git a/src/containers/Tenant/Diagnostics/Partitions/columns/columns.tsx b/src/containers/Tenant/Diagnostics/Partitions/columns/columns.tsx index 1612163408..91f1caf370 100644 --- a/src/containers/Tenant/Diagnostics/Partitions/columns/columns.tsx +++ b/src/containers/Tenant/Diagnostics/Partitions/columns/columns.tsx @@ -9,7 +9,7 @@ import {useTopicDataAvailable} from '../../../../../store/reducers/capabilities/ import {TENANT_DIAGNOSTICS_TABS_IDS} from '../../../../../store/reducers/tenant/constants'; import {cn} from '../../../../../utils/cn'; import {EMPTY_DATA_PLACEHOLDER} from '../../../../../utils/constants'; -import {formatBytes, formatMsToUptime} from '../../../../../utils/dataFormatters/dataFormatters'; +import {formatBytes, formatDurationMs} from '../../../../../utils/dataFormatters/dataFormatters'; import {isNumeric} from '../../../../../utils/utils'; import {useDiagnosticsPageLinkGetter} from '../../DiagnosticsPages'; import { @@ -86,7 +86,7 @@ export const allColumns: Column[] = [ PARTITIONS_WRITE_LAGS_SUB_COLUMNS_IDS.PARTITION_WRITE_LAG ], align: DataTable.RIGHT, - render: ({row}) => formatMsToUptime(row.partitionWriteLag), + render: ({row}) => formatDurationMs(row.partitionWriteLag), }, { name: PARTITIONS_WRITE_LAGS_SUB_COLUMNS_IDS.PARTITION_WRITE_IDLE_TIME, @@ -94,7 +94,7 @@ export const allColumns: Column[] = [ PARTITIONS_WRITE_LAGS_SUB_COLUMNS_IDS.PARTITION_WRITE_IDLE_TIME ], align: DataTable.RIGHT, - render: ({row}) => formatMsToUptime(row.partitionWriteIdleTime), + render: ({row}) => formatDurationMs(row.partitionWriteIdleTime), }, ], }, @@ -109,7 +109,7 @@ export const allColumns: Column[] = [ PARTITIONS_READ_LAGS_SUB_COLUMNS_IDS.CONSUMER_WRITE_LAG ], align: DataTable.RIGHT, - render: ({row}) => formatMsToUptime(row.consumerWriteLag), + render: ({row}) => formatDurationMs(row.consumerWriteLag), }, { name: PARTITIONS_READ_LAGS_SUB_COLUMNS_IDS.CONSUMER_READ_LAG, @@ -117,7 +117,7 @@ export const allColumns: Column[] = [ PARTITIONS_READ_LAGS_SUB_COLUMNS_IDS.CONSUMER_READ_LAG ], align: DataTable.RIGHT, - render: ({row}) => formatMsToUptime(row.consumerReadLag), + render: ({row}) => formatDurationMs(row.consumerReadLag), }, { name: PARTITIONS_READ_LAGS_SUB_COLUMNS_IDS.CONSUMER_READ_IDLE_TIME, @@ -125,7 +125,7 @@ export const allColumns: Column[] = [ PARTITIONS_READ_LAGS_SUB_COLUMNS_IDS.CONSUMER_READ_IDLE_TIME ], align: DataTable.RIGHT, - render: ({row}) => formatMsToUptime(row.consumerReadIdleTime), + render: ({row}) => formatDurationMs(row.consumerReadIdleTime), }, ], }, diff --git a/src/utils/dataFormatters/__test__/formatUptime.test.ts b/src/utils/dataFormatters/__test__/formatDuration.test.ts similarity index 63% rename from src/utils/dataFormatters/__test__/formatUptime.test.ts rename to src/utils/dataFormatters/__test__/formatDuration.test.ts index 8ad347c2f8..19c9ef5962 100644 --- a/src/utils/dataFormatters/__test__/formatUptime.test.ts +++ b/src/utils/dataFormatters/__test__/formatDuration.test.ts @@ -1,6 +1,6 @@ import {UNBREAKABLE_GAP} from '../../utils'; import { - formatUptimeInSeconds, + formatDurationSeconds, getDowntimeFromDateFormatted, getUptimeFromDateFormatted, } from '../dataFormatters'; @@ -24,46 +24,46 @@ describe('getDowntimeFromDateFormatted', () => { expect(getDowntimeFromDateFormatted(3_600_000, 3_599_000)).toBe('0s'); }); }); -describe('formatUptimeInSeconds', () => { +describe('formatDurationSeconds', () => { const M = 60; const H = 60 * M; const D = 24 * H; test('should return days if value is more than 24h', () => { - expect(formatUptimeInSeconds(24 * H)).toBe('1d' + UNBREAKABLE_GAP + '00:00:00'); - expect(formatUptimeInSeconds(D + H + M + 12)).toBe('1d' + UNBREAKABLE_GAP + '01:01:12'); + expect(formatDurationSeconds(24 * H)).toBe('1d' + UNBREAKABLE_GAP + '00:00:00'); + expect(formatDurationSeconds(D + H + M + 12)).toBe('1d' + UNBREAKABLE_GAP + '01:01:12'); // 1 week - expect(formatUptimeInSeconds(7 * D + H + M + 12)).toBe('7d' + UNBREAKABLE_GAP + '01:01:12'); + expect(formatDurationSeconds(7 * D + H + M + 12)).toBe('7d' + UNBREAKABLE_GAP + '01:01:12'); // 1 month - expect(formatUptimeInSeconds(30 * D + 11 * H + 30 * M + 12)).toBe( + expect(formatDurationSeconds(30 * D + 11 * H + 30 * M + 12)).toBe( '30d' + UNBREAKABLE_GAP + '11:30:12', ); - expect(formatUptimeInSeconds(1234 * D + 12 * H + 12 * M + 12)).toBe( + expect(formatDurationSeconds(1234 * D + 12 * H + 12 * M + 12)).toBe( '1234d' + UNBREAKABLE_GAP + '12:12:12', ); }); test('should return hours if value is less than 24h', () => { - expect(formatUptimeInSeconds(H + M + 12)).toBe('1:01:12'); - expect(formatUptimeInSeconds(12 * H + 12 * M + 12)).toBe('12:12:12'); + expect(formatDurationSeconds(H + M + 12)).toBe('1:01:12'); + expect(formatDurationSeconds(12 * H + 12 * M + 12)).toBe('12:12:12'); }); test('should return minutes if value is less than hour', () => { - expect(formatUptimeInSeconds(M + 12)).toBe('1:12'); - expect(formatUptimeInSeconds(12 * M + 2)).toBe('12:02'); + expect(formatDurationSeconds(M + 12)).toBe('1:12'); + expect(formatDurationSeconds(12 * M + 2)).toBe('12:02'); }); test('should return second if value is less than hour', () => { - expect(formatUptimeInSeconds(12)).toBe('12s'); - expect(formatUptimeInSeconds(2)).toBe('2s'); + expect(formatDurationSeconds(12)).toBe('12s'); + expect(formatDurationSeconds(2)).toBe('2s'); }); test('should correctly process negative values', () => { - expect(formatUptimeInSeconds(-0)).toBe('0s'); - expect(formatUptimeInSeconds(-12)).toBe('-12s'); - expect(formatUptimeInSeconds(-1 * (12 * M + 2))).toBe('-12:02'); - expect(formatUptimeInSeconds(-1 * (12 * H + 12 * M + 12))).toBe('-12:12:12'); - expect(formatUptimeInSeconds(-1 * (12 * D + 12 * H + 12 * M + 12))).toBe( + expect(formatDurationSeconds(-0)).toBe('0s'); + expect(formatDurationSeconds(-12)).toBe('-12s'); + expect(formatDurationSeconds(-1 * (12 * M + 2))).toBe('-12:02'); + expect(formatDurationSeconds(-1 * (12 * H + 12 * M + 12))).toBe('-12:12:12'); + expect(formatDurationSeconds(-1 * (12 * D + 12 * H + 12 * M + 12))).toBe( '-12d' + UNBREAKABLE_GAP + '12:12:12', ); }); test('should return empty placeholder on NaN', () => { - expect(formatUptimeInSeconds(Number.NaN)).toBe(undefined); + expect(formatDurationSeconds(Number.NaN)).toBe(undefined); }); }); diff --git a/src/utils/dataFormatters/dataFormatters.ts b/src/utils/dataFormatters/dataFormatters.ts index 2ee9adfd3f..051c7b015d 100644 --- a/src/utils/dataFormatters/dataFormatters.ts +++ b/src/utils/dataFormatters/dataFormatters.ts @@ -39,9 +39,9 @@ export const stringifyVdiskId = (id?: TVDiskID | TVSlotId) => { /** * It works well only with positive values, - * if you want to get negative formatted uptime, use some wrapper like getDowntimeFromDateFormatted + * if you want to get negative formatted duration, use some wrapper like getDowntimeFromDateFormatted */ -export function formatUptimeInSeconds(seconds: number) { +export function formatDurationSeconds(seconds: number) { if (!isNumeric(seconds)) { return undefined; } @@ -69,8 +69,8 @@ export function formatUptimeInSeconds(seconds: number) { return sign + value; } -export const formatMsToUptime = (ms?: number) => { - return formatUptimeInSeconds(Number(ms) / 1000); +export const formatDurationMs = (ms?: number) => { + return formatDurationSeconds(Number(ms) / 1000); }; export function getUptimeFromDateFormatted(dateFrom?: number | string, dateTo?: number | string) { @@ -80,7 +80,7 @@ export function getUptimeFromDateFormatted(dateFrom?: number | string, dateTo?: // Prevent wrong negative uptime values diff = diff < 0 ? 0 : diff; - return formatUptimeInSeconds(diff); + return formatDurationSeconds(diff); } export function getDowntimeFromDateFormatted(dateFrom?: number | string, dateTo?: number | string) { @@ -90,7 +90,7 @@ export function getDowntimeFromDateFormatted(dateFrom?: number | string, dateTo? // Prevent wrong negative uptime values diff = diff < 0 ? 0 : diff; - return formatUptimeInSeconds(-diff); + return formatDurationSeconds(-diff); } function calcTimeDiffInSec(