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

vogievetsky pushed a commit to branch segment_timeline2
in repository https://gitbox.apache.org/repos/asf/druid.git

commit ded6fc2a0d66783213c8d9c9d84b36c562e78b03
Author: Vadim Ogievetsky <[email protected]>
AuthorDate: Wed Oct 30 09:24:13 2024 -0700

    checkpoint
---
 .../src/components/segment-timeline/common.ts      |   8 +-
 .../segment-timeline/segment-bar-chart-render.tsx  |  10 +-
 .../segment-timeline/segment-bar-chart.tsx         | 117 ++++++---------------
 .../segment-timeline/segment-timeline.tsx          |  13 ++-
 web-console/src/utils/date.spec.ts                 |   9 --
 web-console/src/utils/date.ts                      |  89 ----------------
 web-console/src/utils/duration/duration.ts         |  18 +++-
 .../explore-view/utils/get-auto-granularity.ts     |  80 ++++++--------
 .../time-menu-items/time-menu-items.tsx            |  28 ++---
 9 files changed, 98 insertions(+), 274 deletions(-)

diff --git a/web-console/src/components/segment-timeline/common.ts 
b/web-console/src/components/segment-timeline/common.ts
index a1907846f29..bfb6d84e5f3 100644
--- a/web-console/src/components/segment-timeline/common.ts
+++ b/web-console/src/components/segment-timeline/common.ts
@@ -30,18 +30,18 @@ export function aggregateSegmentStats(
   };
 }
 
-export interface SegmentRow extends Record<SegmentStat, number> {
+export interface IntervalRow extends Record<SegmentStat, number> {
   start: Date;
   end: Date;
   durationSeconds: number;
-  datasource?: string;
+  datasource: string;
 }
 
-export interface SegmentBar extends SegmentRow {
+export interface SegmentBar extends IntervalRow {
   offset: Record<SegmentStat, number>;
 }
 
-export function normalizedSegmentRow(sr: SegmentRow): SegmentRow {
+export function normalizedSegmentRow(sr: IntervalRow): IntervalRow {
   return {
     ...sr,
     count: sr.count / sr.durationSeconds,
diff --git 
a/web-console/src/components/segment-timeline/segment-bar-chart-render.tsx 
b/web-console/src/components/segment-timeline/segment-bar-chart-render.tsx
index f1e67c9665a..7864021622d 100644
--- a/web-console/src/components/segment-timeline/segment-bar-chart-render.tsx
+++ b/web-console/src/components/segment-timeline/segment-bar-chart-render.tsx
@@ -27,13 +27,13 @@ import { useMemo, useRef, useState } from 'react';
 import { useGlobalEventListener } from '../../hooks';
 import {
   capitalizeFirst,
-  ceilDay,
   clamp,
-  floorDay,
+  day,
   formatByteRate,
   formatBytes,
   formatInteger,
   formatNumber,
+  TZ_UTC,
 } from '../../utils';
 import type { Margin, Stage } from '../../utils/stage';
 
@@ -105,7 +105,7 @@ export const SegmentBarChartRender = function 
SegmentBarChartRender(
 
   const colorizer = useMemo(() => {
     const s = scaleOrdinal().range(COLORS);
-    return (d: SegmentBar) => (d.datasource ? s(d.datasource) : COLORS[0]) as 
string;
+    return (d: SegmentBar) => s(d.datasource) as string;
   }, []);
 
   const maxStat = max(segmentBars, d => d[shownSegmentStat] + 
d.offset[shownSegmentStat]);
@@ -160,9 +160,9 @@ export const SegmentBarChartRender = function 
SegmentBarChartRender(
       setShiftOffset(mouseDownAt.time.valueOf() - b.valueOf());
     } else {
       if (mouseDownAt.time < b) {
-        setDragging([floorDay(mouseDownAt.time), ceilDay(b)]);
+        setDragging([day.floor(mouseDownAt.time, TZ_UTC), day.ceil(b, 
TZ_UTC)]);
       } else {
-        setDragging([floorDay(b), ceilDay(mouseDownAt.time)]);
+        setDragging([day.floor(b, TZ_UTC), day.ceil(mouseDownAt.time, 
TZ_UTC)]);
       }
     }
   });
diff --git a/web-console/src/components/segment-timeline/segment-bar-chart.tsx 
b/web-console/src/components/segment-timeline/segment-bar-chart.tsx
index 4b24849b723..3ba9b16f082 100644
--- a/web-console/src/components/segment-timeline/segment-bar-chart.tsx
+++ b/web-console/src/components/segment-timeline/segment-bar-chart.tsx
@@ -24,69 +24,17 @@ import { useMemo } from 'react';
 import type { Capabilities } from '../../helpers';
 import { useQueryManager } from '../../hooks';
 import { Api } from '../../singletons';
-import {
-  ceilDay,
-  ceilHour,
-  ceilMonth,
-  ceilYear,
-  filterMap,
-  floorDay,
-  floorHour,
-  floorMonth,
-  floorYear,
-  groupBy,
-  queryDruidSql,
-} from '../../utils';
+import { Duration, filterMap, groupBy, queryDruidSql, TZ_UTC } from 
'../../utils';
 import type { Stage } from '../../utils/stage';
 import { Loader } from '../loader/loader';
 
-import type { SegmentBar, SegmentRow, SegmentStat } from './common';
+import type { IntervalRow, SegmentBar, SegmentStat } from './common';
 import { aggregateSegmentStats, normalizedSegmentRow } from './common';
 import { SegmentBarChartRender } from './segment-bar-chart-render';
 
 import './segment-bar-chart.scss';
 
-type TrimDuration = 'PT1H' | 'P1D' | 'P1M' | 'P1Y';
-
-function floorToDuration(date: Date, duration: TrimDuration): Date {
-  switch (duration) {
-    case 'PT1H':
-      return floorHour(date);
-
-    case 'P1D':
-      return floorDay(date);
-
-    case 'P1M':
-      return floorMonth(date);
-
-    case 'P1Y':
-      return floorYear(date);
-
-    default:
-      throw new Error(`Unexpected duration: ${duration}`);
-  }
-}
-
-function ceilToDuration(date: Date, duration: TrimDuration): Date {
-  switch (duration) {
-    case 'PT1H':
-      return ceilHour(date);
-
-    case 'P1D':
-      return ceilDay(date);
-
-    case 'P1M':
-      return ceilMonth(date);
-
-    case 'P1Y':
-      return ceilYear(date);
-
-    default:
-      throw new Error(`Unexpected duration: ${duration}`);
-  }
-}
-
-function stackSegmentRows(segmentRows: SegmentRow[]): SegmentBar[] {
+function stackIntervalRows(segmentRows: IntervalRow[]): SegmentBar[] {
   const sorted = segmentRows.sort((a, b) => {
     const diff = b.durationSeconds - a.durationSeconds;
     if (diff) return diff;
@@ -99,7 +47,7 @@ function stackSegmentRows(segmentRows: SegmentRow[]): 
SegmentBar[] {
     segmentRow = normalizedSegmentRow(segmentRow);
     const startMs = segmentRow.start.valueOf();
     const endMs = segmentRow.end.valueOf();
-    const segmentRowsBelow = intervalTree.search([startMs + 1, startMs + 2]) 
as SegmentRow[];
+    const segmentRowsBelow = intervalTree.search([startMs + 1, startMs + 2]) 
as IntervalRow[];
     intervalTree.insert([startMs, endMs], segmentRow);
     return {
       ...segmentRow,
@@ -113,7 +61,6 @@ interface SegmentBarChartProps {
   stage: Stage;
   dateRange: NonNullDateRange;
   changeDateRange(newDateRange: NonNullDateRange): void;
-  breakByDataSource: boolean;
   shownSegmentStat: SegmentStat;
   changeActiveDatasource: (datasource: string | undefined) => void;
 }
@@ -123,22 +70,18 @@ export const SegmentBarChart = function 
SegmentBarChart(props: SegmentBarChartPr
     capabilities,
     dateRange,
     changeDateRange,
-    breakByDataSource,
     stage,
     shownSegmentStat,
     changeActiveDatasource,
   } = props;
 
-  const intervalsQuery = useMemo(
-    () => ({ capabilities, dateRange, breakByDataSource }),
-    [capabilities, dateRange, breakByDataSource],
-  );
+  const intervalsQuery = useMemo(() => ({ capabilities, dateRange }), 
[capabilities, dateRange]);
 
-  const [segmentBarsState] = useQueryManager({
+  const [intervalBarsState] = useQueryManager({
     query: intervalsQuery,
-    processQuery: async ({ capabilities, dateRange, breakByDataSource }, 
cancelToken) => {
-      const trimDuration: TrimDuration = 'PT1H';
-      let segmentRows: SegmentRow[];
+    processQuery: async ({ capabilities, dateRange }, cancelToken) => {
+      const trimDuration = new Duration('PT1H');
+      let intervalRows: IntervalRow[];
       if (capabilities.hasSql()) {
         const query = SqlQuery.from(N('sys').table('segments'))
           .changeWhereExpression(
@@ -146,14 +89,14 @@ export const SegmentBarChart = function 
SegmentBarChart(props: SegmentBarChartPr
           )
           .addSelect(C('start'), { addToGroupBy: 'end' })
           .addSelect(C('end'), { addToGroupBy: 'end' })
-          .applyIf(breakByDataSource, q => q.addSelect(C('datasource'), { 
addToGroupBy: 'end' }))
+          .addSelect(C('datasource'), { addToGroupBy: 'end' })
           .addSelect(F.count().as('count'))
           .addSelect(F.sum(C('size')).as('size'))
           .addSelect(F.sum(C('num_rows')).as('rows'));
 
-        segmentRows = (await queryDruidSql({ query: query.toString() 
})).map(sr => {
-          const start = floorToDuration(new Date(sr.start), trimDuration);
-          const end = ceilToDuration(new Date(sr.end), trimDuration);
+        intervalRows = (await queryDruidSql({ query: query.toString() 
})).map(sr => {
+          const start = trimDuration.floor(new Date(sr.start), TZ_UTC);
+          const end = trimDuration.ceil(new Date(sr.end), TZ_UTC);
           return {
             ...sr,
             start,
@@ -165,7 +108,7 @@ export const SegmentBarChart = function 
SegmentBarChart(props: SegmentBarChartPr
         const datasources: string[] = (
           await Api.instance.get(`/druid/coordinator/v1/datasources`, { 
cancelToken })
         ).data;
-        segmentRows = (
+        intervalRows = (
           await Promise.all(
             datasources.map(async datasource => {
               const intervalMap = (
@@ -180,14 +123,14 @@ export const SegmentBarChart = function 
SegmentBarChart(props: SegmentBarChartPr
               return filterMap(Object.entries(intervalMap), ([interval, v]) => 
{
                 // ToDo: Filter on start end
                 const [startStr, endStr] = interval.split('/');
-                const start = floorToDuration(new Date(startStr), 
trimDuration);
-                const end = ceilToDuration(new Date(endStr), trimDuration);
+                const start = trimDuration.floor(new Date(startStr), TZ_UTC);
+                const end = trimDuration.ceil(new Date(endStr), TZ_UTC);
                 const { count, size, rows } = v as any;
                 return {
                   start,
                   end,
                   durationSeconds: (end.valueOf() - start.valueOf()) / 1000,
-                  datasource: breakByDataSource ? datasource : undefined,
+                  datasource,
                   count,
                   size,
                   rows,
@@ -199,38 +142,38 @@ export const SegmentBarChart = function 
SegmentBarChart(props: SegmentBarChartPr
       }
 
       const fullyGroupedSegmentRows = groupBy(
-        segmentRows,
-        segmentRow =>
+        intervalRows,
+        intervalRow =>
           [
-            segmentRow.start.toISOString(),
-            segmentRow.end.toISOString(),
-            segmentRow.datasource || '',
+            intervalRow.start.toISOString(),
+            intervalRow.end.toISOString(),
+            intervalRow.datasource || '',
           ].join('/'),
-        (segmentRows): SegmentRow => {
+        (intervalRows): IntervalRow => {
           return {
-            ...segmentRows[0],
-            ...aggregateSegmentStats(segmentRows),
+            ...intervalRows[0],
+            ...aggregateSegmentStats(intervalRows),
           };
         },
       );
 
-      return stackSegmentRows(fullyGroupedSegmentRows);
+      return stackIntervalRows(fullyGroupedSegmentRows);
     },
   });
 
-  if (segmentBarsState.loading) {
+  if (intervalBarsState.loading) {
     return <Loader />;
   }
 
-  if (segmentBarsState.error) {
+  if (intervalBarsState.error) {
     return (
       <div className="empty-placeholder">
-        <span className="no-data-text">{`Error when loading data: 
${segmentBarsState.getErrorMessage()}`}</span>
+        <span className="no-data-text">{`Error when loading data: 
${intervalBarsState.getErrorMessage()}`}</span>
       </div>
     );
   }
 
-  const segmentBars = segmentBarsState.data;
+  const segmentBars = intervalBarsState.data;
   if (!segmentBars) return null;
 
   return (
diff --git a/web-console/src/components/segment-timeline/segment-timeline.tsx 
b/web-console/src/components/segment-timeline/segment-timeline.tsx
index 10118c30f9d..db6653c912b 100644
--- a/web-console/src/components/segment-timeline/segment-timeline.tsx
+++ b/web-console/src/components/segment-timeline/segment-timeline.tsx
@@ -27,9 +27,11 @@ import { useState } from 'react';
 
 import type { Capabilities } from '../../helpers';
 import {
-  ceilToUtcDay,
+  day,
   isNonNullRange,
   localToUtcDateRange,
+  month,
+  TZ_UTC,
   utcToLocalDateRange,
 } from '../../utils';
 import { Stage } from '../../utils/stage';
@@ -47,10 +49,8 @@ interface SegmentTimelineProps {
 const DEFAULT_TIME_SPAN_MONTHS = 3;
 
 function getDefaultDateRange(): NonNullDateRange {
-  const start = ceilToUtcDay(new Date());
-  const end = new Date(start.valueOf());
-  start.setUTCMonth(start.getUTCMonth() - DEFAULT_TIME_SPAN_MONTHS);
-  return [start, end];
+  const end = day.ceil(new Date(), TZ_UTC);
+  return [month.shift(end, TZ_UTC, -DEFAULT_TIME_SPAN_MONTHS), end];
 }
 
 export const SegmentTimeline = function SegmentTimeline(props: 
SegmentTimelineProps) {
@@ -60,7 +60,7 @@ export const SegmentTimeline = function 
SegmentTimeline(props: SegmentTimelinePr
   const [activeDatasource, setActiveDatasource] = useState<string | 
undefined>();
   const [dateRange, setDateRange] = 
useState<NonNullDateRange>(getDefaultDateRange);
 
-  const datasources: string[] = ['wiki', 'kttm'];
+  const datasources: string[] = ['wiki', 'kttm']; // ToDo
 
   const DatasourceSelect: React.FC = () => {
     const showAll = 'Show all';
@@ -128,7 +128,6 @@ export const SegmentTimeline = function 
SegmentTimeline(props: SegmentTimelinePr
               dateRange={dateRange}
               changeDateRange={setDateRange}
               shownSegmentStat={activeSegmentStat}
-              breakByDataSource
               changeActiveDatasource={(datasource: string | undefined) =>
                 setActiveDatasource(activeDatasource ? undefined : datasource)
               }
diff --git a/web-console/src/utils/date.spec.ts 
b/web-console/src/utils/date.spec.ts
index 843c144244e..b219ee17af0 100644
--- a/web-console/src/utils/date.spec.ts
+++ b/web-console/src/utils/date.spec.ts
@@ -17,7 +17,6 @@
  */
 
 import {
-  ceilToUtcDay,
   dateToIsoDateString,
   intervalToLocalDateRange,
   localDateRangeToInterval,
@@ -60,12 +59,4 @@ describe('date', () => {
       
expect(localDateRangeToInterval(intervalToLocalDateRange(interval))).toEqual(interval);
     });
   });
-
-  describe('ceilToUtcDay', () => {
-    it('works', () => {
-      expect(ceilToUtcDay(new Date('2021-02-03T12:03:02.001Z'))).toEqual(
-        new Date('2021-02-04T00:00:00Z'),
-      );
-    });
-  });
 });
diff --git a/web-console/src/utils/date.ts b/web-console/src/utils/date.ts
index 897618c030b..e59c01a74df 100644
--- a/web-console/src/utils/date.ts
+++ b/web-console/src/utils/date.ts
@@ -98,92 +98,3 @@ export function localDateRangeToInterval(localRange: 
DateRange): string {
     localEndDate ? localToUtcDate(localEndDate).toISOString().slice(0, 19) : ''
   }`;
 }
-
-export function ceilToUtcDay(date: Date): Date {
-  date = new Date(date.valueOf());
-  date.setUTCHours(0, 0, 0, 0);
-  date.setUTCDate(date.getUTCDate() + 1);
-  return date;
-}
-
-// ------------------------------------
-
-export function floorHour(dt: Date): Date {
-  dt = new Date(dt.valueOf());
-  dt.setUTCMinutes(0, 0, 0);
-  return dt;
-}
-
-export function nextHour(dt: Date): Date {
-  dt = new Date(dt.valueOf());
-  dt.setUTCHours(dt.getUTCHours() + 1);
-  return dt;
-}
-
-export function ceilHour(dt: Date): Date {
-  const floor = floorHour(dt);
-  if (floor.valueOf() === dt.valueOf()) return dt;
-  return nextHour(floor);
-}
-
-// ------------------------------------
-
-export function floorDay(dt: Date): Date {
-  dt = new Date(dt.valueOf());
-  dt.setUTCHours(0, 0, 0, 0);
-  return dt;
-}
-
-export function nextDay(dt: Date): Date {
-  dt = new Date(dt.valueOf());
-  dt.setUTCDate(dt.getUTCDate() + 1);
-  return dt;
-}
-
-export function ceilDay(dt: Date): Date {
-  const floor = floorDay(dt);
-  if (floor.valueOf() === dt.valueOf()) return dt;
-  return nextDay(floor);
-}
-
-// ------------------------------------
-
-export function floorMonth(dt: Date): Date {
-  dt = new Date(dt.valueOf());
-  dt.setUTCHours(0, 0, 0, 0);
-  dt.setUTCDate(1);
-  return dt;
-}
-
-export function nextMonth(dt: Date): Date {
-  dt = new Date(dt.valueOf());
-  dt.setUTCMonth(dt.getUTCMonth() + 1);
-  return dt;
-}
-
-export function ceilMonth(dt: Date): Date {
-  const floor = floorMonth(dt);
-  if (floor.valueOf() === dt.valueOf()) return dt;
-  return nextMonth(floor);
-}
-
-// ------------------------------------
-
-export function floorYear(dt: Date): Date {
-  dt = new Date(dt.valueOf());
-  dt.setUTCHours(0, 0, 0, 0);
-  dt.setUTCMonth(0, 1);
-  return dt;
-}
-
-export function nextYear(dt: Date): Date {
-  dt = new Date(dt.valueOf());
-  dt.setUTCFullYear(dt.getUTCFullYear() + 1);
-  return dt;
-}
-
-export function ceilYear(dt: Date): Date {
-  const floor = floorYear(dt);
-  if (floor.valueOf() === dt.valueOf()) return dt;
-  return nextYear(floor);
-}
diff --git a/web-console/src/utils/duration/duration.ts 
b/web-console/src/utils/duration/duration.ts
index b93dab175ad..e582556b3f1 100755
--- a/web-console/src/utils/duration/duration.ts
+++ b/web-console/src/utils/duration/duration.ts
@@ -19,6 +19,8 @@
 import { second, shifters } from 
'../date-floor-shift-ceil/date-floor-shift-ceil';
 import { capitalizeFirst, pluralIfNeeded } from '../general';
 
+export const TZ_UTC = 'Etc/UTC';
+
 const SPANS_WITH_WEEK = ['year', 'month', 'week', 'day', 'hour', 'minute', 
'second'];
 const SPANS_WITHOUT_WEEK = ['year', 'month', 'day', 'hour', 'minute', 
'second'];
 const SPANS_WITHOUT_WEEK_OR_MONTH = ['year', 'day', 'hour', 'minute', 
'second'];
@@ -139,11 +141,6 @@ export class Duration {
   public readonly singleSpan?: string;
   public readonly spans: Readonly<DurationValue>;
 
-  static parse(durationStr: string): Duration {
-    if (typeof durationStr !== 'string') throw new TypeError('Duration JS must 
be a string');
-    return new Duration(getSpansFromString(durationStr));
-  }
-
   static fromCanonicalLength(length: number, skipMonths = false): Duration {
     if (length <= 0) throw new Error('length must be positive');
     let spans = fitIntoSpans(length, skipMonths ? SPANS_WITHOUT_WEEK_OR_MONTH 
: SPANS_WITHOUT_WEEK);
@@ -168,6 +165,17 @@ export class Duration {
     return new Duration(getSpansFromStartEnd(start, end, timezone));
   }
 
+  static pickSmallestGranularityThatFits(
+    granularities: Duration[],
+    span: number,
+    maxEntities: number,
+  ): Duration {
+    for (const granularity of granularities) {
+      if (span / granularity.getCanonicalLength() < maxEntities) return 
granularity;
+    }
+    return granularities[granularities.length - 1];
+  }
+
   constructor(spans: DurationValue | string) {
     const effectiveSpans: DurationValue =
       typeof spans === 'string' ? getSpansFromString(spans) : 
removeZeros(spans);
diff --git a/web-console/src/views/explore-view/utils/get-auto-granularity.ts 
b/web-console/src/views/explore-view/utils/get-auto-granularity.ts
index 21cc2743820..34c3f27b4a6 100644
--- a/web-console/src/views/explore-view/utils/get-auto-granularity.ts
+++ b/web-console/src/views/explore-view/utils/get-auto-granularity.ts
@@ -17,65 +17,47 @@
  */
 
 import type { SqlExpression } from '@druid-toolkit/query';
-import { fitFilterPattern, SqlMulti } from '@druid-toolkit/query';
+import { fitFilterPatterns } from '@druid-toolkit/query';
 
-import { day, Duration, hour } from '../../../utils';
+import { Duration } from '../../../utils';
 
-function getCanonicalDuration(
+function getTimeSpanInExpression(
   expression: SqlExpression,
   timeColumnName: string,
 ): number | undefined {
-  const pattern = fitFilterPattern(expression);
-  if ('column' in pattern && pattern.column !== timeColumnName) return 
undefined;
-
-  switch (pattern.type) {
-    case 'timeInterval':
+  const patterns = fitFilterPatterns(expression);
+  for (const pattern of patterns) {
+    if (pattern.type === 'timeInterval' && pattern.column === timeColumnName) {
       return pattern.end.valueOf() - pattern.start.valueOf();
-
-    case 'timeRelative':
+    } else if (pattern.type === 'timeRelative' && pattern.column === 
timeColumnName) {
       return new Duration(pattern.rangeDuration).getCanonicalLength();
-
-    case 'custom':
-      if (pattern.expression instanceof SqlMulti) {
-        for (const value of pattern.expression.args.values) {
-          const canonicalDuration = getCanonicalDuration(value, 
timeColumnName);
-          if (canonicalDuration !== undefined) return canonicalDuration;
-        }
-      }
-
-      break;
-
-    default:
-      break;
+    }
   }
 
-  return undefined;
+  return;
 }
 
-const DEFAULT_GRANULARITY = 'PT1H';
-
-/**
- * Computes the granularity string from a where clause. If the where clause is 
TRUE, the default
- * granularity is returned (PT1H). Otherwise, the granularity is computed from 
the time column and the
- * duration of the where clause.
- *
- * @param where the where SQLExpression to read from
- * @param timeColumnName the name of the time column (any other time column 
will be ignored)
- * @returns the granularity string (default is PT1H)
- */
 export function getAutoGranularity(where: SqlExpression, timeColumnName: 
string): string {
-  if (where.toString() === 'TRUE') return DEFAULT_GRANULARITY;
-
-  const canonicalDuration = getCanonicalDuration(where, timeColumnName);
-
-  if (canonicalDuration) {
-    if (canonicalDuration > day.canonicalLength * 95) return 'P1W';
-    if (canonicalDuration > day.canonicalLength * 8) return 'P1D';
-    if (canonicalDuration > hour.canonicalLength * 8) return 'PT1H';
-    if (canonicalDuration > hour.canonicalLength * 3) return 'PT5M';
-
-    return 'PT1M';
-  }
-
-  return DEFAULT_GRANULARITY;
+  const timeSpan = getTimeSpanInExpression(where, timeColumnName);
+  if (!timeSpan) return 'P1D';
+  return Duration.pickSmallestGranularityThatFits(
+    [
+      'PT1S',
+      'PT5S',
+      'PT20S',
+      'PT1M',
+      'PT5M',
+      'PT20M',
+      'PT1H',
+      'PT3H',
+      'PT6H',
+      'P1D',
+      'P1W',
+      'P1M',
+      'P3M',
+      'P1Y',
+    ].map(s => new Duration(s)),
+    timeSpan,
+    200,
+  ).toString();
 }
diff --git 
a/web-console/src/views/workbench-view/column-tree/column-tree-menu/time-menu-items/time-menu-items.tsx
 
b/web-console/src/views/workbench-view/column-tree/column-tree-menu/time-menu-items/time-menu-items.tsx
index 11b3a67a3cb..f4af22ebaf5 100644
--- 
a/web-console/src/views/workbench-view/column-tree/column-tree-menu/time-menu-items/time-menu-items.tsx
+++ 
b/web-console/src/views/workbench-view/column-tree/column-tree-menu/time-menu-items/time-menu-items.tsx
@@ -23,17 +23,7 @@ import { C, F, SqlExpression } from '@druid-toolkit/query';
 import type { JSX } from 'react';
 import React from 'react';
 
-import {
-  floorDay,
-  floorHour,
-  floorMonth,
-  floorYear,
-  nextDay,
-  nextHour,
-  nextMonth,
-  nextYear,
-  prettyPrintSql,
-} from '../../../../../utils';
+import { day, hour, month, prettyPrintSql, TZ_UTC, year } from 
'../../../../../utils';
 
 const LATEST_HOUR: SqlExpression = SqlExpression.parse(
   `? >= CURRENT_TIMESTAMP - INTERVAL '1' HOUR`,
@@ -87,10 +77,10 @@ export const TimeMenuItems = React.memo(function 
TimeMenuItems(props: TimeMenuIt
     }
 
     const now = new Date();
-    const hourStart = floorHour(now);
-    const dayStart = floorDay(now);
-    const monthStart = floorMonth(now);
-    const yearStart = floorYear(now);
+    const hourStart = hour.floor(now, TZ_UTC);
+    const dayStart = day.floor(now, TZ_UTC);
+    const monthStart = month.floor(now, TZ_UTC);
+    const yearStart = year.floor(now, TZ_UTC);
     return (
       <MenuItem icon={IconNames.FILTER} text="Filter">
         {filterMenuItem(`Latest hour`, fillWithColumn(LATEST_HOUR, 
columnName))}
@@ -101,19 +91,19 @@ export const TimeMenuItems = React.memo(function 
TimeMenuItems(props: TimeMenuIt
         <MenuDivider />
         {filterMenuItem(
           `Current hour`,
-          fillWithColumnStartEnd(columnName, hourStart, nextHour(hourStart)),
+          fillWithColumnStartEnd(columnName, hourStart, hour.shift(hourStart, 
TZ_UTC, 1)),
         )}
         {filterMenuItem(
           `Current day`,
-          fillWithColumnStartEnd(columnName, dayStart, nextDay(dayStart)),
+          fillWithColumnStartEnd(columnName, dayStart, day.shift(dayStart, 
TZ_UTC, 1)),
         )}
         {filterMenuItem(
           `Current month`,
-          fillWithColumnStartEnd(columnName, monthStart, 
nextMonth(monthStart)),
+          fillWithColumnStartEnd(columnName, monthStart, 
month.shift(monthStart, TZ_UTC, 1)),
         )}
         {filterMenuItem(
           `Current year`,
-          fillWithColumnStartEnd(columnName, yearStart, nextYear(yearStart)),
+          fillWithColumnStartEnd(columnName, yearStart, year.shift(yearStart, 
TZ_UTC, 1)),
         )}
       </MenuItem>
     );


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to