Commit ef0b7bda by Marcus Efraimsson Committed by Torkel Ödegaard

singlestat: render time of last point based on dashboard timezone (#11425)

* singlestat: render time of last point based on dashboard timezone

* changelog: add note for #10338
parent e52aceea
...@@ -56,6 +56,7 @@ ...@@ -56,6 +56,7 @@
* **Cloudwatch**: Add dimension filtering to CloudWatch `dimension_values()` [#10029](https://github.com/grafana/grafana/issues/10029), thx [@willyhutw](https://github.com/willyhutw) * **Cloudwatch**: Add dimension filtering to CloudWatch `dimension_values()` [#10029](https://github.com/grafana/grafana/issues/10029), thx [@willyhutw](https://github.com/willyhutw)
* **Units**: Second to HH:mm:ss formatter [#11107](https://github.com/grafana/grafana/issues/11107), thx [@gladdiologist](https://github.com/gladdiologist) * **Units**: Second to HH:mm:ss formatter [#11107](https://github.com/grafana/grafana/issues/11107), thx [@gladdiologist](https://github.com/gladdiologist)
* **Singlestat**: Add color to prefix and postfix in singlestat panel [#11143](https://github.com/grafana/grafana/pull/11143), thx [@ApsOps](https://github.com/ApsOps) * **Singlestat**: Add color to prefix and postfix in singlestat panel [#11143](https://github.com/grafana/grafana/pull/11143), thx [@ApsOps](https://github.com/ApsOps)
* **Singlestat**: Fix "time of last point" shows local time when dashboard timezone set to UTC [#10338](https://github.com/grafana/grafana/issues/10338)
* **Dashboards**: Version cleanup fails on old databases with many entries [#11278](https://github.com/grafana/grafana/issues/11278) * **Dashboards**: Version cleanup fails on old databases with many entries [#11278](https://github.com/grafana/grafana/issues/11278)
* **Server**: Adjust permissions of unix socket [#11343](https://github.com/grafana/grafana/pull/11343), thx [@corny](https://github.com/corny) * **Server**: Adjust permissions of unix socket [#11343](https://github.com/grafana/grafana/pull/11343), thx [@corny](https://github.com/corny)
* **Shortcuts**: Add shortcut for duplicate panel [#11102](https://github.com/grafana/grafana/issues/11102) * **Shortcuts**: Add shortcut for duplicate panel [#11102](https://github.com/grafana/grafana/issues/11102)
......
...@@ -101,38 +101,88 @@ describeValueFormat('d', 245, 100, 0, '35 week'); ...@@ -101,38 +101,88 @@ describeValueFormat('d', 245, 100, 0, '35 week');
describeValueFormat('d', 2456, 10, 0, '6.73 year'); describeValueFormat('d', 2456, 10, 0, '6.73 year');
describe('date time formats', function() { describe('date time formats', function() {
const epoch = 1505634997920;
const utcTime = moment.utc(epoch);
const browserTime = moment(epoch);
it('should format as iso date', function() { it('should format as iso date', function() {
var str = kbn.valueFormats.dateTimeAsIso(1505634997920, 1); var expected = browserTime.format('YYYY-MM-DD HH:mm:ss');
expect(str).toBe(moment(1505634997920).format('YYYY-MM-DD HH:mm:ss')); var actual = kbn.valueFormats.dateTimeAsIso(epoch);
expect(actual).toBe(expected);
});
it('should format as iso date (in UTC)', function() {
var expected = utcTime.format('YYYY-MM-DD HH:mm:ss');
var actual = kbn.valueFormats.dateTimeAsIso(epoch, true);
expect(actual).toBe(expected);
}); });
it('should format as iso date and skip date when today', function() { it('should format as iso date and skip date when today', function() {
var now = moment(); var now = moment();
var str = kbn.valueFormats.dateTimeAsIso(now.valueOf(), 1); var expected = now.format('HH:mm:ss');
expect(str).toBe(now.format('HH:mm:ss')); var actual = kbn.valueFormats.dateTimeAsIso(now.valueOf(), false);
expect(actual).toBe(expected);
});
it('should format as iso date (in UTC) and skip date when today', function() {
var now = moment.utc();
var expected = now.format('HH:mm:ss');
var actual = kbn.valueFormats.dateTimeAsIso(now.valueOf(), true);
expect(actual).toBe(expected);
}); });
it('should format as US date', function() { it('should format as US date', function() {
var str = kbn.valueFormats.dateTimeAsUS(1505634997920, 1); var expected = browserTime.format('MM/DD/YYYY h:mm:ss a');
expect(str).toBe(moment(1505634997920).format('MM/DD/YYYY h:mm:ss a')); var actual = kbn.valueFormats.dateTimeAsUS(epoch, false);
expect(actual).toBe(expected);
});
it('should format as US date (in UTC)', function() {
var expected = utcTime.format('MM/DD/YYYY h:mm:ss a');
var actual = kbn.valueFormats.dateTimeAsUS(epoch, true);
expect(actual).toBe(expected);
}); });
it('should format as US date and skip date when today', function() { it('should format as US date and skip date when today', function() {
var now = moment(); var now = moment();
var str = kbn.valueFormats.dateTimeAsUS(now.valueOf(), 1); var expected = now.format('h:mm:ss a');
expect(str).toBe(now.format('h:mm:ss a')); var actual = kbn.valueFormats.dateTimeAsUS(now.valueOf(), false);
expect(actual).toBe(expected);
});
it('should format as US date (in UTC) and skip date when today', function() {
var now = moment.utc();
var expected = now.format('h:mm:ss a');
var actual = kbn.valueFormats.dateTimeAsUS(now.valueOf(), true);
expect(actual).toBe(expected);
}); });
it('should format as from now with days', function() { it('should format as from now with days', function() {
var daysAgo = moment().add(-7, 'd'); var daysAgo = moment().add(-7, 'd');
var str = kbn.valueFormats.dateTimeFromNow(daysAgo.valueOf(), 1); var expected = '7 days ago';
expect(str).toBe('7 days ago'); var actual = kbn.valueFormats.dateTimeFromNow(daysAgo.valueOf(), false);
expect(actual).toBe(expected);
});
it('should format as from now with days (in UTC)', function() {
var daysAgo = moment.utc().add(-7, 'd');
var expected = '7 days ago';
var actual = kbn.valueFormats.dateTimeFromNow(daysAgo.valueOf(), true);
expect(actual).toBe(expected);
}); });
it('should format as from now with minutes', function() { it('should format as from now with minutes', function() {
var daysAgo = moment().add(-2, 'm'); var daysAgo = moment().add(-2, 'm');
var str = kbn.valueFormats.dateTimeFromNow(daysAgo.valueOf(), 1); var expected = '2 minutes ago';
expect(str).toBe('2 minutes ago'); var actual = kbn.valueFormats.dateTimeFromNow(daysAgo.valueOf(), false);
expect(actual).toBe(expected);
});
it('should format as from now with minutes (in UTC)', function() {
var daysAgo = moment.utc().add(-2, 'm');
var expected = '2 minutes ago';
var actual = kbn.valueFormats.dateTimeFromNow(daysAgo.valueOf(), true);
expect(actual).toBe(expected);
}); });
}); });
......
...@@ -816,8 +816,8 @@ kbn.valueFormats.timeticks = function(size, decimals, scaledDecimals) { ...@@ -816,8 +816,8 @@ kbn.valueFormats.timeticks = function(size, decimals, scaledDecimals) {
return kbn.valueFormats.s(size / 100, decimals, scaledDecimals); return kbn.valueFormats.s(size / 100, decimals, scaledDecimals);
}; };
kbn.valueFormats.dateTimeAsIso = function(epoch) { kbn.valueFormats.dateTimeAsIso = function(epoch, isUtc) {
var time = moment(epoch); var time = isUtc ? moment.utc(epoch) : moment(epoch);
if (moment().isSame(epoch, 'day')) { if (moment().isSame(epoch, 'day')) {
return time.format('HH:mm:ss'); return time.format('HH:mm:ss');
...@@ -825,8 +825,8 @@ kbn.valueFormats.dateTimeAsIso = function(epoch) { ...@@ -825,8 +825,8 @@ kbn.valueFormats.dateTimeAsIso = function(epoch) {
return time.format('YYYY-MM-DD HH:mm:ss'); return time.format('YYYY-MM-DD HH:mm:ss');
}; };
kbn.valueFormats.dateTimeAsUS = function(epoch) { kbn.valueFormats.dateTimeAsUS = function(epoch, isUtc) {
var time = moment(epoch); var time = isUtc ? moment.utc(epoch) : moment(epoch);
if (moment().isSame(epoch, 'day')) { if (moment().isSame(epoch, 'day')) {
return time.format('h:mm:ss a'); return time.format('h:mm:ss a');
...@@ -834,8 +834,9 @@ kbn.valueFormats.dateTimeAsUS = function(epoch) { ...@@ -834,8 +834,9 @@ kbn.valueFormats.dateTimeAsUS = function(epoch) {
return time.format('MM/DD/YYYY h:mm:ss a'); return time.format('MM/DD/YYYY h:mm:ss a');
}; };
kbn.valueFormats.dateTimeFromNow = function(epoch) { kbn.valueFormats.dateTimeFromNow = function(epoch, isUtc) {
return moment(epoch).fromNow(); var time = isUtc ? moment.utc(epoch) : moment(epoch);
return time.fromNow();
}; };
///// FORMAT MENU ///// ///// FORMAT MENU /////
......
...@@ -308,7 +308,7 @@ class SingleStatCtrl extends MetricsPanelCtrl { ...@@ -308,7 +308,7 @@ class SingleStatCtrl extends MetricsPanelCtrl {
let formatFunc = kbn.valueFormats[this.panel.format]; let formatFunc = kbn.valueFormats[this.panel.format];
data.value = lastPoint[1]; data.value = lastPoint[1];
data.valueRounded = data.value; data.valueRounded = data.value;
data.valueFormatted = formatFunc(data.value, 0, 0); data.valueFormatted = formatFunc(data.value, this.dashboard.isTimezoneUtc());
} else { } else {
data.value = this.series[0].stats[this.panel.valueName]; data.value = this.series[0].stats[this.panel.valueName];
data.flotpairs = this.series[0].flotpairs; data.flotpairs = this.series[0].flotpairs;
......
...@@ -82,6 +82,19 @@ describe('SingleStatCtrl', function() { ...@@ -82,6 +82,19 @@ describe('SingleStatCtrl', function() {
}); });
}); });
singleStatScenario('showing last iso time instead of value (in UTC)', function(ctx) {
ctx.setup(function() {
ctx.data = [{ target: 'test.cpu1', datapoints: [[10, 12], [20, 1505634997920]] }];
ctx.ctrl.panel.valueName = 'last_time';
ctx.ctrl.panel.format = 'dateTimeAsIso';
ctx.setIsUtc(true);
});
it('should set formatted value', function() {
expect(ctx.data.valueFormatted).to.be(moment.utc(1505634997920).format('YYYY-MM-DD HH:mm:ss'));
});
});
singleStatScenario('showing last us time instead of value', function(ctx) { singleStatScenario('showing last us time instead of value', function(ctx) {
ctx.setup(function() { ctx.setup(function() {
ctx.data = [{ target: 'test.cpu1', datapoints: [[10, 12], [20, 1505634997920]] }]; ctx.data = [{ target: 'test.cpu1', datapoints: [[10, 12], [20, 1505634997920]] }];
...@@ -99,6 +112,19 @@ describe('SingleStatCtrl', function() { ...@@ -99,6 +112,19 @@ describe('SingleStatCtrl', function() {
}); });
}); });
singleStatScenario('showing last us time instead of value (in UTC)', function(ctx) {
ctx.setup(function() {
ctx.data = [{ target: 'test.cpu1', datapoints: [[10, 12], [20, 1505634997920]] }];
ctx.ctrl.panel.valueName = 'last_time';
ctx.ctrl.panel.format = 'dateTimeAsUS';
ctx.setIsUtc(true);
});
it('should set formatted value', function() {
expect(ctx.data.valueFormatted).to.be(moment.utc(1505634997920).format('MM/DD/YYYY h:mm:ss a'));
});
});
singleStatScenario('showing last time from now instead of value', function(ctx) { singleStatScenario('showing last time from now instead of value', function(ctx) {
beforeEach(() => { beforeEach(() => {
clock = sinon.useFakeTimers(epoch); clock = sinon.useFakeTimers(epoch);
...@@ -124,6 +150,27 @@ describe('SingleStatCtrl', function() { ...@@ -124,6 +150,27 @@ describe('SingleStatCtrl', function() {
}); });
}); });
singleStatScenario('showing last time from now instead of value (in UTC)', function(ctx) {
beforeEach(() => {
clock = sinon.useFakeTimers(epoch);
});
ctx.setup(function() {
ctx.data = [{ target: 'test.cpu1', datapoints: [[10, 12], [20, 1505634997920]] }];
ctx.ctrl.panel.valueName = 'last_time';
ctx.ctrl.panel.format = 'dateTimeFromNow';
ctx.setIsUtc(true);
});
it('should set formatted value', function() {
expect(ctx.data.valueFormatted).to.be('2 days ago');
});
afterEach(() => {
clock.restore();
});
});
singleStatScenario('MainValue should use same number for decimals as displayed when checking thresholds', function( singleStatScenario('MainValue should use same number for decimals as displayed when checking thresholds', function(
ctx ctx
) { ) {
......
...@@ -23,6 +23,7 @@ export function ControllerTestContext() { ...@@ -23,6 +23,7 @@ export function ControllerTestContext() {
}; };
}, },
}; };
this.isUtc = false;
this.providePhase = function(mocks) { this.providePhase = function(mocks) {
return angularMocks.module(function($provide) { return angularMocks.module(function($provide) {
...@@ -46,6 +47,10 @@ export function ControllerTestContext() { ...@@ -46,6 +47,10 @@ export function ControllerTestContext() {
self.$q = $q; self.$q = $q;
self.panel = new PanelModel({ type: 'test' }); self.panel = new PanelModel({ type: 'test' });
self.dashboard = { meta: {} }; self.dashboard = { meta: {} };
self.isUtc = false;
self.dashboard.isTimezoneUtc = function() {
return self.isUtc;
};
$rootScope.appEvent = sinon.spy(); $rootScope.appEvent = sinon.spy();
$rootScope.onAppEvent = sinon.spy(); $rootScope.onAppEvent = sinon.spy();
...@@ -93,6 +98,10 @@ export function ControllerTestContext() { ...@@ -93,6 +98,10 @@ export function ControllerTestContext() {
}); });
}); });
}; };
this.setIsUtc = function(isUtc = false) {
self.isUtc = isUtc;
};
} }
export function ServiceTestContext() { export function ServiceTestContext() {
......
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