fix: #20173 Time zone notations for UI timestamps (#20293)

* fix: #20173 Time zone notations for UI timestamps

* fixed the bot test
This commit is contained in:
Shailesh Parmar 2025-03-18 10:02:05 +05:30 committed by GitHub
parent b2497fb36e
commit 20086f71ee
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 73 additions and 47 deletions

View File

@ -173,6 +173,7 @@ export const TestSuites = () => {
{ {
title: `${t('label.success')} %`, title: `${t('label.success')} %`,
dataIndex: 'summary', dataIndex: 'summary',
width: 200,
key: 'success', key: 'success',
render: (value: TestSuite['summary']) => { render: (value: TestSuite['summary']) => {
const percent = const percent =

View File

@ -41,7 +41,7 @@ import { removeTestCaseFromTestSuite } from '../../../../rest/testAPI';
import { getNameFromFQN, Transi18next } from '../../../../utils/CommonUtils'; import { getNameFromFQN, Transi18next } from '../../../../utils/CommonUtils';
import { import {
formatDate, formatDate,
formatDateTime, formatDateTimeLong,
} from '../../../../utils/date-time/DateTimeUtils'; } from '../../../../utils/date-time/DateTimeUtils';
import { import {
getColumnNameFromEntityLink, getColumnNameFromEntityLink,
@ -254,7 +254,7 @@ const DataQualityTab: React.FC<DataQualityTabProps> = ({
width: 150, width: 150,
sorter: true, sorter: true,
render: (result: TestCaseResult) => render: (result: TestCaseResult) =>
result?.timestamp ? formatDateTime(result.timestamp) : '--', result?.timestamp ? formatDateTimeLong(result.timestamp) : '--',
}, },
{ {
title: t('label.incident'), title: t('label.incident'),

View File

@ -13,31 +13,42 @@
import { queryByAttribute, render, screen } from '@testing-library/react'; import { queryByAttribute, render, screen } from '@testing-library/react';
import React from 'react'; import React from 'react';
import { MOCK_CHART_COLLECTION_DATA } from '../../../../mocks/TestSuite.mock';
import { ProfilerDetailsCardProps } from '../ProfilerDashboard/profilerDashboard.interface'; import { ProfilerDetailsCardProps } from '../ProfilerDashboard/profilerDashboard.interface';
import ProfilerDetailsCard from './ProfilerDetailsCard'; import ProfilerDetailsCard from './ProfilerDetailsCard';
// Mock utility functions
jest.mock('../../../../utils/ChartUtils', () => ({
axisTickFormatter: jest.fn(),
tooltipFormatter: jest.fn(),
updateActiveChartFilter: jest.fn(),
}));
jest.mock('../../../../utils/date-time/DateTimeUtils', () => ({
formatDateTimeLong: jest.fn(),
}));
// Existing mocks
jest.mock('../ProfilerLatestValue/ProfilerLatestValue', () =>
jest.fn(() => <div>ProfilerLatestValue</div>)
);
jest.mock('../../../common/ErrorWithPlaceholder/ErrorPlaceHolder', () =>
jest.fn(() => <div>ErrorPlaceHolder</div>)
);
jest.mock('../../../../utils/DataInsightUtils', () => ({
CustomTooltip: jest.fn(() => <div>CustomTooltip</div>),
}));
// Improve mock data to be minimal
const mockProps: ProfilerDetailsCardProps = { const mockProps: ProfilerDetailsCardProps = {
chartCollection: MOCK_CHART_COLLECTION_DATA, chartCollection: {
data: [{ name: 'test', value: 1 }],
information: [{ dataKey: 'value', title: 'Test', color: '#000' }],
},
name: 'rowCount', name: 'rowCount',
}; };
jest.mock('../ProfilerLatestValue/ProfilerLatestValue', () => {
return jest.fn().mockImplementation(() => {
return <div>ProfilerLatestValue</div>;
});
});
jest.mock('../../../common/ErrorWithPlaceholder/ErrorPlaceHolder', () => {
return jest.fn().mockImplementation(() => {
return <div>ErrorPlaceHolder</div>;
});
});
jest.mock('../../../../utils/DataInsightUtils', () => {
return jest.fn().mockImplementation(() => {
return <div>CustomTooltip</div>;
});
});
describe('ProfilerDetailsCard Test', () => { describe('ProfilerDetailsCard Test', () => {
it('Component should render', async () => { it('Component should render', async () => {
const { container } = render(<ProfilerDetailsCard {...mockProps} />); const { container } = render(<ProfilerDetailsCard {...mockProps} />);

View File

@ -31,7 +31,7 @@ import {
updateActiveChartFilter, updateActiveChartFilter,
} from '../../../../utils/ChartUtils'; } from '../../../../utils/ChartUtils';
import { CustomTooltip } from '../../../../utils/DataInsightUtils'; import { CustomTooltip } from '../../../../utils/DataInsightUtils';
import { formatDateTime } from '../../../../utils/date-time/DateTimeUtils'; import { formatDateTimeLong } from '../../../../utils/date-time/DateTimeUtils';
import ErrorPlaceHolder from '../../../common/ErrorWithPlaceholder/ErrorPlaceHolder'; import ErrorPlaceHolder from '../../../common/ErrorWithPlaceholder/ErrorPlaceHolder';
import { ProfilerDetailsCardProps } from '../ProfilerDashboard/profilerDashboard.interface'; import { ProfilerDetailsCardProps } from '../ProfilerDashboard/profilerDashboard.interface';
import ProfilerLatestValue from '../ProfilerLatestValue/ProfilerLatestValue'; import ProfilerLatestValue from '../ProfilerLatestValue/ProfilerLatestValue';
@ -98,7 +98,7 @@ const ProfilerDetailsCard: React.FC<ProfilerDetailsCardProps> = ({
<Tooltip <Tooltip
content={ content={
<CustomTooltip <CustomTooltip
dateTimeFormatter={formatDateTime} dateTimeFormatter={formatDateTimeLong}
timeStampKey="timestamp" timeStampKey="timestamp"
valueFormatter={(value) => valueFormatter={(value) =>
tooltipFormatter(value, tickFormatter) tooltipFormatter(value, tickFormatter)

View File

@ -52,7 +52,7 @@ import {
CustomTooltip, CustomTooltip,
getRandomHexColor, getRandomHexColor,
} from '../../../../../utils/DataInsightUtils'; } from '../../../../../utils/DataInsightUtils';
import { formatDateTime } from '../../../../../utils/date-time/DateTimeUtils'; import { formatDateTimeLong } from '../../../../../utils/date-time/DateTimeUtils';
import { import {
showErrorToast, showErrorToast,
showSuccessToast, showSuccessToast,
@ -268,7 +268,7 @@ const CustomMetricGraphs = ({
<Tooltip <Tooltip
content={ content={
<CustomTooltip <CustomTooltip
dateTimeFormatter={formatDateTime} dateTimeFormatter={formatDateTimeLong}
timeStampKey="timestamp" timeStampKey="timestamp"
valueFormatter={(value) => valueFormatter={(value) =>
tooltipFormatter(value) tooltipFormatter(value)

View File

@ -20,7 +20,7 @@ import { TABLE_FRESHNESS_KEY } from '../../../../constants/TestSuite.constant';
import { Thread } from '../../../../generated/entity/feed/thread'; import { Thread } from '../../../../generated/entity/feed/thread';
import { import {
convertMillisecondsToHumanReadableFormat, convertMillisecondsToHumanReadableFormat,
formatDateTime, formatDateTimeLong,
} from '../../../../utils/date-time/DateTimeUtils'; } from '../../../../utils/date-time/DateTimeUtils';
import { getTaskDetailPath } from '../../../../utils/TasksUtils'; import { getTaskDetailPath } from '../../../../utils/TasksUtils';
import { OwnerLabel } from '../../../common/OwnerLabel/OwnerLabel.component'; import { OwnerLabel } from '../../../common/OwnerLabel/OwnerLabel.component';
@ -95,7 +95,7 @@ const TestSummaryCustomTooltip = (
<Card <Card
title={ title={
<Typography.Title level={5}> <Typography.Title level={5}>
{formatDateTime(payload[0].payload.name)} {formatDateTimeLong(payload[0].payload.name)}
</Typography.Title> </Typography.Title>
}> }>
<ul data-testid="test-summary-tooltip-container"> <ul data-testid="test-summary-tooltip-container">

View File

@ -39,7 +39,9 @@ const mockProps = {
], ],
}; };
jest.mock('../../../../utils/date-time/DateTimeUtils', () => ({ jest.mock('../../../../utils/date-time/DateTimeUtils', () => ({
formatDateTime: jest.fn().mockReturnValue('Jan 3, 2024, 6:45 PM'), formatDateTimeLong: jest
.fn()
.mockReturnValue('Jan 3, 2024, 6:45 PM (UTC+05:30)'),
})); }));
jest.mock('../../../../utils/TasksUtils', () => ({ jest.mock('../../../../utils/TasksUtils', () => ({

View File

@ -57,7 +57,7 @@ import {
getPartialNameFromTableFQN, getPartialNameFromTableFQN,
} from '../../utils/CommonUtils'; } from '../../utils/CommonUtils';
import { import {
formatDateTime, formatDateTimeLong,
getCurrentMillis, getCurrentMillis,
getEpochMillisForPastDays, getEpochMillisForPastDays,
} from '../../utils/date-time/DateTimeUtils'; } from '../../utils/date-time/DateTimeUtils';
@ -468,8 +468,8 @@ const IncidentManager = ({
title: t('label.execution-time'), title: t('label.execution-time'),
dataIndex: 'timestamp', dataIndex: 'timestamp',
key: 'timestamp', key: 'timestamp',
width: 150, width: 200,
render: (value: number) => (value ? formatDateTime(value) : '--'), render: (value: number) => (value ? formatDateTimeLong(value) : '--'),
}, },
{ {
title: t('label.status'), title: t('label.status'),
@ -500,7 +500,7 @@ const IncidentManager = ({
title: t('label.severity'), title: t('label.severity'),
dataIndex: 'severity', dataIndex: 'severity',
key: 'severity', key: 'severity',
width: 150, width: 100,
render: (value: Severities, record: TestCaseResolutionStatus) => { render: (value: Severities, record: TestCaseResolutionStatus) => {
if (isPermissionLoading) { if (isPermissionLoading) {
return <Skeleton.Input size="small" />; return <Skeleton.Input size="small" />;

View File

@ -20,7 +20,7 @@ import { NO_DATA_PLACEHOLDER } from '../../../../../constants/constants';
import { PipelineStatus } from '../../../../../generated/entity/services/ingestionPipelines/ingestionPipeline'; import { PipelineStatus } from '../../../../../generated/entity/services/ingestionPipelines/ingestionPipeline';
import { getRunHistoryForPipeline } from '../../../../../rest/ingestionPipelineAPI'; import { getRunHistoryForPipeline } from '../../../../../rest/ingestionPipelineAPI';
import { import {
formatDateTime, formatDateTimeLong,
getCurrentMillis, getCurrentMillis,
getEpochMillisForPastDays, getEpochMillisForPastDays,
} from '../../../../../utils/date-time/DateTimeUtils'; } from '../../../../../utils/date-time/DateTimeUtils';
@ -128,18 +128,19 @@ export const IngestionRecentRuns = ({
{r.timestamp && ( {r.timestamp && (
<p> <p>
{`${t('label.execution-date')}:`}{' '} {`${t('label.execution-date')}:`}{' '}
{formatDateTime(r.timestamp)} {formatDateTimeLong(r.timestamp)}
</p> </p>
)} )}
{r.startDate && ( {r.startDate && (
<p> <p>
{t('label.start-entity', { entity: t('label.date') })}:{' '} {t('label.start-entity', { entity: t('label.date') })}:{' '}
{formatDateTime(r.startDate)} {formatDateTimeLong(r.startDate)}
</p> </p>
)} )}
{r.endDate && ( {r.endDate && (
<p> <p>
{`${t('label.end-date')}:`} {formatDateTime(r.endDate)} {`${t('label.end-date')}:`}{' '}
{formatDateTimeLong(r.endDate)}
</p> </p>
)} )}
</div> </div>

View File

@ -31,7 +31,7 @@ import {
updateActiveChartFilter, updateActiveChartFilter,
} from '../../../utils/ChartUtils'; } from '../../../utils/ChartUtils';
import { CustomTooltip } from '../../../utils/DataInsightUtils'; import { CustomTooltip } from '../../../utils/DataInsightUtils';
import { formatDateTime } from '../../../utils/date-time/DateTimeUtils'; import { formatDateTimeLong } from '../../../utils/date-time/DateTimeUtils';
import ErrorPlaceHolder from '../../common/ErrorWithPlaceholder/ErrorPlaceHolder'; import ErrorPlaceHolder from '../../common/ErrorWithPlaceholder/ErrorPlaceHolder';
import { CustomBarChartProps } from './Chart.interface'; import { CustomBarChartProps } from './Chart.interface';
@ -81,7 +81,7 @@ const CustomBarChart = ({
<Tooltip <Tooltip
content={ content={
<CustomTooltip <CustomTooltip
dateTimeFormatter={formatDateTime} dateTimeFormatter={formatDateTimeLong}
timeStampKey="timestamp" timeStampKey="timestamp"
valueFormatter={(value) => tooltipFormatter(value, tickFormatter)} valueFormatter={(value) => tooltipFormatter(value, tickFormatter)}
/> />

View File

@ -30,7 +30,7 @@ import {
updateActiveChartFilter, updateActiveChartFilter,
} from '../../../utils/ChartUtils'; } from '../../../utils/ChartUtils';
import { CustomTooltip } from '../../../utils/DataInsightUtils'; import { CustomTooltip } from '../../../utils/DataInsightUtils';
import { formatDateTime } from '../../../utils/date-time/DateTimeUtils'; import { formatDateTimeLong } from '../../../utils/date-time/DateTimeUtils';
import ErrorPlaceHolder from '../../common/ErrorWithPlaceholder/ErrorPlaceHolder'; import ErrorPlaceHolder from '../../common/ErrorWithPlaceholder/ErrorPlaceHolder';
import { CustomBarChartProps } from './Chart.interface'; import { CustomBarChartProps } from './Chart.interface';
@ -74,7 +74,7 @@ const OperationDateBarChart = ({
content={ content={
<CustomTooltip <CustomTooltip
customValueKey="data" customValueKey="data"
dateTimeFormatter={formatDateTime} dateTimeFormatter={formatDateTimeLong}
timeStampKey="timestamp" timeStampKey="timestamp"
valueFormatter={(value) => tooltipFormatter(value)} valueFormatter={(value) => tooltipFormatter(value)}
/> />

View File

@ -41,7 +41,7 @@ export interface ChartFilter {
export interface DataInsightChartTooltipProps extends TooltipProps<any, any> { export interface DataInsightChartTooltipProps extends TooltipProps<any, any> {
isPercentage?: boolean; isPercentage?: boolean;
isTier?: boolean; isTier?: boolean;
dateTimeFormatter?: (date?: number) => string; dateTimeFormatter?: (date?: number, format?: string) => string;
valueFormatter?: (value: number | string, key?: string) => string | number; valueFormatter?: (value: number | string, key?: string) => string | number;
timeStampKey?: string; timeStampKey?: string;
transformLabel?: boolean; transformLabel?: boolean;

View File

@ -12,7 +12,10 @@
*/ */
import { JWTTokenExpiry } from '../generated/entity/teams/user'; import { JWTTokenExpiry } from '../generated/entity/teams/user';
import { formatDateTimeLong } from './date-time/DateTimeUtils'; import {
DATE_TIME_WEEKDAY_WITH_ORDINAL,
formatDateTimeLong,
} from './date-time/DateTimeUtils';
export const getJWTTokenExpiryOptions = () => { export const getJWTTokenExpiryOptions = () => {
return Object.keys(JWTTokenExpiry).map((expiry) => { return Object.keys(JWTTokenExpiry).map((expiry) => {
@ -38,7 +41,7 @@ export const getTokenExpiry = (expiry: number) => {
const isTokenExpired = currentTimeStamp >= expiry; const isTokenExpired = currentTimeStamp >= expiry;
return { return {
tokenExpiryDate: formatDateTimeLong(expiry), tokenExpiryDate: formatDateTimeLong(expiry, DATE_TIME_WEEKDAY_WITH_ORDINAL),
isTokenExpired, isTokenExpired,
}; };
}; };

View File

@ -50,7 +50,9 @@ describe('DateTimeUtils tests', () => {
}); });
it(`formatDateShort should formate date and time both`, () => { it(`formatDateShort should formate date and time both`, () => {
expect(formatDateTimeLong(0)).toBe(`Thu 1th January, 1970, 12:00 AM`); expect(formatDateTimeLong(0)).toBe(
`January 01, 1970, 12:00 AM (UTC+00:00)`
);
}); });
it(`formatTimeDurationFromSeconds should formate date and time both`, () => { it(`formatTimeDurationFromSeconds should formate date and time both`, () => {

View File

@ -14,7 +14,8 @@ import { capitalize, isNil, toInteger, toNumber } from 'lodash';
import { DateTime, Duration } from 'luxon'; import { DateTime, Duration } from 'luxon';
export const DATE_TIME_12_HOUR_FORMAT = 'MMM dd, yyyy, hh:mm a'; // e.g. Jan 01, 12:00 AM export const DATE_TIME_12_HOUR_FORMAT = 'MMM dd, yyyy, hh:mm a'; // e.g. Jan 01, 12:00 AM
export const DATE_TIME_WITH_OFFSET_FORMAT = "MMMM dd, yyyy, h:mm a '(UTC'ZZ')'"; // e.g. Jan 01, 12:00 AM (UTC+05:30)
export const DATE_TIME_WEEKDAY_WITH_ORDINAL = "ccc d'th' MMMM, yyyy, hh:mm a"; // e.g. Mon 1st January, 2025, 12:00 AM
/** /**
* @param date EPOCH millis * @param date EPOCH millis
* @returns Formatted date for valid input. Format: MMM DD, YYYY, HH:MM AM/PM * @returns Formatted date for valid input. Format: MMM DD, YYYY, HH:MM AM/PM
@ -49,10 +50,15 @@ export const formatDate = (date?: number, supportUTC = false) => {
* @param date EPOCH millis * @param date EPOCH millis
* @returns Formatted date for valid input. Format: MMM DD, YYYY * @returns Formatted date for valid input. Format: MMM DD, YYYY
*/ */
export const formatDateTimeLong = (timestamp: number, format?: string) => export const formatDateTimeLong = (timestamp?: number, format?: string) => {
DateTime.fromMillis(toNumber(timestamp), { locale: 'en-US' }).toFormat( if (isNil(timestamp)) {
format || "ccc d'th' MMMM, yyyy, hh:mm a" return '';
}
return DateTime.fromMillis(toNumber(timestamp), { locale: 'en-US' }).toFormat(
format || DATE_TIME_WITH_OFFSET_FORMAT
); );
};
/** /**
* *