Skip to content

feat: change uptime format #1748

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 50 additions & 4 deletions src/utils/dataFormatters/__test__/formatUptime.test.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,67 @@
import {getDowntimeFromDateFormatted, getUptimeFromDateFormatted} from '../dataFormatters';
import {EMPTY_DATA_PLACEHOLDER} from '../../constants';
import {UNBREAKABLE_GAP} from '../../utils';
import {
formatUptimeInSeconds,
getDowntimeFromDateFormatted,
getUptimeFromDateFormatted,
} from '../dataFormatters';

describe('getUptimeFromDateFormatted', () => {
it('should calculate and format uptime', () => {
expect(getUptimeFromDateFormatted(3_600_000, 7_200_000)).toBe('1:00:00');
});
it('should return 0 if dateFrom after dateTo', () => {
expect(getUptimeFromDateFormatted(3_600_000, 3_599_000)).toBe('0:00:00');
expect(getUptimeFromDateFormatted(3_600_000, 3_599_000)).toBe('0s');
});
});
describe('getDowntimeFromDateFormatted', () => {
it('should calculate and format downtime as -uptime', () => {
expect(getDowntimeFromDateFormatted(3_600_000, 7_200_000)).toBe('-1:00:00');
});
it('should not add sign if downtime is 0', () => {
expect(getDowntimeFromDateFormatted(3_600_000, 3_600_000)).toBe('0:00:00');
expect(getDowntimeFromDateFormatted(3_600_000, 3_600_000)).toBe('0s');
});
it('should return 0 if dateFrom after dateTo', () => {
expect(getDowntimeFromDateFormatted(3_600_000, 3_599_000)).toBe('0:00:00');
expect(getDowntimeFromDateFormatted(3_600_000, 3_599_000)).toBe('0s');
});
});
describe('formatUptimeInSeconds', () => {
const M = 60;
const H = 60 * M;
const D = 24 * H;

it('should return days if value is more than 24h', () => {
expect(formatUptimeInSeconds(D)).toBe('1d' + UNBREAKABLE_GAP + '00:00:00');
expect(formatUptimeInSeconds(D + H + M + 12)).toBe('1d' + UNBREAKABLE_GAP + '01:01:12');
expect(formatUptimeInSeconds(12 * D + 12 * H + 12 * M + 12)).toBe(
'12d' + UNBREAKABLE_GAP + '12:12:12',
);
expect(formatUptimeInSeconds(1234 * D + 12 * H + 12 * M + 12)).toBe(
'1234d' + UNBREAKABLE_GAP + '12:12:12',
);
});
it('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');
});
it('should return minutes if value is less than hour', () => {
expect(formatUptimeInSeconds(M + 12)).toBe('1:12');
expect(formatUptimeInSeconds(12 * M + 2)).toBe('12:02');
});
it('should return second if value is less than hour', () => {
expect(formatUptimeInSeconds(12)).toBe('12s');
expect(formatUptimeInSeconds(2)).toBe('2s');
});
it('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(
'-12d' + UNBREAKABLE_GAP + '12:12:12',
);
});
it('should return empty placeholder on NaN', () => {
expect(formatUptimeInSeconds(Number.NaN)).toBe(EMPTY_DATA_PLACEHOLDER);
});
});
48 changes: 32 additions & 16 deletions src/utils/dataFormatters/dataFormatters.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import {dateTimeParse} from '@gravity-ui/date-utils';
import {dateTimeParse, duration} from '@gravity-ui/date-utils';

import type {TVDiskID, TVSlotId} from '../../types/api/vdisk';
import {
formatBytes as formatBytesCustom,
getSizeWithSignificantDigits,
} from '../bytesParsers/formatBytes';
import type {BytesSizes} from '../bytesParsers/formatBytes';
import {DAY_IN_SECONDS, HOUR_IN_SECONDS} from '../constants';
import {EMPTY_DATA_PLACEHOLDER, HOUR_IN_SECONDS} from '../constants';
import {configuredNumeral} from '../numeral';
import {isNumeric} from '../utils';
import {UNBREAKABLE_GAP, isNumeric} from '../utils';

import {formatValues} from './common';
import {formatNumberWithDigits, getNumberWithSignificantDigits} from './formatNumber';
Expand Down Expand Up @@ -40,19 +40,38 @@ export const stringifyVdiskId = (id?: TVDiskID | TVSlotId) => {
return id ? Object.values(id).join('-') : '';
};

export const formatUptimeInSeconds = (seconds: number) => {
const days = Math.floor(seconds / DAY_IN_SECONDS);
const remain = seconds % DAY_IN_SECONDS;
/**
* It works well only with positive values,
* if you want to get negative formatted uptime, use some wrapper like getDowntimeFromDateFormatted
*/
export function formatUptimeInSeconds(seconds: number) {
if (!isNumeric(seconds)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets handle somehow negative values. As for me this function should work properly with all values. And we should use it for negative and positive uptime.

return EMPTY_DATA_PLACEHOLDER;
}

const uptime = [days && `${days}d`, configuredNumeral(remain).format('00:00:00')]
.filter(Boolean)
.join(' ');
// duration.format() doesn't work well with negative values
// negative value will be displayed like -2d -12:-58:-21
// so we process positive duration and only then add sign if any
const sign = seconds < 0 ? '-' : '';
const d = duration(Math.abs(seconds), 's').rescale();

return uptime;
};
let value: string;

if (d.days() > 0) {
value = d.format(`d[${i18n('d')}${UNBREAKABLE_GAP}]hh:mm:ss`);
} else if (d.hours() > 0) {
value = d.format('h:mm:ss');
} else if (d.minutes() > 0) {
value = d.format('m:ss');
} else {
value = d.format(`s[${i18n('s')}]`);
}

return sign + value;
}

export const formatMsToUptime = (ms?: number) => {
return ms && formatUptimeInSeconds(ms / 1000);
return formatUptimeInSeconds(Number(ms) / 1000);
};

export function getUptimeFromDateFormatted(dateFrom?: number | string, dateTo?: number | string) {
Expand All @@ -72,10 +91,7 @@ export function getDowntimeFromDateFormatted(dateFrom?: number | string, dateTo?
// Prevent wrong negative uptime values
diff = diff < 0 ? 0 : diff;

const formattedUptime = formatUptimeInSeconds(diff);

// Do not add sign to 0 values to prevent -0:00:00 uptime
return diff === 0 ? formattedUptime : '-' + formattedUptime;
return formatUptimeInSeconds(-diff);
}

export function calcTimeDiffInSec(
Expand Down
5 changes: 4 additions & 1 deletion src/utils/dataFormatters/i18n/en.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
{
"format-cpu.cores": ["core", "cores", "cores", "cores"]
"format-cpu.cores": ["core", "cores", "cores", "cores"],

"d": "d",
"s": "s"
}
3 changes: 1 addition & 2 deletions src/utils/dataFormatters/i18n/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import {registerKeysets} from '../../i18n';

import en from './en.json';
import ru from './ru.json';

const COMPONENT = 'ydb-format-cpu';

export default registerKeysets(COMPONENT, {ru, en});
export default registerKeysets(COMPONENT, {en});
3 changes: 0 additions & 3 deletions src/utils/dataFormatters/i18n/ru.json

This file was deleted.

2 changes: 1 addition & 1 deletion src/utils/nodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export function prepareNodeSystemState(
const DC = systemState.Location?.DataCenter || systemState.DataCenter;
const TenantName = systemState?.Tenants?.[0];

let Uptime: string;
let Uptime: PreparedNodeSystemState['Uptime'];

if (systemState.DisconnectTime) {
Uptime = getDowntimeFromDateFormatted(systemState.DisconnectTime);
Expand Down
8 changes: 7 additions & 1 deletion tests/suites/nodes/nodes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,13 @@ test.describe('Test Nodes Paginated Table', async () => {
const uptimeValues = await paginatedTable.getColumnValues('Uptime');

for (const uptime of uptimeValues) {
expect(uptime).toMatch(/^(\d+d\s)?(\d+):(\d{2}):(\d{2})$/); // Format: DDd? HH:MM:SS
// \d+d\xa0\d{2}:\d{2}:\d{2} - DDd HH:MM:SS, 1d 00:20:30
// \d{1,2}:\d{2}:\d{2}$ - HH:MM:SS, 1:02:02 or 12:02:02
// \d{1,2}:\d{2}$ - MM:SS, 1:02 or 12:02
// \d{1,2}s$ - SSs, 1s or 12s
expect(uptime).toMatch(
/^(\d+d\xa0\d{2}:\d{2}:\d{2}|\d{1,2}:\d{2}:\d{2}|\d{1,2}:\d{2}|\d{1,2}s)$/,
);
}
});

Expand Down
Loading