Commit 1a9c52e1 by Torkel Ödegaard

feat(timepicker): lots of big changes, moving to datemath from…

feat(timepicker): lots of big changes, moving to datemath from kbn.parseDateMath, moving to moment dates instead of native javascript dates
parent 5ad38ee9
...@@ -163,172 +163,6 @@ function($, _, moment) { ...@@ -163,172 +163,6 @@ function($, _, moment) {
return info.sec * info.count; return info.sec * info.count;
}; };
/* This is a simplified version of elasticsearch's date parser */
kbn.parseDate = function(text) {
if(_.isDate(text)) {
return text;
}
var time;
var mathString = "";
var index;
var parseString;
if (text.substring(0,3) === "now") {
time = new Date();
mathString = text.substring(3);
}
else if (text.substring(0,5) === 'today') {
time = new Date();
time.setHours(0,0,0,0);
mathString = text.substring(5);
}
else {
index = text.indexOf("||");
parseString;
if (index === -1) {
parseString = text;
mathString = ""; // nothing else
} else {
parseString = text.substring(0, index);
mathString = text.substring(index + 2);
}
// We're going to just require ISO8601 timestamps, k?
time = new Date(parseString);
}
if (!mathString.length) {
return time;
}
//return [time,parseString,mathString];
return kbn.parseDateMath(mathString, time);
};
kbn.getRelativeTimeInfo = function(str) {
var info = {value: str};
if (str === 'today') {
info.text = 'Today';
info.from = 'today';
info.to = 'now';
} else {
info.text = 'Last ' + str;
info.from = 'now-'+str;
info.to = 'now';
}
return info;
};
kbn._timespanRegex = /^(\d+[h,m,M,w,s,H,d])|today$/;
kbn.isValidTimeSpan = function(str) {
return kbn._timespanRegex.test(str);
};
kbn.parseDateMath = function(mathString, time, roundUp) {
var dateTime = moment(time);
for (var i = 0; i < mathString.length;) {
var c = mathString.charAt(i++),
type,
num,
unit;
if (c === '/') {
type = 0;
} else if (c === '+') {
type = 1;
} else if (c === '-') {
type = 2;
} else {
return false;
}
if (isNaN(mathString.charAt(i))) {
num = 1;
} else {
var numFrom = i;
while (!isNaN(mathString.charAt(i))) {
i++;
}
num = parseInt(mathString.substring(numFrom, i),10);
}
if (type === 0) {
// rounding is only allowed on whole numbers
if (num !== 1) {
return false;
}
}
unit = mathString.charAt(i++);
switch (unit) {
case 'y':
if (type === 0) {
roundUp ? dateTime.endOf('year') : dateTime.startOf('year');
} else if (type === 1) {
dateTime.add(num, 'years');
} else if (type === 2) {
dateTime.subtract(num, 'years');
}
break;
case 'M':
if (type === 0) {
roundUp ? dateTime.endOf('month') : dateTime.startOf('month');
} else if (type === 1) {
dateTime.add(num, 'months');
} else if (type === 2) {
dateTime.subtract(num, 'months');
}
break;
case 'w':
if (type === 0) {
roundUp ? dateTime.endOf('week') : dateTime.startOf('week');
} else if (type === 1) {
dateTime.add(num, 'weeks');
} else if (type === 2) {
dateTime.subtract(num, 'weeks');
}
break;
case 'd':
if (type === 0) {
roundUp ? dateTime.endOf('day') : dateTime.startOf('day');
} else if (type === 1) {
dateTime.add(num, 'days');
} else if (type === 2) {
dateTime.subtract(num, 'days');
}
break;
case 'h':
case 'H':
if (type === 0) {
roundUp ? dateTime.endOf('hour') : dateTime.startOf('hour');
} else if (type === 1) {
dateTime.add(num, 'hours');
} else if (type === 2) {
dateTime.subtract(num,'hours');
}
break;
case 'm':
if (type === 0) {
roundUp ? dateTime.endOf('minute') : dateTime.startOf('minute');
} else if (type === 1) {
dateTime.add(num, 'minutes');
} else if (type === 2) {
dateTime.subtract(num, 'minutes');
}
break;
case 's':
if (type === 0) {
roundUp ? dateTime.endOf('second') : dateTime.startOf('second');
} else if (type === 1) {
dateTime.add(num, 'seconds');
} else if (type === 2) {
dateTime.subtract(num, 'seconds');
}
break;
default:
return false;
}
}
return dateTime.toDate();
};
kbn.query_color_dot = function (color, diameter) { kbn.query_color_dot = function (color, diameter) {
return '<div class="icon-circle" style="' + [ return '<div class="icon-circle" style="' + [
'display:inline-block', 'display:inline-block',
......
define([ define([
'kbn', 'kbn',
'../core_module', 'app/core/core_module',
'app/core/utils/rangeutil',
'moment',
], ],
function (kbn, coreModule) { function (kbn, coreModule, rangeUtil, moment) {
'use strict'; 'use strict';
coreModule.directive('ngModelOnblur', function() { coreModule.directive('ngModelOnblur', function() {
...@@ -46,7 +48,8 @@ function (kbn, coreModule) { ...@@ -46,7 +48,8 @@ function (kbn, coreModule) {
if (ctrl.$isEmpty(modelValue)) { if (ctrl.$isEmpty(modelValue)) {
return true; return true;
} }
return kbn.isValidTimeSpan(viewValue); var info = rangeUtil.describeTextRange(viewValue);
return info.invalid !== true;
}; };
} }
}; };
......
...@@ -43,6 +43,19 @@ function parse(text, roundUp?) { ...@@ -43,6 +43,19 @@ function parse(text, roundUp?) {
return parseDateMath(mathString, time, roundUp); return parseDateMath(mathString, time, roundUp);
} }
function isValid(text) {
var date = parse(text);
if (!date) {
return false;
}
if (moment.isMoment(date)) {
return date.isValid();
}
return false;
}
function parseDateMath(mathString, time, roundUp?) { function parseDateMath(mathString, time, roundUp?) {
var dateTime = time; var dateTime = time;
var i = 0; var i = 0;
...@@ -107,5 +120,6 @@ function parseDateMath(mathString, time, roundUp?) { ...@@ -107,5 +120,6 @@ function parseDateMath(mathString, time, roundUp?) {
export = { export = {
parse: parse, parse: parse,
parseDateMath: parseDateMath parseDateMath: parseDateMath,
isValid: isValid
}; };
...@@ -75,7 +75,7 @@ _.each(rangeOptions, function (frame) { ...@@ -75,7 +75,7 @@ _.each(rangeOptions, function (frame) {
return opt; return opt;
} }
opt = {from: 'now-' + expr, to: 'now', display: 'Parse error'}; opt = {from: 'now-' + expr, to: 'now'};
if (/^\d+\w$/.test(expr)) { if (/^\d+\w$/.test(expr)) {
let unit = expr[expr.length - 1]; let unit = expr[expr.length - 1];
...@@ -87,6 +87,9 @@ _.each(rangeOptions, function (frame) { ...@@ -87,6 +87,9 @@ _.each(rangeOptions, function (frame) {
opt.display += 's'; opt.display += 's';
} }
} }
} else {
opt.display = 'parse error';
opt.invalid = true;
} }
return opt; return opt;
......
...@@ -42,14 +42,14 @@ define([ ...@@ -42,14 +42,14 @@ define([
return value; return value;
} }
if (value.length === 8) { if (value.length === 8) {
return moment.utc(value, 'YYYYMMDD').toDate(); return moment.utc(value, 'YYYYMMDD');
} }
if (value.length === 15) { if (value.length === 15) {
return moment.utc(value, 'YYYYMMDDTHHmmss').toDate(); return moment.utc(value, 'YYYYMMDDTHHmmss');
} }
var epoch = parseInt(value); var epoch = parseInt(value);
if (!_.isNaN(epoch)) { if (!_.isNaN(epoch)) {
return new Date(epoch); return moment(epoch);
} }
return null; return null;
......
define([ define([
'angular', 'angular',
'app/core/utils/datemath',
'app/core/utils/rangeutil',
'lodash', 'lodash',
'kbn', 'kbn',
'jquery', 'jquery',
], ],
function (angular, _, kbn, $) { function (angular, dateMath, rangeUtil, _, kbn, $) {
'use strict'; 'use strict';
var module = angular.module('grafana.services'); var module = angular.module('grafana.services');
...@@ -63,30 +65,32 @@ function (angular, _, kbn, $) { ...@@ -63,30 +65,32 @@ function (angular, _, kbn, $) {
// check panel time overrrides // check panel time overrrides
if (scope.panel.timeFrom) { if (scope.panel.timeFrom) {
if (!kbn.isValidTimeSpan(scope.panel.timeFrom)) { var timeFromInfo = rangeUtil.describeTextRange(scope.panel.timeFrom);
scope.panelMeta.timeInfo = 'invalid time override'; if (timeFromInfo.invalid) {
scope.panelMeta.timeFromInfo = 'invalid time override';
return; return;
} }
if (_.isString(scope.rangeUnparsed.from)) { if (_.isString(scope.rangeUnparsed.from)) {
var timeInfo = kbn.getRelativeTimeInfo(scope.panel.timeFrom); var timeFromDate = dateMath.parse(timeFromInfo.from);
scope.panelMeta.timeInfo = timeInfo.text; scope.panelMeta.timeInfo = timeFromInfo.display;
scope.rangeUnparsed.from = timeInfo.from; scope.rangeUnparsed.from = timeFromInfo.from;
scope.rangeUnparsed.to = timeInfo.to; scope.rangeUnparsed.to = timeFromInfo.to;
scope.range.from = kbn.parseDate(scope.rangeUnparsed.from); scope.range.from = timeFromDate;
} }
} }
if (scope.panel.timeShift) { if (scope.panel.timeShift) {
if (!kbn.isValidTimeSpan(scope.panel.timeShift)) { var timeShiftInfo = rangeUtil.describeTextRange(scope.panel.timeFrom);
if (timeShiftInfo.invalid) {
scope.panelMeta.timeInfo = 'invalid timeshift'; scope.panelMeta.timeInfo = 'invalid timeshift';
return; return;
} }
var timeShift = '-' + scope.panel.timeShift; var timeShift = '-' + scope.panel.timeShift;
scope.panelMeta.timeInfo += ' timeshift ' + timeShift; scope.panelMeta.timeInfo += ' timeshift ' + timeShift;
scope.range.from = kbn.parseDateMath(timeShift, scope.range.from); scope.range.from = dateMath.parseDateMath(timeShift, scope.range.from, false);
scope.range.to = kbn.parseDateMath(timeShift, scope.range.to); scope.range.to = dateMath.parseDateMath(timeShift, scope.range.to, true);
scope.rangeUnparsed = scope.range; scope.rangeUnparsed = scope.range;
} }
...@@ -99,6 +103,8 @@ function (angular, _, kbn, $) { ...@@ -99,6 +103,8 @@ function (angular, _, kbn, $) {
this.issueMetricQuery = function(scope, datasource) { this.issueMetricQuery = function(scope, datasource) {
var metricsQuery = { var metricsQuery = {
range: scope.rangeUnparsed, range: scope.rangeUnparsed,
timeFrom: scope.range.valueOf(),
timeTo: scope.range.valueOf(),
interval: scope.interval, interval: scope.interval,
targets: scope.panel.targets, targets: scope.panel.targets,
format: scope.panel.renderer === 'png' ? 'png' : 'json', format: scope.panel.renderer === 'png' ? 'png' : 'json',
...@@ -118,6 +124,5 @@ function (angular, _, kbn, $) { ...@@ -118,6 +124,5 @@ function (angular, _, kbn, $) {
return results; return results;
}); });
}; };
}); });
}); });
...@@ -141,11 +141,9 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticRes ...@@ -141,11 +141,9 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticRes
}); });
}; };
ElasticDatasource.prototype.getQueryHeader = function(timeRange) { ElasticDatasource.prototype.getQueryHeader = function(timeFrom, timeTo) {
var header = {search_type: "count", "ignore_unavailable": true}; var header = {search_type: "count", "ignore_unavailable": true};
var from = kbn.parseDate(timeRange.from); header.index = this.indexPattern.getIndexList(timeFrom, timeTo);
var to = kbn.parseDate(timeRange.to);
header.index = this.indexPattern.getIndexList(from, to);
return angular.toJson(header); return angular.toJson(header);
}; };
...@@ -154,15 +152,13 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticRes ...@@ -154,15 +152,13 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticRes
var target; var target;
var sentTargets = []; var sentTargets = [];
var header = this.getQueryHeader(options.range); var header = this.getQueryHeader(options.timeFrom, options.timeTo);
var timeFrom = this.translateTime(options.range.from);
var timeTo = this.translateTime(options.range.to);
for (var i = 0; i < options.targets.length; i++) { for (var i = 0; i < options.targets.length; i++) {
target = options.targets[i]; target = options.targets[i];
if (target.hide) {return;} if (target.hide) {return;}
var esQuery = this.queryBuilder.build(target, timeFrom, timeTo); var esQuery = this.queryBuilder.build(target, options.timeFrom, options.timeTo);
payload += header + '\n'; payload += header + '\n';
payload += angular.toJson(esQuery) + '\n'; payload += angular.toJson(esQuery) + '\n';
...@@ -170,8 +166,8 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticRes ...@@ -170,8 +166,8 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticRes
} }
payload = payload.replace(/\$interval/g, options.interval); payload = payload.replace(/\$interval/g, options.interval);
payload = payload.replace(/\$timeFrom/g, this.translateTime(options.range.from)); payload = payload.replace(/\$timeFrom/g, options.timeFrom);
payload = payload.replace(/\$timeTo/g, this.translateTime(options.range.to)); payload = payload.replace(/\$timeTo/g, options.timeTo);
payload = payload.replace(/\$maxDataPoints/g, options.maxDataPoints); payload = payload.replace(/\$maxDataPoints/g, options.maxDataPoints);
payload = templateSrv.replace(payload, options.scopedVars); payload = templateSrv.replace(payload, options.scopedVars);
...@@ -180,14 +176,6 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticRes ...@@ -180,14 +176,6 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticRes
}); });
}; };
ElasticDatasource.prototype.translateTime = function(date) {
if (_.isString(date)) {
return date;
}
return date.getTime();
};
ElasticDatasource.prototype.metricFindQuery = function() { ElasticDatasource.prototype.metricFindQuery = function() {
return this._get('/_mapping').then(function(res) { return this._get('/_mapping').then(function(res) {
var fields = {}; var fields = {};
......
...@@ -89,7 +89,32 @@ describe("DateMath", () => { ...@@ -89,7 +89,32 @@ describe("DateMath", () => {
expect(dateMath.parse('now/' + span, true).format(format)).to.eql(now.endOf(span).format(format)); expect(dateMath.parse('now/' + span, true).format(format)).to.eql(now.endOf(span).format(format));
}); });
}); });
});
describe('isValid', () => {
it('should return false when invalid date text', () => {
expect(dateMath.isValid('asd')).to.be(false);
});
it('should return true when valid date text', () => {
expect(dateMath.isValid('now-1h')).to.be(true);
});
});
describe('relative time to date parsing', function() {
it('should handle negative time', function() {
var date = dateMath.parseDateMath('-2d', moment([2014, 1, 5]));
expect(date.valueOf()).to.equal(moment([2014, 1, 3]).valueOf());
});
it('should handle multiple math expressions', function() {
var date = dateMath.parseDateMath('-2d-6h', moment([2014, 1, 5]));
expect(date.valueOf()).to.equal(moment([2014, 1, 2, 18]).valueOf());
});
it('should return false when invalid expression', function() {
var date = dateMath.parseDateMath('2', moment([2014, 1, 5]));
expect(date).to.equal(undefined);
});
}); });
}); });
......
...@@ -57,10 +57,8 @@ define([ ...@@ -57,10 +57,8 @@ define([
}; };
ctx.ds.query({ ctx.ds.query({
range: { timeFrom: moment(new Date(2015, 4, 30, 10)),
from: new Date(2015, 4, 30, 10), timeTo: moment(new Date(2015, 5, 1, 10)),
to: new Date(2015, 5, 1, 10)
},
targets: [{ bucketAggs: [], metrics: [] }] targets: [{ bucketAggs: [], metrics: [] }]
}); });
......
define([ define([
'kbn', 'kbn',
'lodash' 'lodash',
], function(kbn, _) { 'app/core/utils/datemath',
], function(kbn, _, dateMath) {
'use strict'; 'use strict';
function ControllerTestContext() { function ControllerTestContext() {
...@@ -107,8 +108,8 @@ define([ ...@@ -107,8 +108,8 @@ define([
return this.time; return this.time;
} }
return { return {
from : kbn.parseDate(this.time.from), from : dateMath.parse(this.time.from, false),
to : kbn.parseDate(this.time.to) to : dateMath.parse(this.time.to, true)
}; };
}; };
......
define([ define([
'kbn' 'kbn',
], function(kbn) { 'app/core/utils/datemath'
], function(kbn, dateMath) {
'use strict'; 'use strict';
function describeValueFormat(desc, value, tickSize, tickDecimals, result) { function describeValueFormat(desc, value, tickSize, tickDecimals, result) {
...@@ -60,60 +61,33 @@ define([ ...@@ -60,60 +61,33 @@ define([
describe('calculateInterval', function() { describe('calculateInterval', function() {
it('1h 100 resultion', function() { it('1h 100 resultion', function() {
var range = { from: kbn.parseDate('now-1h'), to: kbn.parseDate('now') }; var range = { from: dateMath.parse('now-1h'), to: dateMath.parse('now') };
var str = kbn.calculateInterval(range, 100, null); var str = kbn.calculateInterval(range, 100, null);
expect(str).to.be('30s'); expect(str).to.be('30s');
}); });
it('10m 1600 resolution', function() { it('10m 1600 resolution', function() {
var range = { from: kbn.parseDate('now-10m'), to: kbn.parseDate('now') }; var range = { from: dateMath.parse('now-10m'), to: dateMath.parse('now') };
var str = kbn.calculateInterval(range, 1600, null); var str = kbn.calculateInterval(range, 1600, null);
expect(str).to.be('100ms'); expect(str).to.be('100ms');
}); });
it('fixed user interval', function() { it('fixed user interval', function() {
var range = { from: kbn.parseDate('now-10m'), to: kbn.parseDate('now') }; var range = { from: dateMath.parse('now-10m'), to: dateMath.parse('now') };
var str = kbn.calculateInterval(range, 1600, '10s'); var str = kbn.calculateInterval(range, 1600, '10s');
expect(str).to.be('10s'); expect(str).to.be('10s');
}); });
it('short time range and user low limit', function() { it('short time range and user low limit', function() {
var range = { from: kbn.parseDate('now-10m'), to: kbn.parseDate('now') }; var range = { from: dateMath.parse('now-10m'), to: dateMath.parse('now') };
var str = kbn.calculateInterval(range, 1600, '>10s'); var str = kbn.calculateInterval(range, 1600, '>10s');
expect(str).to.be('10s'); expect(str).to.be('10s');
}); });
it('large time range and user low limit', function() { it('large time range and user low limit', function() {
var range = { from: kbn.parseDate('now-14d'), to: kbn.parseDate('now') }; var range = { from: dateMath.parse('now-14d'), to: dateMath.parse('now') };
var str = kbn.calculateInterval(range, 1000, '>10s'); var str = kbn.calculateInterval(range, 1000, '>10s');
expect(str).to.be('30m'); expect(str).to.be('30m');
}); });
});
describe('relative time to date parsing', function() {
it('should handle negative time', function() {
var date = kbn.parseDateMath('-2d', new Date(2014,1,5));
expect(date.getTime()).to.equal(new Date(2014, 1, 3).getTime());
});
it('should handle today', function() {
var date = kbn.parseDate('today');
var today = new Date();
today.setHours(0,0,0,0);
expect(date.getTime()).to.equal(today.getTime());
});
it('should handle multiple math expressions', function() {
var date = kbn.parseDateMath('-2d-6h', new Date(2014, 1, 5));
expect(date.toString()).to.equal(new Date(2014, 1, 2, 18).toString());
});
it('should return false when invalid expression', function() {
var date = kbn.parseDateMath('2', new Date(2014, 1, 5));
expect(date).to.equal(false);
});
}); });
}); });
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment