This is an automated email from the ASF dual-hosted git repository.

michaelsmolina pushed a commit to branch 3.0
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 8dfe95f89dc1f1bdc2f89f4f986ff13ccebf63bd
Author: Beto Dealmeida <robe...@dealmeida.net>
AuthorDate: Wed Sep 27 09:52:36 2023 -0700

    fix: smarter date formatter (#25404)
    
    (cherry picked from commit f0080f9c559c407c5d06e03db27f2cc40fb227e2)
---
 superset-frontend/jest.config.js                   |  3 +
 .../formatters/finestTemporalGrain.test.ts         | 63 +++++++++++++++++
 .../time-format/formatters/finestTemporalGrain.ts  | 80 ++++++++++++++++++++++
 .../superset-ui-core/src/time-format/index.ts      |  1 +
 .../components/Select/SelectFilterPlugin.tsx       |  6 +-
 5 files changed, 150 insertions(+), 3 deletions(-)

diff --git a/superset-frontend/jest.config.js b/superset-frontend/jest.config.js
index 24e4886ecd..316102c5c2 100644
--- a/superset-frontend/jest.config.js
+++ b/superset-frontend/jest.config.js
@@ -17,6 +17,9 @@
  * under the License.
  */
 
+// timezone for unit tests
+process.env.TZ = 'America/New_York';
+
 module.exports = {
   testRegex:
     
'\\/superset-frontend\\/(spec|src|plugins|packages|tools)\\/.*(_spec|\\.test)\\.[jt]sx?$',
diff --git 
a/superset-frontend/packages/superset-ui-core/src/time-format/formatters/finestTemporalGrain.test.ts
 
b/superset-frontend/packages/superset-ui-core/src/time-format/formatters/finestTemporalGrain.test.ts
new file mode 100644
index 0000000000..6e4f07df4b
--- /dev/null
+++ 
b/superset-frontend/packages/superset-ui-core/src/time-format/formatters/finestTemporalGrain.test.ts
@@ -0,0 +1,63 @@
+/*
+ * 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 finestTemporalGrain from './finestTemporalGrain';
+
+test('finestTemporalGrain', () => {
+  const monthFormatter = finestTemporalGrain([
+    new Date('2003-01-01 00:00:00Z').getTime(),
+    new Date('2003-02-01 00:00:00Z').getTime(),
+  ]);
+  expect(monthFormatter(new Date('2003-01-01 00:00:00Z').getTime())).toBe(
+    '2003-01-01',
+  );
+  expect(monthFormatter(new Date('2003-02-01 00:00:00Z').getTime())).toBe(
+    '2003-02-01',
+  );
+
+  const yearFormatter = finestTemporalGrain([
+    new Date('2003-01-01 00:00:00Z').getTime(),
+    new Date('2004-01-01 00:00:00Z').getTime(),
+  ]);
+  expect(yearFormatter(new Date('2003-01-01 00:00:00Z').getTime())).toBe(
+    '2003',
+  );
+  expect(yearFormatter(new Date('2004-01-01 00:00:00Z').getTime())).toBe(
+    '2004',
+  );
+
+  const milliSecondFormatter = finestTemporalGrain([
+    new Date('2003-01-01 00:00:00Z').getTime(),
+    new Date('2003-04-05 06:07:08.123Z').getTime(),
+  ]);
+  expect(milliSecondFormatter(new Date('2003-01-01 
00:00:00Z').getTime())).toBe(
+    '2003-01-01 00:00:00.000',
+  );
+
+  const localTimeFormatter = finestTemporalGrain(
+    [
+      new Date('2003-01-01 00:00:00Z').getTime(),
+      new Date('2003-02-01 00:00:00Z').getTime(),
+    ],
+    true,
+  );
+  expect(localTimeFormatter(new Date('2003-01-01 00:00:00Z').getTime())).toBe(
+    '2002-12-31 19:00',
+  );
+});
diff --git 
a/superset-frontend/packages/superset-ui-core/src/time-format/formatters/finestTemporalGrain.ts
 
b/superset-frontend/packages/superset-ui-core/src/time-format/formatters/finestTemporalGrain.ts
new file mode 100644
index 0000000000..c03b7ec159
--- /dev/null
+++ 
b/superset-frontend/packages/superset-ui-core/src/time-format/formatters/finestTemporalGrain.ts
@@ -0,0 +1,80 @@
+/*
+ * 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 { utcFormat, timeFormat } from 'd3-time-format';
+import { utcUtils, localTimeUtils } from '../utils/d3Time';
+import TimeFormatter from '../TimeFormatter';
+
+/*
+ * A formatter that examines all the values, and uses the finest temporal 
grain.
+ */
+export default function finestTemporalGrain(
+  values: any[],
+  useLocalTime = false,
+) {
+  const format = useLocalTime ? timeFormat : utcFormat;
+
+  const formatMillisecond = format('%Y-%m-%d %H:%M:%S.%L');
+  const formatSecond = format('%Y-%m-%d %H:%M:%S');
+  const formatMinute = format('%Y-%m-%d %H:%M');
+  const formatHour = format('%Y-%m-%d %H:%M');
+  const formatDay = format('%Y-%m-%d');
+  const formatMonth = format('%Y-%m-%d');
+  const formatYear = format('%Y');
+
+  const {
+    hasMillisecond,
+    hasSecond,
+    hasMinute,
+    hasHour,
+    isNotFirstDayOfMonth,
+    isNotFirstMonth,
+  } = useLocalTime ? localTimeUtils : utcUtils;
+
+  let formatFunc = formatYear;
+  values.forEach((value: any) => {
+    if (formatFunc === formatYear && isNotFirstMonth(value)) {
+      formatFunc = formatMonth;
+    }
+    if (formatFunc === formatMonth && isNotFirstDayOfMonth(value)) {
+      formatFunc = formatDay;
+    }
+    if (formatFunc === formatDay && hasHour(value)) {
+      formatFunc = formatHour;
+    }
+    if (formatFunc === formatHour && hasMinute(value)) {
+      formatFunc = formatMinute;
+    }
+    if (formatFunc === formatMinute && hasSecond(value)) {
+      formatFunc = formatSecond;
+    }
+    if (formatFunc === formatSecond && hasMillisecond(value)) {
+      formatFunc = formatMillisecond;
+    }
+  });
+
+  return new TimeFormatter({
+    description:
+      'Use the finest grain in an array of dates to format all dates in the 
array',
+    formatFunc,
+    id: 'finest_temporal_grain',
+    label: 'Format temporal columns with the finest grain',
+    useLocalTime,
+  });
+}
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 53f23f3643..b0d95c1433 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
@@ -35,6 +35,7 @@ export { default as createMultiFormatter } from 
'./factories/createMultiFormatte
 export { default as smartDateFormatter } from './formatters/smartDate';
 export { default as smartDateDetailedFormatter } from 
'./formatters/smartDateDetailed';
 export { default as smartDateVerboseFormatter } from 
'./formatters/smartDateVerbose';
+export { default as finestTemporalGrainFormatter } from 
'./formatters/finestTemporalGrain';
 
 export { default as normalizeTimestamp } from './utils/normalizeTimestamp';
 export { default as denormalizeTimestamp } from './utils/denormalizeTimestamp';
diff --git 
a/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx 
b/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx
index 2c5d919188..bef70e68f3 100644
--- a/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx
+++ b/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx
@@ -26,7 +26,7 @@ import {
   GenericDataType,
   getColumnLabel,
   JsonObject,
-  smartDateDetailedFormatter,
+  finestTemporalGrainFormatter,
   t,
   tn,
 } from '@superset-ui/core';
@@ -117,9 +117,9 @@ export default function PluginFilterSelect(props: 
PluginFilterSelectProps) {
   const labelFormatter = useMemo(
     () =>
       getDataRecordFormatter({
-        timeFormatter: smartDateDetailedFormatter,
+        timeFormatter: finestTemporalGrainFormatter(data.map(el => el.col)),
       }),
-    [],
+    [data],
   );
 
   const updateDataMask = useCallback(

Reply via email to