diff --git a/superset-frontend/packages/superset-ui-core/src/time-format/index.ts b/superset-frontend/packages/superset-ui-core/src/time-format/index.ts index 48ac1a680..b086effd2 100644 --- a/superset-frontend/packages/superset-ui-core/src/time-format/index.ts +++ b/superset-frontend/packages/superset-ui-core/src/time-format/index.ts @@ -36,4 +36,6 @@ export { default as smartDateFormatter } from './formatters/smartDate'; export { default as smartDateDetailedFormatter } from './formatters/smartDateDetailed'; export { default as smartDateVerboseFormatter } from './formatters/smartDateVerbose'; +export { default as normalizeTimestamp } from './utils/normalizeTimestamp'; + export * from './types'; diff --git a/superset-frontend/packages/superset-ui-core/src/time-format/utils/normalizeTimestamp.ts b/superset-frontend/packages/superset-ui-core/src/time-format/utils/normalizeTimestamp.ts new file mode 100644 index 000000000..0e49aee7e --- /dev/null +++ b/superset-frontend/packages/superset-ui-core/src/time-format/utils/normalizeTimestamp.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. + */ + +const TS_REGEX = /(\d{4}-\d{2}-\d{2})[\sT](\d{2}:\d{2}:\d{2}\.?\d*).*/; + +export default function normalizeTimestamp(value: string): string { + const match = value.match(TS_REGEX); + if (match) { + return `${match[1]}T${match[2]}Z`; + } + return value; +} diff --git a/superset-frontend/packages/superset-ui-core/test/time-format/utils/normalizeTimestamp.test.ts b/superset-frontend/packages/superset-ui-core/test/time-format/utils/normalizeTimestamp.test.ts new file mode 100644 index 000000000..6ccdcb574 --- /dev/null +++ b/superset-frontend/packages/superset-ui-core/test/time-format/utils/normalizeTimestamp.test.ts @@ -0,0 +1,43 @@ +/* + * 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 normalizeTimestamp from '../../../src/time-format/utils/normalizeTimestamp'; + +test('normalizeTimestamp should normalize typical timestamps', () => { + expect(normalizeTimestamp('2023-03-11 08:26:52.695 UTC')).toEqual( + '2023-03-11T08:26:52.695Z', + ); + expect(normalizeTimestamp('2023-03-11 08:26:52.695 Europe/Helsinki')).toEqual( + '2023-03-11T08:26:52.695Z', + ); + expect(normalizeTimestamp('2023-03-11T08:26:52.695 UTC')).toEqual( + '2023-03-11T08:26:52.695Z', + ); + expect(normalizeTimestamp('2023-03-11T08:26:52.695')).toEqual( + '2023-03-11T08:26:52.695Z', + ); + expect(normalizeTimestamp('2023-03-11 08:26:52')).toEqual( + '2023-03-11T08:26:52Z', + ); +}); + +test('normalizeTimestamp should return unmatched timestamps as-is', () => { + expect(normalizeTimestamp('abcd')).toEqual('abcd'); + expect(normalizeTimestamp('03/11/2023')).toEqual('03/11/2023'); +}); diff --git a/superset-frontend/plugins/plugin-chart-table/src/utils/DateWithFormatter.ts b/superset-frontend/plugins/plugin-chart-table/src/utils/DateWithFormatter.ts index eef513bca..c92c2ca1a 100644 --- a/superset-frontend/plugins/plugin-chart-table/src/utils/DateWithFormatter.ts +++ b/superset-frontend/plugins/plugin-chart-table/src/utils/DateWithFormatter.ts @@ -16,9 +16,11 @@ * specific language governing permissions and limitations * under the License. */ -import { DataRecordValue, TimeFormatFunction } from '@superset-ui/core'; - -const REGEXP_TIMESTAMP_NO_TIMEZONE = /T(\d{2}:){2}\d{2}$/; +import { + DataRecordValue, + normalizeTimestamp, + TimeFormatFunction, +} from '@superset-ui/core'; /** * Extended Date object with a custom formatter, and retains the original input @@ -31,19 +33,12 @@ export default class DateWithFormatter extends Date { constructor( input: DataRecordValue, - { - formatter = String, - forceUTC = true, - }: { formatter?: TimeFormatFunction; forceUTC?: boolean } = {}, + { formatter = String }: { formatter?: TimeFormatFunction } = {}, ) { let value = input; // assuming timestamps without a timezone is in UTC time - if ( - forceUTC && - typeof value === 'string' && - REGEXP_TIMESTAMP_NO_TIMEZONE.test(value) - ) { - value = `${value}Z`; + if (typeof value === 'string') { + value = normalizeTimestamp(value); } super(value as string);