mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
TimeSeries: fix & optimize time string parsing (#64640)
This commit is contained in:
parent
8bb10f87e9
commit
855de98133
@ -18,6 +18,9 @@ export const fieldIndexComparer = (field: Field, reverse = false): IndexComparer
|
||||
case FieldType.boolean:
|
||||
return booleanIndexComparer(values, reverse);
|
||||
case FieldType.time:
|
||||
if (typeof field.values.get(0) === 'number') {
|
||||
return timestampIndexComparer(values, reverse);
|
||||
}
|
||||
return timeIndexComparer(values, reverse);
|
||||
default:
|
||||
return naturalIndexComparer(reverse);
|
||||
@ -76,6 +79,12 @@ const falsyComparer = (a: unknown, b: unknown): number => {
|
||||
return 0;
|
||||
};
|
||||
|
||||
const timestampIndexComparer = (values: Vector<number>, reverse: boolean): IndexComparer => {
|
||||
let vals = values.toArray();
|
||||
let mult = reverse ? -1 : 1;
|
||||
return (a: number, b: number): number => mult * (vals[a] - vals[b]);
|
||||
};
|
||||
|
||||
const timeIndexComparer = (values: Vector<unknown>, reverse: boolean): IndexComparer => {
|
||||
return (a: number, b: number): number => {
|
||||
const vA = values.get(a);
|
||||
|
@ -391,12 +391,12 @@ describe('fieldToTimeField', () => {
|
||||
// this needs to run in a non-UTC timezone env to ensure the parsing is not dependent on env tz settings
|
||||
//process.env.TZ = 'Pacific/Easter';
|
||||
|
||||
it('should always parse ISO 8601 date strings in UTC timezone (e.g. 2011-10-05T14:48:00.000Z)', () => {
|
||||
it('should properly parse ISO 8601 date strings in UTC offset timezone', () => {
|
||||
const stringTimeField: Field = {
|
||||
config: {},
|
||||
name: 'ISO 8601 date strings',
|
||||
type: FieldType.time,
|
||||
values: new ArrayVector(['2021-11-11T19:45:00.000Z']),
|
||||
values: new ArrayVector(['2021-11-11T19:45:00Z']),
|
||||
};
|
||||
|
||||
expect(fieldToTimeField(stringTimeField)).toEqual({
|
||||
@ -406,4 +406,32 @@ describe('fieldToTimeField', () => {
|
||||
values: new ArrayVector([1636659900000]),
|
||||
});
|
||||
});
|
||||
|
||||
it('should properly parse additional ISO 8601 date strings with tz offsets and millis', () => {
|
||||
const stringTimeField: Field = {
|
||||
config: {},
|
||||
name: 'ISO 8601 date strings',
|
||||
type: FieldType.time,
|
||||
values: new ArrayVector([
|
||||
'2021-11-11T19:45:00+05:30',
|
||||
'2021-11-11T19:45:00-05:30',
|
||||
'2021-11-11T19:45:00+0530',
|
||||
'2021-11-11T19:45:00-0530',
|
||||
'2021-11-11T19:45:00.0000000000+05:30',
|
||||
'2021-11-11T19:45:00.0000000000-0530',
|
||||
'2021-11-11T19:45:00.000Z',
|
||||
'2021-11-11T19:45:00.0000000000Z',
|
||||
]),
|
||||
};
|
||||
|
||||
expect(fieldToTimeField(stringTimeField)).toEqual({
|
||||
config: {},
|
||||
name: 'ISO 8601 date strings',
|
||||
type: FieldType.time,
|
||||
values: new ArrayVector([
|
||||
1636640100000, 1636679700000, 1636640100000, 1636679700000, 1636640100000, 1636679700000, 1636659900000,
|
||||
1636659900000,
|
||||
]),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -109,8 +109,8 @@ export function convertFieldType(field: Field, opts: ConvertFieldTypeOptions): F
|
||||
}
|
||||
}
|
||||
|
||||
// matches ISO 8601, e.g. 2021-11-11T19:45:00.000Z (float portion optional)
|
||||
const iso8601Regex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/;
|
||||
// matches common ISO 8601 (see tests)
|
||||
const iso8601Regex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3,})?(?:Z|[-+]\d{2}:?\d{2})$/;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
|
@ -12,6 +12,7 @@ import {
|
||||
SortedVector,
|
||||
TimeRange,
|
||||
} from '@grafana/data';
|
||||
import { convertFieldType } from '@grafana/data/src/transformations/transformers/convertFieldType';
|
||||
import { GraphFieldConfig, LineInterpolation } from '@grafana/schema';
|
||||
import { applyNullInsertThreshold } from '@grafana/ui/src/components/GraphNG/nullInsertThreshold';
|
||||
import { nullToValue } from '@grafana/ui/src/components/GraphNG/nullToValue';
|
||||
@ -29,6 +30,17 @@ export function prepareGraphableFields(
|
||||
return null;
|
||||
}
|
||||
|
||||
// some datasources simply tag the field as time, but don't convert to milli epochs
|
||||
// so we're stuck with doing the parsing here to avoid Moment slowness everywhere later
|
||||
// this mutates (once)
|
||||
for (let frame of series) {
|
||||
for (let field of frame.fields) {
|
||||
if (field.type === FieldType.time && typeof field.values.get(0) !== 'number') {
|
||||
field.values = convertFieldType(field, { destinationType: FieldType.time }).values;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (series.every((df) => df.meta?.type === DataFrameType.TimeSeriesLong)) {
|
||||
series = prepareTimeSeriesLong(series);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user