From bfc27091f37551c28bcaeb58fa5bd6042dc2a143 Mon Sep 17 00:00:00 2001 From: Vadim Ogievetsky Date: Thu, 16 Jan 2025 14:24:25 -0800 Subject: [PATCH 1/2] Query with timezone --- licenses.yaml | 8 +- web-console/package-lock.json | 49 ++++--- web-console/package.json | 4 +- web-console/src/components/index.ts | 1 + .../src/components/table-cell/table-cell.tsx | 14 +- .../timezone-menu-items.spec.tsx.snap | 3 - .../timezone-menu-items.spec.tsx | 2 +- .../timezone-menu-items.tsx | 58 ++++---- web-console/src/utils/date-format.ts | 10 +- web-console/src/utils/date.ts | 24 +--- web-console/src/utils/index.tsx | 1 + web-console/src/utils/ticks.ts | 127 ++++++++++++++++++ .../components/control-pane/control-pane.tsx | 4 + .../filter-pane/filter-menu/filter-menu.tsx | 3 +- .../components/filter-pane/filter-pane.tsx | 7 +- .../generic-output-table.tsx | 5 +- .../components/module-pane/module-pane.tsx | 8 +- .../src/views/explore-view/explore-view.tsx | 61 ++++++++- .../explore-view/models/explore-state.ts | 14 ++ .../module-repository/module-repository.ts | 7 +- .../bar-chart-module/bar-chart-module.tsx | 43 +++--- .../grouping-table-module.tsx | 11 +- .../multi-axis-chart-module.tsx | 32 ++--- .../pie-chart-module/pie-chart-module.tsx | 8 +- .../record-table-module.tsx | 5 +- .../continuous-chart-render.tsx | 58 +++++--- .../time-chart-module/time-chart-module.tsx | 72 +++++----- .../utils/filter-pattern-helpers.ts | 5 +- .../workbench-view/run-panel/run-panel.tsx | 9 +- 29 files changed, 468 insertions(+), 185 deletions(-) rename web-console/src/{views/workbench-view => components}/timezone-menu-items/__snapshots__/timezone-menu-items.spec.tsx.snap (99%) rename web-console/src/{views/workbench-view => components}/timezone-menu-items/timezone-menu-items.spec.tsx (95%) rename web-console/src/{views/workbench-view => components}/timezone-menu-items/timezone-menu-items.tsx (83%) create mode 100644 web-console/src/utils/ticks.ts diff --git a/licenses.yaml b/licenses.yaml index 87faae70ad8a..e285bfe5c865 100644 --- a/licenses.yaml +++ b/licenses.yaml @@ -5465,7 +5465,7 @@ license_category: binary module: web-console license_name: Apache License version 2.0 copyright: Vadim Ogievetsky -version: 1.1.0 +version: 1.2.0 --- @@ -5682,10 +5682,10 @@ license_file_path: licenses/bin/d3-shape.ISC name: "d3-time-format" license_category: binary module: web-console -license_name: BSD-3-Clause License +license_name: ISC License copyright: Mike Bostock -version: 2.2.1 -license_file_path: licenses/bin/d3-time-format.BSD3 +version: 4.1.0 +license_file_path: licenses/bin/d3-time-format.ISC --- diff --git a/web-console/package-lock.json b/web-console/package-lock.json index 06d7528731b2..e1f48743d00c 100644 --- a/web-console/package-lock.json +++ b/web-console/package-lock.json @@ -19,7 +19,7 @@ "@internationalized/date": "^3.5.6", "ace-builds": "~1.5.3", "axios": "^1.7.7", - "chronoshift": "^1.1.0", + "chronoshift": "^1.2.0", "classnames": "^2.2.6", "copy-to-clipboard": "^3.3.3", "d3-array": "^3.2.4", @@ -28,6 +28,7 @@ "d3-scale": "^4.0.2", "d3-selection": "^3.0.0", "d3-shape": "^3.2.0", + "d3-time-format": "^4.1.0", "date-fns": "^2.28.0", "druid-query-toolkit": "^1.0.2", "echarts": "^5.5.1", @@ -63,6 +64,7 @@ "@types/d3-scale": "^4.0.8", "@types/d3-selection": "^3.0.11", "@types/d3-shape": "^3.1.6", + "@types/d3-time-format": "^4.0.3", "@types/enzyme": "^3.10.18", "@types/enzyme-adapter-react-16": "^1.0.9", "@types/file-saver": "^2.0.7", @@ -3812,6 +3814,13 @@ "integrity": "sha512-9MVYlmIgmRR31C5b4FVSWtuMmBHh2mOWQYfl7XAYOa8dsnb7iEmUmRSWSFgXFtkjxO65d7hTUHQC+RhR/9IWFg==", "dev": true }, + "node_modules/@types/d3-time-format": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz", + "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/enzyme": { "version": "3.10.18", "resolved": "https://registry.npmjs.org/@types/enzyme/-/enzyme-3.10.18.tgz", @@ -5831,9 +5840,9 @@ } }, "node_modules/chronoshift": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/chronoshift/-/chronoshift-1.1.0.tgz", - "integrity": "sha512-Mq72wZIn3lF8yyHo2LjOnWir8CXVafHalOXvYN1qvpYAYX9yOyUnlxLtd6W6g74xYDv9lfc/3sNZfY3EmkUwUw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/chronoshift/-/chronoshift-1.2.0.tgz", + "integrity": "sha512-VRmMsk6N1X4q/6xBhzG31qhQP2cAQJXSAvXQIJPZi3ILAXjhvu0Lr0m5dpJKEEsEVGLCnPOQlk7UYwBd2u8lzQ==", "license": "Apache-2.0", "dependencies": { "@internationalized/date": "^3.5.6", @@ -6666,11 +6675,15 @@ "integrity": "sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA==" }, "node_modules/d3-time-format": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.2.1.tgz", - "integrity": "sha512-VA6WqORO1+H1SvSzgl2oT0z3niANh3opa8Cencpen1LFthw/bEX71R/DgjPlWw78J4UHmD0jCPP1W0HpwMkhjg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", "dependencies": { - "d3-time": "1" + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" } }, "node_modules/data-urls": { @@ -20908,6 +20921,12 @@ "integrity": "sha512-9MVYlmIgmRR31C5b4FVSWtuMmBHh2mOWQYfl7XAYOa8dsnb7iEmUmRSWSFgXFtkjxO65d7hTUHQC+RhR/9IWFg==", "dev": true }, + "@types/d3-time-format": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz", + "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==", + "dev": true + }, "@types/enzyme": { "version": "3.10.18", "resolved": "https://registry.npmjs.org/@types/enzyme/-/enzyme-3.10.18.tgz", @@ -22452,9 +22471,9 @@ "dev": true }, "chronoshift": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/chronoshift/-/chronoshift-1.1.0.tgz", - "integrity": "sha512-Mq72wZIn3lF8yyHo2LjOnWir8CXVafHalOXvYN1qvpYAYX9yOyUnlxLtd6W6g74xYDv9lfc/3sNZfY3EmkUwUw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/chronoshift/-/chronoshift-1.2.0.tgz", + "integrity": "sha512-VRmMsk6N1X4q/6xBhzG31qhQP2cAQJXSAvXQIJPZi3ILAXjhvu0Lr0m5dpJKEEsEVGLCnPOQlk7UYwBd2u8lzQ==", "requires": { "@internationalized/date": "^3.5.6", "tslib": "^2.8.1" @@ -23041,11 +23060,11 @@ "integrity": "sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA==" }, "d3-time-format": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.2.1.tgz", - "integrity": "sha512-VA6WqORO1+H1SvSzgl2oT0z3niANh3opa8Cencpen1LFthw/bEX71R/DgjPlWw78J4UHmD0jCPP1W0HpwMkhjg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", "requires": { - "d3-time": "1" + "d3-time": "1 - 3" } }, "data-urls": { diff --git a/web-console/package.json b/web-console/package.json index c33bc6b65538..37e67a7bcd58 100644 --- a/web-console/package.json +++ b/web-console/package.json @@ -60,7 +60,7 @@ "@internationalized/date": "^3.5.6", "ace-builds": "~1.5.3", "axios": "^1.7.7", - "chronoshift": "^1.1.0", + "chronoshift": "^1.2.0", "classnames": "^2.2.6", "copy-to-clipboard": "^3.3.3", "d3-array": "^3.2.4", @@ -69,6 +69,7 @@ "d3-scale": "^4.0.2", "d3-selection": "^3.0.0", "d3-shape": "^3.2.0", + "d3-time-format": "^4.1.0", "date-fns": "^2.28.0", "druid-query-toolkit": "^1.0.2", "echarts": "^5.5.1", @@ -104,6 +105,7 @@ "@types/d3-scale": "^4.0.8", "@types/d3-selection": "^3.0.11", "@types/d3-shape": "^3.1.6", + "@types/d3-time-format": "^4.0.3", "@types/enzyme": "^3.10.18", "@types/enzyme-adapter-react-16": "^1.0.9", "@types/file-saver": "^2.0.7", diff --git a/web-console/src/components/index.ts b/web-console/src/components/index.ts index 18a4c21e72c2..60f35c51191c 100644 --- a/web-console/src/components/index.ts +++ b/web-console/src/components/index.ts @@ -61,5 +61,6 @@ export * from './table-clickable-cell/table-clickable-cell'; export * from './table-column-selector/table-column-selector'; export * from './table-filterable-cell/table-filterable-cell'; export * from './timed-button/timed-button'; +export * from './timezone-menu-items/timezone-menu-items'; export * from './view-control-bar/view-control-bar'; export * from './warning-checklist/warning-checklist'; diff --git a/web-console/src/components/table-cell/table-cell.tsx b/web-console/src/components/table-cell/table-cell.tsx index 6bd53271d6a9..efe7bb6468ae 100644 --- a/web-console/src/components/table-cell/table-cell.tsx +++ b/web-console/src/components/table-cell/table-cell.tsx @@ -17,11 +17,16 @@ */ import { IconNames } from '@blueprintjs/icons'; +import { Timezone } from 'chronoshift'; import * as JSONBig from 'json-bigint-native'; import React, { useState } from 'react'; import { ShowValueDialog } from '../../dialogs/show-value-dialog/show-value-dialog'; -import { isSimpleArray, prettyFormatIsoDateWithMsIfNeeded } from '../../utils'; +import { + isSimpleArray, + prettyFormatIsoDateWithMsIfNeeded, + toIsoStringInTimezone, +} from '../../utils'; import { ActionIcon } from '../action-icon/action-icon'; import './table-cell.scss'; @@ -51,10 +56,11 @@ function shortenString(str: string): ShortParts { export interface TableCellProps { value: any; unlimited?: boolean; + timezone?: Timezone; } export const TableCell = React.memo(function TableCell(props: TableCellProps) { - const { value, unlimited } = props; + const { value, unlimited, timezone = Timezone.UTC } = props; const [showValue, setShowValue] = useState(); function renderShowValueDialog() { @@ -98,7 +104,9 @@ export const TableCell = React.memo(function TableCell(props: TableCellProps) { const dateValue = value.valueOf(); return (
- {isNaN(dateValue) ? 'Invalid date' : prettyFormatIsoDateWithMsIfNeeded(value.toISOString())} + {isNaN(dateValue) + ? 'Invalid date' + : prettyFormatIsoDateWithMsIfNeeded(toIsoStringInTimezone(value, timezone))}
); } else if (isSimpleArray(value)) { diff --git a/web-console/src/views/workbench-view/timezone-menu-items/__snapshots__/timezone-menu-items.spec.tsx.snap b/web-console/src/components/timezone-menu-items/__snapshots__/timezone-menu-items.spec.tsx.snap similarity index 99% rename from web-console/src/views/workbench-view/timezone-menu-items/__snapshots__/timezone-menu-items.spec.tsx.snap rename to web-console/src/components/timezone-menu-items/__snapshots__/timezone-menu-items.spec.tsx.snap index 9c958fb88001..313c9721c1f2 100644 --- a/web-console/src/views/workbench-view/timezone-menu-items/__snapshots__/timezone-menu-items.spec.tsx.snap +++ b/web-console/src/components/timezone-menu-items/__snapshots__/timezone-menu-items.spec.tsx.snap @@ -2,9 +2,6 @@ exports[`TimezoneMenuItems matches snapshot 1`] = ` [ - , { const now = new Date(); @@ -96,8 +97,7 @@ export const TimezoneMenuItems = function TimezoneMenuItems(props: TimezoneMenuI }; }, []); - return [ - , + const defaultItem = ( setSqlTimeZone(undefined)} - />, + /> + ); + + const namedMenuItems = timezones.map(({ timezone, offsetInMinutes }) => ( + + {timezone} + + ) : ( + timezone + ) + } + label={`UTC${timezoneOffsetInMinutesToString(offsetInMinutes, false)}`} + shouldDismissPopover={false} + onClick={() => setSqlTimeZone(timezone)} + /> + )); + + if (namedOnly) { + return [defaultItem, ...namedMenuItems]; + } + + return [ + defaultItem, - {timezones.map(({ timezone, offsetInMinutes }) => ( - - {timezone} - - ) : ( - timezone - ) - } - label={`UTC${timezoneOffsetInMinutesToString(offsetInMinutes, false)}`} - shouldDismissPopover={false} - onClick={() => setSqlTimeZone(timezone)} - /> - ))} + {namedMenuItems} , {offsetsInMinutes.map(offsetInMinutes => { diff --git a/web-console/src/utils/date-format.ts b/web-console/src/utils/date-format.ts index a5e6b96bfdf2..bcfcef38ec36 100644 --- a/web-console/src/utils/date-format.ts +++ b/web-console/src/utils/date-format.ts @@ -16,9 +16,9 @@ * limitations under the License. */ -import type { Duration } from 'chronoshift'; +import type { Duration, Timezone } from 'chronoshift'; -import { prettyFormatIsoDate } from './date'; +import { prettyFormatIsoDate, toIsoStringInTimezone } from './date'; export function formatStartDuration(start: Date, duration: Duration): string { let sliceLength; @@ -54,9 +54,9 @@ export function formatStartDuration(start: Date, duration: Duration): string { )}`; } -export function formatIsoDateRange(start: Date, end: Date): string { - let startStr = prettyFormatIsoDate(start); - let endStr = prettyFormatIsoDate(end); +export function formatIsoDateRange(start: Date, end: Date, timezone: Timezone): string { + let startStr = prettyFormatIsoDate(toIsoStringInTimezone(start, timezone)); + let endStr = prettyFormatIsoDate(toIsoStringInTimezone(end, timezone)); if (start.getMinutes() === 0 && end.getMinutes() === 0) { startStr = startStr.slice(0, 16); diff --git a/web-console/src/utils/date.ts b/web-console/src/utils/date.ts index 08f1d1df43ba..e240932d92b4 100644 --- a/web-console/src/utils/date.ts +++ b/web-console/src/utils/date.ts @@ -17,6 +17,8 @@ */ import type { DateRange, NonNullDateRange } from '@blueprintjs/datetime'; +import { fromDate, toTimeZone } from '@internationalized/date'; +import type { Timezone } from 'chronoshift'; const CURRENT_YEAR = new Date().getUTCFullYear(); @@ -38,24 +40,10 @@ export function prettyFormatIsoDate(isoDate: string | Date): string { return prettyFormatIsoDateWithMsIfNeeded(isoDate).replace(/\.\d\d\d/, ''); } -export function prettyFormatIsoDateTick(date: Date): string { - // s like 2016-06-27T19:00:00.000Z - let s = date.toISOString(); - if (!s.endsWith('.000Z')) { - return s.slice(19, 23); // => ".001" - } - s = s.slice(0, 19); // s like 2016-06-27T19:00:00 - - if (!s.endsWith(':00')) { - return s.slice(11); // => 00:00:01 - } - s = s.slice(0, 16); // s like 2016-06-27T19:00 - - if (!s.endsWith('T00:00')) { - return s.slice(11); // => 00:00 - } - - return s.slice(0, 10); // s like 2016-06-27 +export function toIsoStringInTimezone(date: Date, timezone: Timezone): string { + if (timezone.isUTC()) return date.toISOString(); + const zonedDate = toTimeZone(fromDate(date, 'Etc/UTC'), timezone.toString()); + return zonedDate.toString().replace(/[+-]\d\d:\d\d\[.+$/, ''); } export function utcToLocalDate(utcDate: Date): Date { diff --git a/web-console/src/utils/index.tsx b/web-console/src/utils/index.tsx index b562bce51500..760e79b6f8d9 100644 --- a/web-console/src/utils/index.tsx +++ b/web-console/src/utils/index.tsx @@ -37,5 +37,6 @@ export * from './sanitizers'; export * from './sql'; export * from './stage'; export * from './table-helpers'; +export * from './ticks'; export * from './types'; export * from './values-query'; diff --git a/web-console/src/utils/ticks.ts b/web-console/src/utils/ticks.ts new file mode 100644 index 000000000000..fbe4fc10e674 --- /dev/null +++ b/web-console/src/utils/ticks.ts @@ -0,0 +1,127 @@ +/* + * 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 { fromDate, toTimeZone } from '@internationalized/date'; +import type { Timezone } from 'chronoshift'; +import { day, Duration, hour, minute, month, second, week, year } from 'chronoshift'; +import { bisector, tickStep } from 'd3-array'; +import { utcFormat } from 'd3-time-format'; + +// --------------------------------------- +// Tick formatting + +const formatMillisecond = utcFormat('.%L'); +const formatSecond = utcFormat(':%S'); +const formatMinute = utcFormat('%I:%M'); +const formatHour = utcFormat('%I %p'); +const formatDay = utcFormat('%a %d'); +const formatWeek = utcFormat('%b %d'); +const formatMonth = utcFormat('%B'); +const formatYear = utcFormat('%Y'); + +/* + * Format ticks with the timezone provided + * Originally based on https://github.com/d3/d3-scale/blob/v3.3.0/src/time.js#L29 + */ +export function tickFormatWithTimezone(date: Date, timezone: Timezone): string { + if (second.floor(date, timezone) < date) return formatMillisecond(date); + if (minute.floor(date, timezone) < date) return formatSecond(date); + + const offsetDate = offsetDateToTimezone(date, timezone); + if (hour.floor(date, timezone) < date) return formatMinute(offsetDate); + if (day.floor(date, timezone) < date) return formatHour(offsetDate); + if (month.floor(date, timezone) < date) { + return week.floor(date, timezone) < date ? formatDay(offsetDate) : formatWeek(offsetDate); + } + if (year.floor(date, timezone) < date) return formatMonth(offsetDate); + return formatYear(offsetDate); +} + +function offsetDateToTimezone(date: Date, timezone: Timezone): Date { + const zonedDate = toTimeZone(fromDate(date, 'Etc/UTC'), timezone.toString()); + return new Date(date.valueOf() + zonedDate.offset); +} + +// --------------------------------------- +// Tick calculation + +/* + * Calculates the ticks in a manner identical to D3's default tick calculator but in a timezone aware manner + * Originally based on https://github.com/d3/d3-time/blob/v3.1.0/src/ticks.js#L35 + */ +export function timezoneAwareTicks( + start: Date, + end: Date, + count: number, + timezone: Timezone, +): Date[] { + if (end < start) throw new Error('start must come before end end'); + const interval = tickInterval(start, end, count); + if (!interval) return []; + return interval.materialize(start, end, timezone); +} + +const durationSecond = new Duration('PT1S'); +const durationMinute = new Duration('PT1M'); +const durationHour = new Duration('PT1H'); +const durationDay = new Duration('P1D'); +const durationWeek = new Duration('P1W'); +const durationMonth = new Duration('P1M'); +const durationYear = new Duration('P1Y'); +const tickIntervals: [Duration, number, number][] = [ + [durationSecond, 1, second.canonicalLength], + [durationSecond, 5, 5 * second.canonicalLength], + [durationSecond, 15, 15 * second.canonicalLength], + [durationSecond, 30, 30 * second.canonicalLength], + [durationMinute, 1, minute.canonicalLength], + [durationMinute, 5, 5 * minute.canonicalLength], + [durationMinute, 15, 15 * minute.canonicalLength], + [durationMinute, 30, 30 * minute.canonicalLength], + [durationHour, 1, hour.canonicalLength], + [durationHour, 3, 3 * hour.canonicalLength], + [durationHour, 6, 6 * hour.canonicalLength], + [durationHour, 12, 12 * hour.canonicalLength], + [durationDay, 1, day.canonicalLength], + [durationDay, 2, 2 * day.canonicalLength], + [durationWeek, 1, week.canonicalLength], + [durationMonth, 1, month.canonicalLength], + [durationMonth, 3, 3 * month.canonicalLength], + [durationYear, 1, year.canonicalLength], +]; + +/* + * Calculates the distance between adjacent ticks + * Originally based on https://github.com/d3/d3-time/blob/v3.1.0/src/ticks.js#L43 + * It is basically the same function except it returns a Duration which is timezone aware + */ +function tickInterval(start: Date, end: Date, count: number): Duration { + const target = Math.abs(end.valueOf() - start.valueOf()) / count; + const i = bisector(([, , step]) => step).right(tickIntervals, target); + if (i === tickIntervals.length) + return durationYear.multiply( + tickStep(start.valueOf() / year.canonicalLength, end.valueOf() / year.canonicalLength, count), + ); + // Original line uses milliseconds: if (i === 0) return millisecond.every(Math.max(tickStep(start, end, count), 1)); + if (i === 0) + return durationSecond.multiply( + Math.max(tickStep(start.valueOf() / 1000, end.valueOf() / 1000, count), 1), + ); + const [t, step] = + tickIntervals[target / tickIntervals[i - 1][2] < tickIntervals[i][2] / target ? i - 1 : i]; + return t.multiply(step); +} diff --git a/web-console/src/views/explore-view/components/control-pane/control-pane.tsx b/web-console/src/views/explore-view/components/control-pane/control-pane.tsx index f9d2345c9644..1b1cd5a785f4 100644 --- a/web-console/src/views/explore-view/components/control-pane/control-pane.tsx +++ b/web-console/src/views/explore-view/components/control-pane/control-pane.tsx @@ -18,6 +18,7 @@ import { Button, + Icon, InputGroup, Intent, Menu, @@ -148,6 +149,9 @@ export const ControlPane = function ControlPane(props: ControlPaneProps) { : undefined + } onClick={() => onValueChange(o)} /> ))} diff --git a/web-console/src/views/explore-view/components/filter-pane/filter-menu/filter-menu.tsx b/web-console/src/views/explore-view/components/filter-pane/filter-menu/filter-menu.tsx index 321a7d1d0ba6..b5f5ee2fc9b7 100644 --- a/web-console/src/views/explore-view/components/filter-pane/filter-menu/filter-menu.tsx +++ b/web-console/src/views/explore-view/components/filter-pane/filter-menu/filter-menu.tsx @@ -30,6 +30,7 @@ import { } from '@blueprintjs/core'; import { IconNames } from '@blueprintjs/icons'; import type { CancelToken } from 'axios'; +import { Timezone } from 'chronoshift'; import type { Column, FilterPattern, @@ -379,7 +380,7 @@ export const FilterMenu = React.memo(function FilterMenu(props: FilterMenuProps) if (tab === 'compose') { onAddToSourceQueryAsColumn( filterPatternToExpression(pattern).as( - formatPatternWithoutNegation(pattern), + formatPatternWithoutNegation(pattern, Timezone.UTC), ), ); } else { diff --git a/web-console/src/views/explore-view/components/filter-pane/filter-pane.tsx b/web-console/src/views/explore-view/components/filter-pane/filter-pane.tsx index 4d685c74c4a9..cab39295b9fe 100644 --- a/web-console/src/views/explore-view/components/filter-pane/filter-pane.tsx +++ b/web-console/src/views/explore-view/components/filter-pane/filter-pane.tsx @@ -19,6 +19,7 @@ import { Button, Popover } from '@blueprintjs/core'; import { IconNames } from '@blueprintjs/icons'; import type { CancelToken } from 'axios'; +import type { Timezone } from 'chronoshift'; import classNames from 'classnames'; import { isDate } from 'date-fns'; import type { @@ -47,6 +48,7 @@ import './filter-pane.scss'; export interface FilterPaneProps { querySource: QuerySource | undefined; + timezone: Timezone; filter: SqlExpression; onFilterChange(filter: SqlExpression): void; runSqlQuery(query: string | SqlQuery, cancelToken?: CancelToken): Promise; @@ -57,6 +59,7 @@ export interface FilterPaneProps { export const FilterPane = forwardRef(function FilterPane(props: FilterPaneProps, ref) { const { querySource, + timezone, filter, onFilterChange, runSqlQuery, @@ -165,7 +168,7 @@ export const FilterPane = forwardRef(function FilterPane(props: FilterPaneProps,