diff --git a/devenv/dev-dashboards/scenarios/relative_time_zone_support.json b/devenv/dev-dashboards/scenarios/relative_time_zone_support.json
new file mode 100644
index 00000000000..5fcf0e10597
--- /dev/null
+++ b/devenv/dev-dashboards/scenarios/relative_time_zone_support.json
@@ -0,0 +1,179 @@
+{
+ "annotations": {
+ "list": [
+ {
+ "builtIn": 1,
+ "datasource": {
+ "type": "grafana",
+ "uid": "-- Grafana --"
+ },
+ "enable": true,
+ "hide": true,
+ "iconColor": "rgba(0, 211, 255, 1)",
+ "name": "Annotations & Alerts",
+ "type": "dashboard"
+ }
+ ]
+ },
+ "editable": true,
+ "fiscalYearStartMonth": 0,
+ "graphTooltip": 0,
+ "id": 7271,
+ "links": [],
+ "liveNow": false,
+ "panels": [
+ {
+ "datasource": {
+ "type": "testdata",
+ "uid": "gdev-testdata"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "custom": {
+ "align": "auto",
+ "cellOptions": {
+ "type": "auto"
+ },
+ "inspect": false
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 0,
+ "y": 0
+ },
+ "id": 2,
+ "options": {
+ "cellHeight": "sm",
+ "footer": {
+ "countRows": false,
+ "fields": "",
+ "reducer": [
+ "sum"
+ ],
+ "show": false
+ },
+ "showHeader": true
+ },
+ "pluginVersion": "10.2.0-pre",
+ "targets": [
+ {
+ "datasource": {
+ "type": "testdata",
+ "uid": "gdev-testdata"
+ },
+ "refId": "A",
+ "scenarioId": "random_walk",
+ "seriesCount": 1
+ }
+ ],
+ "title": "Panel in timezone",
+ "type": "table"
+ },
+ {
+ "datasource": {
+ "type": "testdata",
+ "uid": "gdev-testdata"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "custom": {
+ "align": "auto",
+ "cellOptions": {
+ "type": "auto"
+ },
+ "inspect": false
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 12,
+ "y": 0
+ },
+ "id": 3,
+ "options": {
+ "cellHeight": "sm",
+ "footer": {
+ "countRows": false,
+ "fields": "",
+ "reducer": [
+ "sum"
+ ],
+ "show": false
+ },
+ "showHeader": true
+ },
+ "pluginVersion": "10.2.0-pre",
+ "targets": [
+ {
+ "datasource": {
+ "type": "testdata",
+ "uid": "gdev-testdata"
+ },
+ "refId": "A",
+ "scenarioId": "random_walk",
+ "seriesCount": 1
+ }
+ ],
+ "timeFrom": "now/d",
+ "title": "Panel with relative time override",
+ "type": "table"
+ }
+ ],
+ "refresh": "",
+ "schemaVersion": 38,
+ "tags": [],
+ "templating": {
+ "list": []
+ },
+ "time": {
+ "from": "now-3h",
+ "to": "now"
+ },
+ "timepicker": {},
+ "timezone": "Asia/Tokyo",
+ "title": "Panel Tests - Relative time zone support",
+ "uid": "d41dbaa2-a39e-4536-ab2b-caca52f1a9c8",
+ "version": 7,
+ "weekStart": ""
+}
diff --git a/devenv/jsonnet/dev-dashboards.libsonnet b/devenv/jsonnet/dev-dashboards.libsonnet
index bd946d2f062..56259f979bf 100644
--- a/devenv/jsonnet/dev-dashboards.libsonnet
+++ b/devenv/jsonnet/dev-dashboards.libsonnet
@@ -548,6 +548,13 @@ local dashboard = grafana.dashboard;
id: 0,
}
},
+ dashboard.new('relative_time_zone_support', import '../dev-dashboards/scenarios/relative_time_zone_support.json') +
+ resource.addMetadata('folder', 'dev-dashboards') +
+ {
+ spec+: {
+ id: 0,
+ }
+ },
dashboard.new('reuse', import '../dev-dashboards/transforms/reuse.json') +
resource.addMetadata('folder', 'dev-dashboards') +
{
diff --git a/e2e/dashboards-suite/dashboard-time-zone.spec.ts b/e2e/dashboards-suite/dashboard-time-zone.spec.ts
index 08686be2cbc..accdeb5991d 100644
--- a/e2e/dashboards-suite/dashboard-time-zone.spec.ts
+++ b/e2e/dashboards-suite/dashboard-time-zone.spec.ts
@@ -85,6 +85,127 @@ e2e.scenario({
},
});
+e2e.scenario({
+ describeName: 'Dashboard time zone support',
+ itName: 'Tests relative timezone support and overrides',
+ addScenarioDataSource: false,
+ addScenarioDashBoard: false,
+ skipScenario: false,
+ scenario: () => {
+ // Open dashboard
+ e2e.flows.openDashboard({
+ uid: 'd41dbaa2-a39e-4536-ab2b-caca52f1a9c8',
+ });
+
+ e2e().intercept('/api/ds/query*').as('dataQuery');
+
+ // Switch to Browser timezone
+ e2e.flows.setTimeRange({
+ from: 'now-6h',
+ to: 'now',
+ zone: 'Browser',
+ });
+ // Need to wait for 2 calls as there's 2 panels
+ e2e().wait(['@dataQuery', '@dataQuery']);
+
+ e2e.components.Panels.Panel.title('Panel with relative time override')
+ .should('be.visible')
+ .within(() => {
+ e2e().contains('[role="row"]', '00:00:00').should('be.visible');
+ });
+
+ // Today so far, still in Browser timezone
+ e2e.flows.setTimeRange({
+ from: 'now/d',
+ to: 'now',
+ });
+ // Need to wait for 2 calls as there's 2 panels
+ e2e().wait(['@dataQuery', '@dataQuery']);
+
+ e2e.components.Panels.Panel.title('Panel with relative time override')
+ .should('be.visible')
+ .within(() => {
+ e2e().contains('[role="row"]', '00:00:00').should('be.visible');
+ });
+
+ e2e.components.Panels.Panel.title('Panel in timezone')
+ .should('be.visible')
+ .within(() => {
+ e2e().contains('[role="row"]', '00:00:00').should('be.visible');
+ });
+
+ // Test Tokyo timezone
+ e2e.flows.setTimeRange({
+ from: 'now-6h',
+ to: 'now',
+ zone: 'Asia/Tokyo',
+ });
+ // Need to wait for 2 calls as there's 2 panels
+ e2e().wait(['@dataQuery', '@dataQuery']);
+
+ e2e.components.Panels.Panel.title('Panel with relative time override')
+ .should('be.visible')
+ .within(() => {
+ e2e().contains('[role="row"]', '00:00:00').should('be.visible');
+ });
+
+ // Today so far, still in Tokyo timezone
+ e2e.flows.setTimeRange({
+ from: 'now/d',
+ to: 'now',
+ });
+ // Need to wait for 2 calls as there's 2 panels
+ e2e().wait(['@dataQuery', '@dataQuery']);
+
+ e2e.components.Panels.Panel.title('Panel with relative time override')
+ .should('be.visible')
+ .within(() => {
+ e2e().contains('[role="row"]', '00:00:00').should('be.visible');
+ });
+
+ e2e.components.Panels.Panel.title('Panel in timezone')
+ .should('be.visible')
+ .within(() => {
+ e2e().contains('[role="row"]', '00:00:00').should('be.visible');
+ });
+
+ // Test LA timezone
+ e2e.flows.setTimeRange({
+ from: 'now-6h',
+ to: 'now',
+ zone: 'America/Los_Angeles',
+ });
+ // Need to wait for 2 calls as there's 2 panels
+ e2e().wait(['@dataQuery', '@dataQuery']);
+
+ e2e.components.Panels.Panel.title('Panel with relative time override')
+ .should('be.visible')
+ .within(() => {
+ e2e().contains('[role="row"]', '00:00:00').should('be.visible');
+ });
+
+ // Today so far, still in LA timezone
+ e2e.flows.setTimeRange({
+ from: 'now/d',
+ to: 'now',
+ });
+ // Need to wait for 2 calls as there's 2 panels
+ e2e().wait(['@dataQuery', '@dataQuery']);
+
+ e2e.components.Panels.Panel.title('Panel with relative time override')
+ .should('be.visible')
+ .within(() => {
+ e2e().contains('[role="row"]', '00:00:00').should('be.visible');
+ });
+
+ e2e.components.Panels.Panel.title('Panel in timezone')
+ .should('be.visible')
+ .within(() => {
+ e2e().contains('[role="row"]', '00:00:00').should('be.visible');
+ });
+ },
+});
+
const isTimeCorrect = (inUtc: string, inTz: string, offset: number): boolean => {
if (inUtc === inTz) {
// we need to catch issues when timezone isn't changed for some reason like https://github.com/grafana/grafana/issues/35504
diff --git a/packages/grafana-data/src/datetime/moment_wrapper.ts b/packages/grafana-data/src/datetime/moment_wrapper.ts
index 73f5850dde5..33f53fb0a17 100644
--- a/packages/grafana-data/src/datetime/moment_wrapper.ts
+++ b/packages/grafana-data/src/datetime/moment_wrapper.ts
@@ -130,6 +130,20 @@ export const dateTimeForTimeZone = (
return toUtc(input, formatInput);
}
+ if (timezone && timezone !== 'browser') {
+ let result: moment.Moment;
+
+ if (typeof input === 'string' && formatInput) {
+ result = moment.tz(input, formatInput, timezone);
+ } else {
+ result = moment.tz(input, timezone);
+ }
+
+ if (isDateTime(result)) {
+ return result;
+ }
+ }
+
return dateTime(input, formatInput);
};
diff --git a/packages/grafana-e2e-selectors/src/selectors/components.ts b/packages/grafana-e2e-selectors/src/selectors/components.ts
index adef35cd7c4..318446188a9 100644
--- a/packages/grafana-e2e-selectors/src/selectors/components.ts
+++ b/packages/grafana-e2e-selectors/src/selectors/components.ts
@@ -112,6 +112,7 @@ export const Components = {
Table: {
header: 'table header',
footer: 'table-footer',
+ body: 'data-testid table body',
},
},
},
@@ -425,4 +426,7 @@ export const Components = {
annotationsTypeInput: 'annotations-type-input',
annotationsChoosePanelInput: 'choose-panels-input',
},
+ Tooltip: {
+ container: 'data-testid tooltip',
+ },
};
diff --git a/packages/grafana-e2e/src/flows/index.ts b/packages/grafana-e2e/src/flows/index.ts
index e699542788f..e7fbfb3ce82 100644
--- a/packages/grafana-e2e/src/flows/index.ts
+++ b/packages/grafana-e2e/src/flows/index.ts
@@ -11,6 +11,7 @@ export * from './openPanelMenuItem';
export * from './revertAllChanges';
export * from './saveDashboard';
export * from './selectOption';
+export * from './setTimeRange';
export * from './importDashboard';
export * from './importDashboards';
export * from './userPreferences';
diff --git a/packages/grafana-ui/src/components/Table/Table.tsx b/packages/grafana-ui/src/components/Table/Table.tsx
index 7c7bef084a1..1f1ddf6f160 100644
--- a/packages/grafana-ui/src/components/Table/Table.tsx
+++ b/packages/grafana-ui/src/components/Table/Table.tsx
@@ -13,6 +13,7 @@ import {
import { VariableSizeList } from 'react-window';
import { Field, FieldType, ReducerID } from '@grafana/data';
+import { selectors } from '@grafana/e2e-selectors';
import { TableCellHeight } from '@grafana/schema';
import { useTheme2 } from '../../themes';
@@ -330,7 +331,7 @@ export const Table = memo((props: Props) => {