Skip to content
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

feat: Centralize and Standardize Date/Time Format Handling #6925

Merged
Merged
Show file tree
Hide file tree
Changes from 5 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
21 changes: 11 additions & 10 deletions frontend/src/components/CustomTimePicker/CustomTimePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,16 +113,17 @@ function CustomTimePicker({
selectedTimeValue: string,
): string => {
if (selectedTime === 'custom') {
// Convert the date range string to 12-hour format
const dates = selectedTimeValue.split(' - ');
if (dates.length === 2) {
const startDate = dayjs(dates[0], 'DD/MM/YYYY HH:mm');
const endDate = dayjs(dates[1], 'DD/MM/YYYY HH:mm');

return `${startDate.format('DD/MM/YYYY hh:mm A')} - ${endDate.format(
'DD/MM/YYYY hh:mm A',
)}`;
}
// TODO(shaheer): if the user preference is 12 hour format, then convert the date range string to 12-hour format (pick this up while working on 12/24 hour preference feature)
// // Convert the date range string to 12-hour format
// const dates = selectedTimeValue.split(' - ');
// if (dates.length === 2) {
// const startDate = dayjs(dates[0], DATE_TIME_FORMATS.UK_DATETIME);
// const endDate = dayjs(dates[1], DATE_TIME_FORMATS.UK_DATETIME);

// return `${startDate.format(DATE_TIME_FORMATS.UK_DATETIME)} - ${endDate.format(
// DATE_TIME_FORMATS.UK_DATETIME,
// )}`;
// }
return selectedTimeValue;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import './RangePickerModal.styles.scss';

import { DatePicker } from 'antd';
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
import { DateTimeRangeType } from 'container/TopNav/CustomDateTimeModal';
import { LexicalContext } from 'container/TopNav/DateTimeSelectionV2/config';
import dayjs, { Dayjs } from 'dayjs';
Expand Down Expand Up @@ -68,12 +69,9 @@ function RangePickerModal(props: RangePickerModalProps): JSX.Element {
<RangePicker
disabledDate={disabledDate}
allowClear
showTime={{
use12Hours: true,
format: 'hh:mm A',
}}
showTime
format={(date: Dayjs): string =>
date.tz(timezone.value).format('YYYY-MM-DD hh:mm A')
date.tz(timezone.value).format(DATE_TIME_FORMATS.ISO_DATETIME)
}
onOk={onModalOkHandler}
// eslint-disable-next-line react/jsx-props-no-spreading
Expand Down
17 changes: 9 additions & 8 deletions frontend/src/components/Graph/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
Tooltip,
} from 'chart.js';
import annotationPlugin from 'chartjs-plugin-annotation';
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
import { generateGridTitle } from 'container/GridPanelSwitch/utils';
import dayjs from 'dayjs';
import { useIsDarkMode } from 'hooks/useDarkMode';
Expand Down Expand Up @@ -67,13 +68,13 @@ Tooltip.positioners.custom = TooltipPositionHandler;

// Map of Chart.js time formats to dayjs format strings
const formatMap = {
'HH:mm:ss': 'HH:mm:ss',
'HH:mm': 'HH:mm',
'MM/DD HH:mm': 'MM/DD HH:mm',
'MM/dd HH:mm': 'MM/DD HH:mm',
'MM/DD': 'MM/DD',
'YY-MM': 'YY-MM',
YY: 'YY',
'HH:mm:ss': DATE_TIME_FORMATS.TIME_SECONDS,
'HH:mm': DATE_TIME_FORMATS.TIME,
'MM/DD HH:mm': DATE_TIME_FORMATS.SLASH_SHORT,
'MM/dd HH:mm': DATE_TIME_FORMATS.SLASH_SHORT,
'MM/DD': DATE_TIME_FORMATS.DATE_SHORT,
'YY-MM': DATE_TIME_FORMATS.YEAR_MONTH,
YY: DATE_TIME_FORMATS.YEAR_SHORT,
};

const Graph = forwardRef<ToggleGraphProps | undefined, GraphProps>(
Expand Down Expand Up @@ -136,7 +137,7 @@ const Graph = forwardRef<ToggleGraphProps | undefined, GraphProps>(
const format = formatMap[fmt as keyof typeof formatMap];
if (!format) {
console.warn(`Missing datetime format for ${fmt}`);
return dayjsTime.format('YYYY-MM-DD HH:mm:ss'); // fallback format
return dayjsTime.format(DATE_TIME_FORMATS.ISO_DATETIME_SECONDS); // fallback format
}

return dayjsTime.format(format);
Expand Down
21 changes: 12 additions & 9 deletions frontend/src/components/Graph/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Chart, ChartConfiguration, ChartData, Color } from 'chart.js';
import * as chartjsAdapter from 'chartjs-adapter-date-fns';
import { Timezone } from 'components/CustomTimePicker/timezoneUtils';
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
import dayjs from 'dayjs';
import { MutableRefObject } from 'react';

Expand Down Expand Up @@ -99,7 +100,9 @@ export const getGraphOptions = (
callbacks: {
title(context): string | string[] {
const date = dayjs(context[0].parsed.x);
return date.tz(timezone.value).format('MMM DD, YYYY, HH:mm:ss');
return date
.tz(timezone.value)
.format(DATE_TIME_FORMATS.MONTH_DATETIME_FULL_SECONDS);
},
label(context): string | string[] {
let label = context.dataset.label || '';
Expand Down Expand Up @@ -154,14 +157,14 @@ export const getGraphOptions = (
unit: xAxisTimeUnit?.unitName || 'minute',
stepSize: xAxisTimeUnit?.stepSize || 1,
displayFormats: {
millisecond: 'HH:mm:ss',
second: 'HH:mm:ss',
minute: 'HH:mm',
hour: 'MM/dd HH:mm',
day: 'MM/dd',
week: 'MM/dd',
month: 'yy-MM',
year: 'yy',
millisecond: DATE_TIME_FORMATS.TIME_SECONDS,
second: DATE_TIME_FORMATS.TIME_SECONDS,
minute: DATE_TIME_FORMATS.TIME,
hour: DATE_TIME_FORMATS.SLASH_SHORT,
day: DATE_TIME_FORMATS.DATE_SHORT,
week: DATE_TIME_FORMATS.DATE_SHORT,
month: DATE_TIME_FORMATS.YEAR_MONTH,
year: DATE_TIME_FORMATS.YEAR_SHORT,
},
},
type: 'time',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Tag, Typography } from 'antd';
import { ColumnsType } from 'antd/es/table';
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
import { getMs } from 'container/Trace/Filters/Panel/PanelBody/Duration/util';
import {
BlockLink,
Expand Down Expand Up @@ -33,8 +34,8 @@ export const getListColumns = (
if (key === 'timestamp') {
const date =
typeof value === 'string'
? dayjs(value).format('YYYY-MM-DD HH:mm:ss.SSS')
: dayjs(value / 1e6).format('YYYY-MM-DD HH:mm:ss.SSS');
? dayjs(value).format(DATE_TIME_FORMATS.ISO_DATETIME_MS)
: dayjs(value / 1e6).format(DATE_TIME_FORMATS.ISO_DATETIME_MS);

return (
<BlockLink to={getTraceLink(item)} openInNewTab>
Expand Down
5 changes: 3 additions & 2 deletions frontend/src/components/Logs/ListLogView/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Typography } from 'antd';
import cx from 'classnames';
import LogDetail from 'components/LogDetail';
import { VIEW_TYPES } from 'components/LogDetail/constants';
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
import { unescapeString } from 'container/LogDetailedView/utils';
import { FontSize } from 'container/OptionsMenu/types';
import dompurify from 'dompurify';
Expand Down Expand Up @@ -181,11 +182,11 @@ function ListLogView({
typeof flattenLogData.timestamp === 'string'
? formatTimezoneAdjustedTimestamp(
flattenLogData.timestamp,
'YYYY-MM-DD HH:mm:ss.SSS',
DATE_TIME_FORMATS.ISO_DATETIME_MS,
)
: formatTimezoneAdjustedTimestamp(
flattenLogData.timestamp / 1e6,
'YYYY-MM-DD HH:mm:ss.SSS',
DATE_TIME_FORMATS.ISO_DATETIME_MS,
),
[flattenLogData.timestamp, formatTimezoneAdjustedTimestamp],
);
Expand Down
8 changes: 6 additions & 2 deletions frontend/src/components/Logs/RawLogView/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Convert from 'ansi-to-html';
import { DrawerProps } from 'antd';
import LogDetail from 'components/LogDetail';
import { VIEW_TYPES, VIEWS } from 'components/LogDetail/constants';
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
import { unescapeString } from 'container/LogDetailedView/utils';
import LogsExplorerContext from 'container/LogsExplorerContext';
import dompurify from 'dompurify';
Expand Down Expand Up @@ -94,10 +95,13 @@ function RawLogView({
const text = useMemo(() => {
const date =
typeof data.timestamp === 'string'
? formatTimezoneAdjustedTimestamp(data.timestamp, 'YYYY-MM-DD HH:mm:ss.SSS')
? formatTimezoneAdjustedTimestamp(
data.timestamp,
DATE_TIME_FORMATS.ISO_DATETIME_MS,
)
: formatTimezoneAdjustedTimestamp(
data.timestamp / 1e6,
'YYYY-MM-DD HH:mm:ss.SSS',
DATE_TIME_FORMATS.ISO_DATETIME_MS,
);

return `${date} | ${attributesText} ${data.body}`;
Expand Down
8 changes: 6 additions & 2 deletions frontend/src/components/Logs/TableView/useTableView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Convert from 'ansi-to-html';
import { Typography } from 'antd';
import { ColumnsType } from 'antd/es/table';
import cx from 'classnames';
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
import { unescapeString } from 'container/LogDetailedView/utils';
import dompurify from 'dompurify';
import { useIsDarkMode } from 'hooks/useDarkMode';
Expand Down Expand Up @@ -99,10 +100,13 @@ export const useTableView = (props: UseTableViewProps): UseTableViewResult => {
render: (field): ColumnTypeRender<Record<string, unknown>> => {
const date =
typeof field === 'string'
? formatTimezoneAdjustedTimestamp(field, 'YYYY-MM-DD HH:mm:ss.SSS')
? formatTimezoneAdjustedTimestamp(
field,
DATE_TIME_FORMATS.ISO_DATETIME_MS,
)
: formatTimezoneAdjustedTimestamp(
field / 1e6,
'YYYY-MM-DD HH:mm:ss.SSS',
DATE_TIME_FORMATS.ISO_DATETIME_MS,
);
return {
children: (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { Typography } from 'antd';
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
import { useTimezone } from 'providers/Timezone';

function Time({ CreatedOrUpdateTime }: DateProps): JSX.Element {
const { formatTimezoneAdjustedTimestamp } = useTimezone();
const time = new Date(CreatedOrUpdateTime);
const timeString = formatTimezoneAdjustedTimestamp(
time,
'MM/DD/YYYY hh:mm:ss A (UTC Z)',
DATE_TIME_FORMATS.UTC_US,
);
return <Typography>{timeString}</Typography>;
}
Expand Down
68 changes: 68 additions & 0 deletions frontend/src/constants/dateTimeFormats.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
export const DATE_TIME_FORMATS = {
// ISO formats (YYYY-MM-DD)
ISO_DATETIME: 'YYYY-MM-DD HH:mm',
ISO_DATETIME_SECONDS: 'YYYY-MM-DD HH:mm:ss',
ISO_DATETIME_MS: 'YYYY-MM-DD HH:mm:ss.SSS',
ISO_DATETIME_UTC: 'YYYY-MM-DD HH:mm:ss (UTC Z)',

// Standard regional formats
US_DATE: 'MM/DD/YYYY',
UK_DATETIME: 'DD/MM/YYYY HH:mm',
UK_DATETIME_SECONDS: 'DD/MM/YYYY HH:mm:ss',

// US_DATE: 'MM/DD/YYYY',
US_DATETIME: 'MM/DD/YYYY, HH:mm',
US_DATETIME_SECONDS: 'MM/DD/YYYY, HH:mm:ss',

// Slash formats
SLASH_DATETIME: 'YYYY/MM/DD HH:mm',
SLASH_DATETIME_SECONDS: 'YYYY/MM/DD HH:mm:ss',
SLASH_SHORT: 'MM/DD HH:mm',

// Time only formats
TIME: 'HH:mm',
TIME_SECONDS: 'HH:mm:ss',
TIME_UTC: 'HH:mm:ss (UTC Z)',
TIME_UTC_MS: 'HH:mm:ss.SSS (UTC Z)',

// Short date formats
DATE_SHORT: 'MM/DD',
YEAR_SHORT: 'YY',
YEAR_MONTH: 'YY-MM',

// Month name formats
MONTH_DATE_FULL: 'MMMM DD, YYYY',
MONTH_DATE_SHORT: 'DD MMM YYYY',
MONTH_DATETIME_SHORT: 'DD MMM YYYY HH:mm',
MONTH_YEAR: 'MMM DD YYYY',
MONTH_DATETIME: 'MMM DD, YYYY, HH:mm',
MONTH_DATETIME_SECONDS: 'MMM DD YYYY HH:mm:ss',
MONTH_DATETIME_FULL: 'MMMM DD, YYYY HH:mm',
MONTH_DATETIME_FULL_SECONDS: 'MMM DD, YYYY, HH:mm:ss',

// Ordinal formats (1st, 2nd, 3rd, etc)
ORDINAL_DATE: 'Do MMM YYYY',
ORDINAL_ONLY: 'Do',
ORDINAL_DATETIME: 'Do MMMM, YYYY ⎯ HH:mm:ss',

// UTC specific formats
UTC_FULL: 'DD/MM/YYYY HH:mm:ss (UTC Z)',
UTC_US: 'MM/DD/YYYY HH:mm:ss (UTC Z)',
UTC_US_MS: 'MM/DD/YYYY, HH:mm:ss.SSS (UTC Z)',
UTC_MONTH_FULL: 'MMMM DD, YYYY HH:mm (UTC Z)',
UTC_MONTH_SHORT: 'MMM DD HH:mm:ss.SSS (UTC Z)',
UTC_MONTH_COMPACT: 'MMM DD,YYYY, HH:mm (UTC Z)',
UTC_TIME_DATE: 'HH:mm:ss (UTC Z) MM/DD',

// Formats with dash separator
DASH_DATETIME: 'MMM D, YYYY ⎯ HH:mm:ss',
DASH_DATETIME_UTC: 'MMM D, YYYY ⎯ HH:mm:ss (UTC Z)',
DASH_TIME_DATE: 'HH:mm:ss ⎯ MMM D, YYYY (UTC Z)',
} as const;

export const convert24hTo12h = (format: string): string =>
format
.replace(/\bHH:mm:ss\.SSS\b/g, 'hh:mm:ss.SSS A')
.replace(/\bHH:mm:ss\b/g, 'hh:mm:ss A')
.replace(/\bHH:mm\b/g, 'hh:mm A')
.replace(/\bh:mm:ss\b/g, 'h:mm:ss A');
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import useUrlQuery from 'hooks/useUrlQuery';
import history from 'lib/history';
import heatmapPlugin from 'lib/uPlotLib/plugins/heatmapPlugin';
import timelinePlugin from 'lib/uPlotLib/plugins/timelinePlugin';
import { uPlotXAxisValuesFormat } from 'lib/uPlotLib/utils/constants';
import { useTimezone } from 'providers/Timezone';
import { useMemo, useRef } from 'react';
import { useDispatch } from 'react-redux';
Expand Down Expand Up @@ -60,6 +61,7 @@ function HorizontalTimelineGraph({
{
gap: 10,
stroke: isDarkMode ? Color.BG_VANILLA_400 : Color.BG_INK_400,
values: uPlotXAxisValuesFormat,
},
{ show: false },
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { ColumnsType } from 'antd/es/table';
import ClientSideQBSearch, {
AttributeKey,
} from 'components/ClientSideQBSearch/ClientSideQBSearch';
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
import { ConditionalAlertPopover } from 'container/AlertHistory/AlertPopover/AlertPopover';
import { transformKeyValuesToAttributeValuesMap } from 'container/QueryBuilder/filters/utils';
import { useIsDarkMode } from 'hooks/useDarkMode';
Expand Down Expand Up @@ -112,7 +113,7 @@ export const timelineTableColumns = ({
width: 200,
render: (value): JSX.Element => (
<div className="alert-rule__created-at">
{formatTimezoneAdjustedTimestamp(value, 'MMM D, YYYY ⎯ HH:mm:ss')}
{formatTimezoneAdjustedTimestamp(value, DATE_TIME_FORMATS.DASH_DATETIME)}
</div>
),
},
Expand Down
6 changes: 5 additions & 1 deletion frontend/src/container/AllError/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import logEvent from 'api/common/logEvent';
import getAll from 'api/errors/getAll';
import getErrorCounts from 'api/errors/getErrorCounts';
import { ResizeTable } from 'components/ResizeTable';
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
import ROUTES from 'constants/routes';
import { useNotifications } from 'hooks/useNotifications';
import useResourceAttribute from 'hooks/useResourceAttribute';
Expand Down Expand Up @@ -164,7 +165,10 @@ function AllErrors(): JSX.Element {
) => string,
): JSX.Element => (
<Typography>
{formatTimezoneAdjustedTimestamp(value, 'DD/MM/YYYY hh:mm:ss A')}
{formatTimezoneAdjustedTimestamp(
value,
DATE_TIME_FORMATS.UK_DATETIME_SECONDS,
)}
</Typography>
);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
import { themeColors } from 'constants/theme';
import dayjs from 'dayjs';
import { generateColor } from 'lib/uPlotLib/utils/generateColor';
Expand All @@ -19,7 +20,9 @@ const tooltipPlugin = (
return value.toFixed(3);
}
if (value instanceof Date) {
return dayjs(value).tz(timezone).format('MM/DD/YYYY, h:mm:ss A');
return dayjs(value)
.tz(timezone)
.format(DATE_TIME_FORMATS.US_DATETIME_SECONDS);
}
if (value == null) {
return 'N/A';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
import dayjs from 'dayjs';

export interface QuantityData {
Expand Down Expand Up @@ -36,7 +37,7 @@ interface CsvData {
}

const formatDate = (timestamp: number): string =>
dayjs.unix(timestamp).format('MM/DD/YYYY');
dayjs.unix(timestamp).format(DATE_TIME_FORMATS.US_DATE);

const getQuantityData = (
data: QuantityData[],
Expand Down
Loading
Loading