From e660ea25331b092ecb5976254abfc45a413f07d5 Mon Sep 17 00:00:00 2001 From: Kamil Gabryjelski Date: Tue, 7 Dec 2021 14:29:12 +0100 Subject: [PATCH] fix(legacy-plugin-chart-calendar): fix timestamp timezone in Calendar (#17664) * fix(legacy-plugin-chart-calendar): fix timestamp timezone in Calendar * Fix prop type --- .../src/Calendar.js | 44 +++---------------- .../src/transformProps.js | 10 ++++- .../legacy-plugin-chart-calendar/src/utils.ts | 30 +++++++++++++ .../test/getFormattedUTCTime.ts | 28 ++++++++++++ 4 files changed, 73 insertions(+), 39 deletions(-) create mode 100644 superset-frontend/plugins/legacy-plugin-chart-calendar/src/utils.ts create mode 100644 superset-frontend/plugins/legacy-plugin-chart-calendar/test/getFormattedUTCTime.ts diff --git a/superset-frontend/plugins/legacy-plugin-chart-calendar/src/Calendar.js b/superset-frontend/plugins/legacy-plugin-chart-calendar/src/Calendar.js index 6b701c231a..a1adc28189 100644 --- a/superset-frontend/plugins/legacy-plugin-chart-calendar/src/Calendar.js +++ b/superset-frontend/plugins/legacy-plugin-chart-calendar/src/Calendar.js @@ -19,27 +19,10 @@ import PropTypes from 'prop-types'; import { extent as d3Extent, range as d3Range } from 'd3-array'; import { select as d3Select } from 'd3-selection'; -import { - getNumberFormatter, - getTimeFormatter, - getSequentialSchemeRegistry, -} from '@superset-ui/core'; +import { getSequentialSchemeRegistry } from '@superset-ui/core'; import CalHeatMap from './vendor/cal-heatmap'; import './vendor/cal-heatmap.css'; -function convertUTC(dttm) { - return new Date( - dttm.getUTCFullYear(), - dttm.getUTCMonth(), - dttm.getUTCDate(), - dttm.getUTCHours(), - dttm.getUTCMinutes(), - dttm.getUTCSeconds(), - ); -} - -const convertUTCTS = uts => convertUTC(new Date(uts)).getTime(); - const propTypes = { data: PropTypes.shape({ // Object hashed by metric name, @@ -65,8 +48,8 @@ const propTypes = { showMetricName: PropTypes.bool, showValues: PropTypes.bool, steps: PropTypes.number, - timeFormat: PropTypes.string, - valueFormat: PropTypes.string, + timeFormatter: PropTypes.func, + valueFormatter: PropTypes.func, verboseMap: PropTypes.object, }; @@ -84,14 +67,11 @@ function Calendar(element, props) { showValues, steps, subdomainGranularity, - timeFormat, - valueFormat, + timeFormatter, + valueFormatter, verboseMap, } = props; - const valueFormatter = getNumberFormatter(valueFormat); - const timeFormatter = getTimeFormatter(timeFormat); - const container = d3Select(element) .classed('superset-legacy-chart-calendar', true) .style('height', height); @@ -102,17 +82,7 @@ function Calendar(element, props) { ? (date, value) => valueFormatter(value) : null; - // Trick to convert all timestamps to UTC - // TODO: Verify if this conversion is really necessary - // since all timestamps should always be in UTC. - const metricsData = {}; - Object.keys(data.data).forEach(metric => { - metricsData[metric] = {}; - Object.keys(data.data[metric]).forEach(ts => { - metricsData[metric][convertUTCTS(ts * 1000) / 1000] = - data.data[metric][ts]; - }); - }); + const metricsData = data.data; Object.keys(metricsData).forEach(metric => { const calContainer = div.append('div'); @@ -131,7 +101,7 @@ function Calendar(element, props) { const cal = new CalHeatMap(); cal.init({ - start: convertUTCTS(data.start), + start: data.start, data: timestamps, itemSelector: calContainer.node(), legendVerticalPosition: 'top', diff --git a/superset-frontend/plugins/legacy-plugin-chart-calendar/src/transformProps.js b/superset-frontend/plugins/legacy-plugin-chart-calendar/src/transformProps.js index cfd57d1e27..379a206f51 100644 --- a/superset-frontend/plugins/legacy-plugin-chart-calendar/src/transformProps.js +++ b/superset-frontend/plugins/legacy-plugin-chart-calendar/src/transformProps.js @@ -16,6 +16,10 @@ * specific language governing permissions and limitations * under the License. */ + +import { getNumberFormatter } from '@superset-ui/core'; +import { getFormattedUTCTime } from './utils'; + export default function transformProps(chartProps) { const { height, formData, queriesData, datasource } = chartProps; const { @@ -34,6 +38,8 @@ export default function transformProps(chartProps) { } = formData; const { verboseMap } = datasource; + const timeFormatter = ts => getFormattedUTCTime(ts, xAxisTimeFormat); + const valueFormatter = getNumberFormatter(yAxisFormat); return { height, @@ -48,8 +54,8 @@ export default function transformProps(chartProps) { showValues, steps, subdomainGranularity, - timeFormat: xAxisTimeFormat, - valueFormat: yAxisFormat, + timeFormatter, + valueFormatter, verboseMap, }; } diff --git a/superset-frontend/plugins/legacy-plugin-chart-calendar/src/utils.ts b/superset-frontend/plugins/legacy-plugin-chart-calendar/src/utils.ts new file mode 100644 index 0000000000..9f3c0bee1b --- /dev/null +++ b/superset-frontend/plugins/legacy-plugin-chart-calendar/src/utils.ts @@ -0,0 +1,30 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { getTimeFormatter } from '@superset-ui/core'; + +// Assume that given timestamp is UTC +export const getFormattedUTCTime = ( + ts: number | string, + timeFormat?: string, +) => { + const date = new Date(ts); + const offset = date.getTimezoneOffset() * 60 * 1000; + return getTimeFormatter(timeFormat)(date.getTime() - offset); +}; diff --git a/superset-frontend/plugins/legacy-plugin-chart-calendar/test/getFormattedUTCTime.ts b/superset-frontend/plugins/legacy-plugin-chart-calendar/test/getFormattedUTCTime.ts new file mode 100644 index 0000000000..535542e868 --- /dev/null +++ b/superset-frontend/plugins/legacy-plugin-chart-calendar/test/getFormattedUTCTime.ts @@ -0,0 +1,28 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { getFormattedUTCTime } from '../src/utils'; + +describe('getFormattedUTCTime', () => { + it('formatted date string should equal to UTC date', () => { + const ts = 1420070400000; // 2015.01.01 00:00:00 UTC + const formattedTime = getFormattedUTCTime(ts, '%Y-%m-%d %H:%M:%S'); + expect(formattedTime).toEqual('2015-01-01 00:00:00'); + }); +});