mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[ML] Consolidate date formatting. (#27205)
* [ML] Consolidate date formatting. * [ML] Adds unit tests. * [ML] Fixes tz issue in date_utils test.
This commit is contained in:
parent
19b9da5dca
commit
55aafcec59
13 changed files with 91 additions and 31 deletions
|
@ -25,7 +25,11 @@ import {
|
|||
EuiText,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import { formatDate } from '@elastic/eui/lib/services/format';
|
||||
import {
|
||||
formatHumanReadableDate,
|
||||
formatHumanReadableDateTime,
|
||||
formatHumanReadableDateTimeSeconds
|
||||
} from '../../util/date_utils';
|
||||
|
||||
import { DescriptionCell } from './description_cell';
|
||||
import { DetectorCell } from './detector_cell';
|
||||
|
@ -47,11 +51,11 @@ const INFLUENCERS_LIMIT = 5; // Maximum number of influencers to display befo
|
|||
|
||||
function renderTime(date, aggregationInterval) {
|
||||
if (aggregationInterval === 'hour') {
|
||||
return formatDate(date, 'MMMM Do YYYY, HH:mm');
|
||||
return formatHumanReadableDateTime(date);
|
||||
} else if (aggregationInterval === 'second') {
|
||||
return formatDate(date, 'MMMM Do YYYY, HH:mm:ss');
|
||||
return formatHumanReadableDateTimeSeconds(date);
|
||||
} else {
|
||||
return formatDate(date, 'MMMM Do YYYY');
|
||||
return formatHumanReadableDate(date);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ import {
|
|||
EuiSpacer,
|
||||
EuiText
|
||||
} from '@elastic/eui';
|
||||
import { formatDate } from '@elastic/eui/lib/services/format';
|
||||
import { formatHumanReadableDateTimeSeconds } from '../../util/date_utils';
|
||||
|
||||
import { EntityCell } from './entity_cell';
|
||||
import {
|
||||
|
@ -105,10 +105,10 @@ function getDetailsItems(anomaly, examples, filter) {
|
|||
}
|
||||
|
||||
const anomalyTime = source[TIME_FIELD_NAME];
|
||||
let timeDesc = `${formatDate(anomalyTime, 'MMMM Do YYYY, HH:mm:ss')}`;
|
||||
let timeDesc = `${formatHumanReadableDateTimeSeconds(anomalyTime)}`;
|
||||
if (source.bucket_span !== undefined) {
|
||||
const anomalyEndTime = anomalyTime + (source.bucket_span * 1000);
|
||||
timeDesc += ` to ${formatDate(anomalyEndTime, 'MMMM Do YYYY, HH:mm:ss')}`;
|
||||
timeDesc += ` to ${formatHumanReadableDateTimeSeconds(anomalyEndTime)}`;
|
||||
}
|
||||
items.push({
|
||||
title: 'time',
|
||||
|
|
|
@ -20,6 +20,7 @@ import { numTicksForDateFormat } from '../../util/chart_utils';
|
|||
import { calculateTextWidth } from '../../util/string_utils';
|
||||
import { IntervalHelperProvider } from '../../util/ml_time_buckets';
|
||||
import { mlChartTooltipService } from '../../components/chart_tooltip/chart_tooltip_service';
|
||||
import { formatHumanReadableDateTime } from '../../util/date_utils';
|
||||
|
||||
import { uiModules } from 'ui/modules';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
@ -155,7 +156,7 @@ module.directive('mlDocumentCountChart', function (Private) {
|
|||
.on('mouseout', () => mlChartTooltipService.hide());
|
||||
|
||||
function showChartTooltip(data, rect) {
|
||||
const formattedDate = moment(data.time).format('MMMM Do YYYY, HH:mm');
|
||||
const formattedDate = formatHumanReadableDateTime(data.time);
|
||||
const contents = `${formattedDate}<br/><hr/>count: ${data.value}`;
|
||||
|
||||
// Calculate the y offset.
|
||||
|
|
|
@ -19,6 +19,7 @@ import moment from 'moment';
|
|||
|
||||
// don't use something like plugins/ml/../common
|
||||
// because it won't work with the jest tests
|
||||
import { formatHumanReadableDateTime } from '../../util/date_utils';
|
||||
import { formatValue } from '../../formatters/format_value';
|
||||
import { getSeverityWithLow } from '../../../common/util/anomaly_utils';
|
||||
import {
|
||||
|
@ -403,7 +404,7 @@ export class ExplorerChartDistribution extends React.Component {
|
|||
function showLineChartTooltip(marker, circle) {
|
||||
// Show the time and metric values in the tooltip.
|
||||
// Uses date, value, upper, lower and anomalyScore (optional) marker properties.
|
||||
const formattedDate = moment(marker.date).format('MMMM Do YYYY, HH:mm');
|
||||
const formattedDate = formatHumanReadableDateTime(marker.date);
|
||||
let contents = `${formattedDate}<br/><hr/>`;
|
||||
|
||||
if (_.has(marker, 'entity')) {
|
||||
|
|
|
@ -19,6 +19,7 @@ import moment from 'moment';
|
|||
|
||||
// don't use something like plugins/ml/../common
|
||||
// because it won't work with the jest tests
|
||||
import { formatHumanReadableDateTime } from '../../util/date_utils';
|
||||
import { formatValue } from '../../formatters/format_value';
|
||||
import {
|
||||
getSeverityWithLow,
|
||||
|
@ -334,7 +335,7 @@ export class ExplorerChartSingleMetric extends React.Component {
|
|||
function showLineChartTooltip(marker, circle) {
|
||||
// Show the time and metric values in the tooltip.
|
||||
// Uses date, value, upper, lower and anomalyScore (optional) marker properties.
|
||||
const formattedDate = moment(marker.date).format('MMMM Do YYYY, HH:mm');
|
||||
const formattedDate = formatHumanReadableDateTime(marker.date);
|
||||
let contents = formattedDate + '<br/><hr/>';
|
||||
|
||||
if (_.has(marker, 'anomalyScore')) {
|
||||
|
|
|
@ -43,6 +43,7 @@ import { mlFieldFormatService } from 'plugins/ml/services/field_format_service';
|
|||
import { JobSelectServiceProvider } from 'plugins/ml/components/job_select_list/job_select_service';
|
||||
import { isTimeSeriesViewDetector } from 'plugins/ml/../common/util/job_utils';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
import { formatHumanReadableDateTime } from '../util/date_utils';
|
||||
import {
|
||||
DRAG_SELECT_ACTION,
|
||||
SWIMLANE_DEFAULT_LIMIT,
|
||||
|
@ -1074,7 +1075,7 @@ module.controller('MlExplorerController', function (
|
|||
// Click is in one of the cells in the Overall swimlane - reload the 'view by' swimlane
|
||||
// to show the top 'view by' values for the selected time.
|
||||
loadViewBySwimlaneForSelectedTime(timerange.earliestMs, timerange.latestMs);
|
||||
$scope.viewByLoadedForTimeFormatted = moment(timerange.earliestMs).format('MMMM Do YYYY, HH:mm');
|
||||
$scope.viewByLoadedForTimeFormatted = formatHumanReadableDateTime(timerange.earliestMs);
|
||||
}
|
||||
|
||||
if (influencers.length === 0) {
|
||||
|
|
|
@ -19,6 +19,7 @@ import moment from 'moment';
|
|||
|
||||
// don't use something like plugins/ml/../common
|
||||
// because it won't work with the jest tests
|
||||
import { formatHumanReadableDateTime } from '../util/date_utils';
|
||||
import { numTicksForDateFormat } from '../util/chart_utils';
|
||||
import { getSeverityColor } from '../../common/util/anomaly_utils';
|
||||
import { mlEscape } from '../util/string_utils';
|
||||
|
@ -274,7 +275,7 @@ export class ExplorerSwimlane extends React.Component {
|
|||
const displayScore = (bucketScore > 1 ? parseInt(bucketScore) : '< 1');
|
||||
|
||||
// Display date using same format as Kibana visualizations.
|
||||
const formattedDate = moment(time * 1000).format('MMMM Do YYYY, HH:mm');
|
||||
const formattedDate = formatHumanReadableDateTime(time * 1000);
|
||||
let contents = `${formattedDate}<br/><hr/>`;
|
||||
if (swimlaneData.fieldName !== undefined) {
|
||||
contents += `${mlEscape(swimlaneData.fieldName)}: ${mlEscape(laneLabel)}<br/><hr/>`;
|
||||
|
|
|
@ -15,6 +15,7 @@ import d3 from 'd3';
|
|||
import angular from 'angular';
|
||||
import moment from 'moment';
|
||||
|
||||
import { formatHumanReadableDateTime } from '../../../../../util/date_utils';
|
||||
import { TimeBuckets } from 'ui/time_buckets';
|
||||
import { numTicksForDateFormat } from '../../../../../util/chart_utils';
|
||||
import { mlEscape } from '../../../../../util/string_utils';
|
||||
|
@ -229,7 +230,7 @@ module.directive('mlPopulationJobChart', function () {
|
|||
function showTooltip(data, el) {
|
||||
scope;
|
||||
let contents = '';
|
||||
const formattedDate = moment(data.date).format('MMMM Do YYYY, HH:mm');
|
||||
const formattedDate = formatHumanReadableDateTime(data.date);
|
||||
contents += `${formattedDate}<br/><hr/>`;
|
||||
contents += `${mlEscape(scope.overFieldName)}: ${mlEscape(data.label)}<br/>`;
|
||||
if (scope.chartData.fieldFormat !== undefined) {
|
||||
|
|
|
@ -13,18 +13,13 @@ import React from 'react';
|
|||
|
||||
import { EuiDescriptionList } from '@elastic/eui';
|
||||
|
||||
// @ts-ignore
|
||||
import { formatDate } from '@elastic/eui/lib/services/format';
|
||||
import { Annotation } from '../../../../common/types/annotations';
|
||||
import { formatHumanReadableDateTimeSeconds } from '../../../util/date_utils';
|
||||
|
||||
interface Props {
|
||||
annotation: Annotation;
|
||||
}
|
||||
|
||||
function formatListDate(ts: number) {
|
||||
return formatDate(ts, 'MMMM Do YYYY, HH:mm:ss');
|
||||
}
|
||||
|
||||
export const AnnotationDescriptionList: React.SFC<Props> = ({ annotation }) => {
|
||||
const listItems = [
|
||||
{
|
||||
|
@ -33,21 +28,21 @@ export const AnnotationDescriptionList: React.SFC<Props> = ({ annotation }) => {
|
|||
},
|
||||
{
|
||||
title: 'Start',
|
||||
description: formatListDate(annotation.timestamp),
|
||||
description: formatHumanReadableDateTimeSeconds(annotation.timestamp),
|
||||
},
|
||||
];
|
||||
|
||||
if (annotation.end_timestamp !== undefined) {
|
||||
listItems.push({
|
||||
title: 'End',
|
||||
description: formatListDate(annotation.end_timestamp),
|
||||
description: formatHumanReadableDateTimeSeconds(annotation.end_timestamp),
|
||||
});
|
||||
}
|
||||
|
||||
if (annotation.create_time !== undefined && annotation.modified_time !== undefined) {
|
||||
listItems.push({
|
||||
title: 'Created',
|
||||
description: formatListDate(annotation.create_time),
|
||||
description: formatHumanReadableDateTimeSeconds(annotation.create_time),
|
||||
});
|
||||
listItems.push({
|
||||
title: 'Created by',
|
||||
|
@ -55,7 +50,7 @@ export const AnnotationDescriptionList: React.SFC<Props> = ({ annotation }) => {
|
|||
});
|
||||
listItems.push({
|
||||
title: 'Last modified',
|
||||
description: formatListDate(annotation.modified_time),
|
||||
description: formatHumanReadableDateTimeSeconds(annotation.modified_time),
|
||||
});
|
||||
listItems.push({
|
||||
title: 'Modified by',
|
||||
|
|
|
@ -21,9 +21,7 @@ import {
|
|||
EuiToolTip
|
||||
} from '@elastic/eui';
|
||||
|
||||
import { formatDate } from '@elastic/eui/lib/services/format';
|
||||
|
||||
const TIME_FORMAT = 'MMMM Do YYYY, HH:mm:ss';
|
||||
import { formatHumanReadableDateTimeSeconds } from '../../../util/date_utils';
|
||||
|
||||
function getColumns(viewForecast) {
|
||||
return [
|
||||
|
@ -31,21 +29,21 @@ function getColumns(viewForecast) {
|
|||
field: 'forecast_create_timestamp',
|
||||
name: 'Created',
|
||||
dataType: 'date',
|
||||
render: (date) => formatDate(date, TIME_FORMAT),
|
||||
render: (date) => formatHumanReadableDateTimeSeconds(date),
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
field: 'forecast_start_timestamp',
|
||||
name: 'From',
|
||||
dataType: 'date',
|
||||
render: (date) => formatDate(date, TIME_FORMAT),
|
||||
render: (date) => formatHumanReadableDateTimeSeconds(date),
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
field: 'forecast_end_timestamp',
|
||||
name: 'To',
|
||||
dataType: 'date',
|
||||
render: (date) => formatDate(date, TIME_FORMAT),
|
||||
render: (date) => formatHumanReadableDateTimeSeconds(date),
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
|
@ -95,4 +93,3 @@ ForecastsList.propType = {
|
|||
forecasts: PropTypes.array,
|
||||
viewForecast: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ import {
|
|||
showMultiBucketAnomalyMarker,
|
||||
showMultiBucketAnomalyTooltip,
|
||||
} from '../../../util/chart_utils';
|
||||
import { formatHumanReadableDateTimeSeconds } from '../../../util/date_utils';
|
||||
import { TimeBuckets } from 'ui/time_buckets';
|
||||
import { mlTableService } from '../../../services/table_service';
|
||||
import { ContextChartMask } from '../context_chart_mask';
|
||||
|
@ -1265,7 +1266,7 @@ export class TimeseriesChart extends React.Component {
|
|||
|
||||
// Show the time and metric values in the tooltip.
|
||||
// Uses date, value, upper, lower and anomalyScore (optional) marker properties.
|
||||
const formattedDate = moment(marker.date).format('MMMM Do YYYY, HH:mm');
|
||||
const formattedDate = formatHumanReadableDateTimeSeconds(marker.date);
|
||||
let contents = formattedDate + '<br/><hr/>';
|
||||
|
||||
if (_.has(marker, 'anomalyScore')) {
|
||||
|
|
35
x-pack/plugins/ml/public/util/date_utils.test.ts
Normal file
35
x-pack/plugins/ml/public/util/date_utils.test.ts
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import moment from 'moment-timezone';
|
||||
|
||||
import {
|
||||
formatHumanReadableDate,
|
||||
formatHumanReadableDateTime,
|
||||
formatHumanReadableDateTimeSeconds,
|
||||
} from './date_utils';
|
||||
|
||||
describe('formatHumanReadableDate', () => {
|
||||
beforeEach(() => {
|
||||
moment.tz.setDefault('UTC');
|
||||
});
|
||||
afterEach(() => {
|
||||
moment.tz.setDefault('Browser');
|
||||
});
|
||||
|
||||
test('formatHumanReadableDate', () => {
|
||||
const formattedDate = formatHumanReadableDate(0);
|
||||
expect(formattedDate).toBe('January 1st 1970');
|
||||
});
|
||||
test('formatHumanReadableDateTime', () => {
|
||||
const formattedDate = formatHumanReadableDateTime(0);
|
||||
expect(formattedDate).toBe('January 1st 1970, 00:00');
|
||||
});
|
||||
test('formatHumanReadableDateTimeSeconds', () => {
|
||||
const formattedDate = formatHumanReadableDateTimeSeconds(0);
|
||||
expect(formattedDate).toBe('January 1st 1970, 00:00:00');
|
||||
});
|
||||
});
|
22
x-pack/plugins/ml/public/util/date_utils.ts
Normal file
22
x-pack/plugins/ml/public/util/date_utils.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
// utility functions for handling dates
|
||||
|
||||
// @ts-ignore
|
||||
import { formatDate } from '@elastic/eui/lib/services/format';
|
||||
|
||||
export function formatHumanReadableDate(ts: number) {
|
||||
return formatDate(ts, 'MMMM Do YYYY');
|
||||
}
|
||||
|
||||
export function formatHumanReadableDateTime(ts: number) {
|
||||
return formatDate(ts, 'MMMM Do YYYY, HH:mm');
|
||||
}
|
||||
|
||||
export function formatHumanReadableDateTimeSeconds(ts: number) {
|
||||
return formatDate(ts, 'MMMM Do YYYY, HH:mm:ss');
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue