[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:
Walter Rafelsberger 2018-12-17 10:12:41 +01:00 committed by GitHub
parent 19b9da5dca
commit 55aafcec59
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 91 additions and 31 deletions

View file

@ -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);
}
}

View file

@ -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',

View file

@ -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.

View file

@ -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')) {

View file

@ -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')) {

View file

@ -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) {

View file

@ -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/>`;

View file

@ -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) {

View file

@ -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',

View file

@ -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
};

View file

@ -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')) {

View 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');
});
});

View 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');
}