fix(legacy-plugin-chart-calendar): fix timestamp timezone in Calendar (#17664)

* fix(legacy-plugin-chart-calendar): fix timestamp timezone in Calendar

* Fix prop type
This commit is contained in:
Kamil Gabryjelski 2021-12-07 14:29:12 +01:00 committed by GitHub
parent 2ae83fac86
commit e660ea2533
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 73 additions and 39 deletions

View File

@ -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',

View File

@ -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,
};
}

View File

@ -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);
};

View File

@ -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');
});
});