mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
GrafanaDS: Add support for annotation time regions (#65462)
Co-authored-by: Ryan McKinley <ryantxu@gmail.com> Co-authored-by: Adela Almasan <adela.almasan@grafana.com>
This commit is contained in:
@@ -1,40 +1,160 @@
|
||||
import { dateTime, TimeRange } from '@grafana/data';
|
||||
import { TimeRegionConfig } from 'app/core/utils/timeRegions';
|
||||
|
||||
import { calculateTimesWithin, TimeRegionConfig } from './timeRegions';
|
||||
import { calculateTimesWithin } from './timeRegions';
|
||||
|
||||
// note: calculateTimesWithin always returns time ranges in UTC
|
||||
describe('timeRegions', () => {
|
||||
describe('day of week', () => {
|
||||
it('4 sundays in january 2021', () => {
|
||||
it('returns regions with 4 Mondays in March 2023', () => {
|
||||
const cfg: TimeRegionConfig = {
|
||||
fromDayOfWeek: 1,
|
||||
from: '12:00',
|
||||
};
|
||||
|
||||
const tr: TimeRange = {
|
||||
from: dateTime('2021-01-00', 'YYYY-MM-dd'),
|
||||
to: dateTime('2021-02-00', 'YYYY-MM-dd'),
|
||||
from: dateTime('2023-03-01'),
|
||||
to: dateTime('2023-03-31'),
|
||||
raw: {
|
||||
to: '',
|
||||
from: '',
|
||||
},
|
||||
};
|
||||
|
||||
const regions = calculateTimesWithin(cfg, tr);
|
||||
expect(regions).toMatchInlineSnapshot(`
|
||||
[
|
||||
{
|
||||
"from": 1609779600000,
|
||||
"to": 1609779600000,
|
||||
"from": 1678060800000,
|
||||
"to": 1678147199000,
|
||||
},
|
||||
{
|
||||
"from": 1610384400000,
|
||||
"to": 1610384400000,
|
||||
"from": 1678665600000,
|
||||
"to": 1678751999000,
|
||||
},
|
||||
{
|
||||
"from": 1610989200000,
|
||||
"to": 1610989200000,
|
||||
"from": 1679270400000,
|
||||
"to": 1679356799000,
|
||||
},
|
||||
{
|
||||
"from": 1611594000000,
|
||||
"to": 1611594000000,
|
||||
"from": 1679875200000,
|
||||
"to": 1679961599000,
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
||||
describe('day and time of week', () => {
|
||||
it('returns regions with 4 Mondays at 20:00 in March 2023', () => {
|
||||
const cfg: TimeRegionConfig = {
|
||||
fromDayOfWeek: 1,
|
||||
from: '20:00',
|
||||
};
|
||||
|
||||
const tr: TimeRange = {
|
||||
from: dateTime('2023-03-01'),
|
||||
to: dateTime('2023-03-31'),
|
||||
raw: {
|
||||
to: '',
|
||||
from: '',
|
||||
},
|
||||
};
|
||||
|
||||
const regions = calculateTimesWithin(cfg, tr);
|
||||
expect(regions).toMatchInlineSnapshot(`
|
||||
[
|
||||
{
|
||||
"from": 1678132800000,
|
||||
"to": 1678132800000,
|
||||
},
|
||||
{
|
||||
"from": 1678737600000,
|
||||
"to": 1678737600000,
|
||||
},
|
||||
{
|
||||
"from": 1679342400000,
|
||||
"to": 1679342400000,
|
||||
},
|
||||
{
|
||||
"from": 1679947200000,
|
||||
"to": 1679947200000,
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
||||
describe('day of week range', () => {
|
||||
it('returns regions with days range', () => {
|
||||
const cfg: TimeRegionConfig = {
|
||||
fromDayOfWeek: 1,
|
||||
toDayOfWeek: 3,
|
||||
};
|
||||
|
||||
const tr: TimeRange = {
|
||||
from: dateTime('2023-03-01'),
|
||||
to: dateTime('2023-03-31'),
|
||||
raw: {
|
||||
to: '',
|
||||
from: '',
|
||||
},
|
||||
};
|
||||
|
||||
const regions = calculateTimesWithin(cfg, tr);
|
||||
expect(regions).toMatchInlineSnapshot(`
|
||||
[
|
||||
{
|
||||
"from": 1678060800000,
|
||||
"to": 1678319999000,
|
||||
},
|
||||
{
|
||||
"from": 1678665600000,
|
||||
"to": 1678924799000,
|
||||
},
|
||||
{
|
||||
"from": 1679270400000,
|
||||
"to": 1679529599000,
|
||||
},
|
||||
{
|
||||
"from": 1679875200000,
|
||||
"to": 1680134399000,
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
it('returns regions with days/times range', () => {
|
||||
const cfg: TimeRegionConfig = {
|
||||
fromDayOfWeek: 1,
|
||||
from: '20:00',
|
||||
toDayOfWeek: 2,
|
||||
to: '10:00',
|
||||
};
|
||||
|
||||
const tr: TimeRange = {
|
||||
from: dateTime('2023-03-01'),
|
||||
to: dateTime('2023-03-31'),
|
||||
raw: {
|
||||
to: '',
|
||||
from: '',
|
||||
},
|
||||
};
|
||||
|
||||
const regions = calculateTimesWithin(cfg, tr);
|
||||
expect(regions).toMatchInlineSnapshot(`
|
||||
[
|
||||
{
|
||||
"from": 1678132800000,
|
||||
"to": 1678183200000,
|
||||
},
|
||||
{
|
||||
"from": 1678737600000,
|
||||
"to": 1678788000000,
|
||||
},
|
||||
{
|
||||
"from": 1679342400000,
|
||||
"to": 1679392800000,
|
||||
},
|
||||
{
|
||||
"from": 1679947200000,
|
||||
"to": 1679997600000,
|
||||
},
|
||||
]
|
||||
`);
|
||||
|
||||
@@ -6,6 +6,8 @@ export interface TimeRegionConfig {
|
||||
|
||||
to?: string;
|
||||
toDayOfWeek?: number; // 1-7
|
||||
|
||||
timezone?: string;
|
||||
}
|
||||
|
||||
interface ParsedTime {
|
||||
@@ -32,8 +34,8 @@ export function calculateTimesWithin(cfg: TimeRegionConfig, tRange: TimeRange):
|
||||
}
|
||||
|
||||
const hRange = {
|
||||
from: parseTimeRange(timeRegion.from),
|
||||
to: parseTimeRange(timeRegion.to),
|
||||
from: parseTimeOfDay(timeRegion.from),
|
||||
to: parseTimeOfDay(timeRegion.to),
|
||||
};
|
||||
|
||||
if (!timeRegion.fromDayOfWeek && timeRegion.toDayOfWeek) {
|
||||
@@ -78,10 +80,11 @@ export function calculateTimesWithin(cfg: TimeRegionConfig, tRange: TimeRange):
|
||||
|
||||
const regions: AbsoluteTimeRange[] = [];
|
||||
|
||||
const fromStart = dateTime(tRange.from);
|
||||
const fromStart = dateTime(tRange.from).utc();
|
||||
fromStart.set('hour', 0);
|
||||
fromStart.set('minute', 0);
|
||||
fromStart.set('second', 0);
|
||||
fromStart.set('millisecond', 0);
|
||||
fromStart.add(hRange.from.h, 'hours');
|
||||
fromStart.add(hRange.from.m, 'minutes');
|
||||
fromStart.add(hRange.from.s, 'seconds');
|
||||
@@ -95,7 +98,7 @@ export function calculateTimesWithin(cfg: TimeRegionConfig, tRange: TimeRange):
|
||||
break;
|
||||
}
|
||||
|
||||
const fromEnd = dateTime(fromStart);
|
||||
const fromEnd = dateTime(fromStart).utc();
|
||||
|
||||
if (fromEnd.hour) {
|
||||
if (hRange.from.h <= hRange.to.h) {
|
||||
@@ -134,35 +137,36 @@ export function calculateTimesWithin(cfg: TimeRegionConfig, tRange: TimeRange):
|
||||
return regions;
|
||||
}
|
||||
|
||||
function parseTimeRange(str?: string): ParsedTime {
|
||||
export function parseTimeOfDay(str?: string): ParsedTime {
|
||||
const result: ParsedTime = {};
|
||||
if (!str?.length) {
|
||||
return result;
|
||||
}
|
||||
|
||||
const timeRegex = /^([\d]+):?(\d{2})?/;
|
||||
const match = timeRegex.exec(str);
|
||||
|
||||
if (!match) {
|
||||
const match = str.split(':');
|
||||
if (!match?.length) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result.h = Math.min(23, Math.max(0, Number(match[0])));
|
||||
if (match.length > 1) {
|
||||
result.h = Number(match[1]);
|
||||
result.m = 0;
|
||||
|
||||
if (match.length > 2 && match[2] !== undefined) {
|
||||
result.m = Number(match[2]);
|
||||
}
|
||||
|
||||
if (result.h > 23) {
|
||||
result.h = 23;
|
||||
}
|
||||
|
||||
if (result.m > 59) {
|
||||
result.m = 59;
|
||||
result.m = Math.min(60, Math.max(0, Number(match[1])));
|
||||
if (match.length > 2) {
|
||||
result.s = Math.min(60, Math.max(0, Number(match[2])));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function formatTimeOfDayString(t?: ParsedTime): string {
|
||||
if (!t || (t.h == null && t.m == null && t.s == null)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
let str = String(t.h ?? 0).padStart(2, '0') + ':' + String(t.m ?? 0).padStart(2, '0');
|
||||
if (t.s != null) {
|
||||
str += String(t.s ?? 0).padStart(2, '0');
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user