Skip to content

Commit 857889a

Browse files
[ES|QL] Supports timezone (elastic#247917)
## Summary Closes elastic#172221 ES|QL now supports timezones! This PR is adding support to: - our esql expression. The applications that are using the expression are getting this for free - consumers of the getESQLResults utility - all analyst xp apps There are usages (such as in ML) that are still not supporting it. The teams need to handle it themselves. I didnt do it mostly for 2 reasons: - I dont know the apps and the code - it is very complicated to do so ### When we will merge I am going to merge it after the next serverless release (next week) to be sure that all the ES serverless instances have the new request parameter already. --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
1 parent 58f3fe4 commit 857889a

16 files changed

Lines changed: 72 additions & 52 deletions

File tree

src/platform/packages/private/kbn-generate-csv/src/generate_csv_esql.test.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@ describe('CsvESQLGenerator', () => {
317317
);
318318

319319
expect(mockDataClientSearchFn).toBeCalledWith(
320-
{ params: { filter: undefined, locale: 'en', query: '' } },
320+
{ params: { filter: undefined, locale: 'en', query: '', time_zone: 'America/New_York' } },
321321
{
322322
strategy: 'esql',
323323
transport: {
@@ -396,7 +396,7 @@ describe('CsvESQLGenerator', () => {
396396
);
397397

398398
expect(mockDataClientSearchFn).toBeCalledWith(
399-
{ params: { filter: undefined, locale: 'en', query: '' } },
399+
{ params: { filter: undefined, locale: 'en', query: '', time_zone: 'America/New_York' } },
400400
{
401401
strategy: 'esql',
402402
transport: {
@@ -494,6 +494,7 @@ describe('CsvESQLGenerator', () => {
494494
},
495495
locale: 'en',
496496
query: 'FROM kibana_sample_data_logs | LIMIT 10 | LIMIT 500',
497+
time_zone: 'America/New_York',
497498
},
498499
},
499500
{
@@ -568,6 +569,7 @@ describe('CsvESQLGenerator', () => {
568569
]),
569570
locale: 'en',
570571
query: `${query.esql} | LIMIT 500`,
572+
time_zone: 'America/New_York',
571573
},
572574
},
573575
{
@@ -607,6 +609,7 @@ describe('CsvESQLGenerator', () => {
607609
filter: undefined,
608610
locale: 'en',
609611
query: 'FROM custom-metrics | LIMIT 500',
612+
time_zone: 'America/New_York',
610613
},
611614
},
612615
{

src/platform/packages/private/kbn-generate-csv/src/generate_csv_esql.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ export class CsvESQLGenerator {
7474
let reportingError: undefined | ReportingError;
7575
const warnings: string[] = [];
7676

77-
const { maxSizeBytes, maxRows, bom, escapeFormulaValues } = settings;
77+
const { maxSizeBytes, maxRows, bom, escapeFormulaValues, timezone } = settings;
7878
const builder = new MaxSizeStringBuilder(this.stream, byteSizeValueToNumber(maxSizeBytes), bom);
7979

8080
// it will return undefined if there are no _tstart, _tend named params in the query
@@ -130,10 +130,7 @@ export class CsvESQLGenerator {
130130
// locale can be used for number/date formatting
131131
locale: i18n.getLocale(),
132132
...(params.length ? { params } : {}),
133-
// TODO: time_zone support was temporarily removed from ES|QL,
134-
// we will need to add it back in once it is supported again.
135-
// https://github.com/elastic/elasticsearch/pull/102767
136-
// timezone
133+
time_zone: timezone,
137134
},
138135
};
139136

src/platform/packages/shared/kbn-es-query/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ export {
143143
isDataViewFieldSubtypeMulti,
144144
isDataViewFieldSubtypeNested,
145145
isCCSRemoteIndexName,
146+
getTimeZoneFromSettings,
146147
} from './src/utils';
147148

148149
export type { ExecutionContextSearch } from './src/expressions/types';

src/platform/packages/shared/kbn-es-types/src/search.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -675,10 +675,7 @@ export interface ESQLSearchResponse {
675675
}
676676

677677
export interface ESQLSearchParams {
678-
// TODO: time_zone support was temporarily removed from ES|QL,
679-
// we will need to add it back in once it is supported again.
680-
// https://github.com/elastic/elasticsearch/pull/102767
681-
// time_zone?: string;
678+
time_zone?: string;
682679
query: string;
683680
filter?: unknown;
684681
project_routing?: string;

src/platform/packages/shared/kbn-esql-utils/src/utils/run_query.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import dateMath from '@kbn/datemath';
1212
import type { DatatableColumn } from '@kbn/expressions-plugin/common';
1313
import type { ISearchGeneric } from '@kbn/search-types';
1414
import type { TimeRange } from '@kbn/es-query';
15+
import { getTimeZoneFromSettings } from '@kbn/es-query';
1516
import { esFieldTypeToKibanaFieldType } from '@kbn/field-types';
1617
import type { ESQLColumn, ESQLSearchResponse, ESQLSearchParams } from '@kbn/es-types';
1718
import { lastValueFrom } from 'rxjs';
@@ -177,6 +178,7 @@ export async function getESQLResults({
177178
dropNullColumns,
178179
timeRange,
179180
variables,
181+
timezone,
180182
}: {
181183
esqlQuery: string;
182184
search: ISearchGeneric;
@@ -185,6 +187,7 @@ export async function getESQLResults({
185187
dropNullColumns?: boolean;
186188
timeRange?: TimeRange;
187189
variables?: ESQLControlVariable[];
190+
timezone?: string;
188191
}): Promise<{
189192
response: ESQLSearchResponse;
190193
params: ESQLSearchParams;
@@ -198,6 +201,7 @@ export async function getESQLResults({
198201
query: esqlQuery,
199202
...(dropNullColumns ? { dropNullColumns: true } : {}),
200203
...(namedParams.length ? { params: namedParams } : {}),
204+
...(timezone ? { time_zone: getTimeZoneFromSettings(timezone) } : {}),
201205
},
202206
},
203207
{

src/platform/plugins/shared/controls/public/controls/esql_control/utils/get_esql_single_column_values.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
import type { ISearchGeneric } from '@kbn/search-types';
1111
import type { TimeRange } from '@kbn/es-query';
1212
import { getESQLResults } from '@kbn/esql-utils';
13+
import { UI_SETTINGS } from '@kbn/data-plugin/public';
1314
import type { ESQLControlVariable } from '@kbn/esql-types';
15+
import { coreServices } from '../../../services/kibana_services';
1416

1517
export interface GetESQLSingleColumnValuesSuccess {
1618
values: string[];
@@ -35,13 +37,15 @@ export const getESQLSingleColumnValues = async ({
3537
GetESQLSingleColumnValuesSuccess | GetESQLSingleColumnValuesFailure
3638
> => {
3739
try {
40+
const timezone = coreServices.uiSettings?.get<'Browser' | string>(UI_SETTINGS.DATEFORMAT_TZ);
3841
const results = await getESQLResults({
3942
esqlQuery: query,
4043
search,
4144
signal: undefined,
4245
filter: undefined,
4346
dropNullColumns: true,
4447
timeRange,
48+
timezone,
4549
variables: esqlVariables,
4650
});
4751
const columns = results.response.columns.map((col) => col.name);

src/platform/plugins/shared/data/common/search/expressions/esql.test.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,12 @@ describe('getEsqlFn', () => {
2828
const esqlFn = getEsqlFn({
2929
getStartDependencies: async () => ({
3030
search: mockSearch,
31-
uiSettings: {} as UiSettingsCommon,
31+
uiSettings: {
32+
get: jest.fn((key: string) => {
33+
if (key === 'dateFormat:tz') return 'UTC';
34+
return undefined;
35+
}),
36+
} as unknown as UiSettingsCommon,
3237
}),
3338
});
3439

src/platform/plugins/shared/data/common/search/expressions/esql.ts

Lines changed: 8 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import {
3232
import { zipObject } from 'lodash';
3333
import type { Observable } from 'rxjs';
3434
import { catchError, defer, map, switchMap, tap, throwError } from 'rxjs';
35-
import { buildEsQuery, type Filter } from '@kbn/es-query';
35+
import { buildEsQuery, type Filter, getTimeZoneFromSettings } from '@kbn/es-query';
3636
import type { ESQLSearchParams, ESQLSearchResponse } from '@kbn/es-types';
3737
import DateMath from '@kbn/datemath';
3838
import { getEsQueryConfig } from '../../es_query';
@@ -60,10 +60,6 @@ type Output = Observable<Datatable>;
6060

6161
interface Arguments {
6262
query: string;
63-
// TODO: time_zone support was temporarily removed from ES|QL,
64-
// we will need to add it back in once it is supported again.
65-
// https://github.com/elastic/elasticsearch/pull/102767
66-
// timezone?: string;
6763
timeField?: string;
6864
locale?: string;
6965

@@ -117,15 +113,6 @@ export const getEsqlFn = ({ getStartDependencies }: EsqlFnArguments) => {
117113
defaultMessage: 'An ES|QL query.',
118114
}),
119115
},
120-
// timezone: {
121-
// aliases: ['tz'],
122-
// types: ['string'],
123-
// default: 'UTC',
124-
// help: i18n.translate('data.search.esql.timezone.help', {
125-
// defaultMessage:
126-
// 'The timezone to use for date operations. Valid ISO8601 formats and UTC offsets both work.',
127-
// }),
128-
// },
129116
timeField: {
130117
aliases: ['timeField'],
131118
types: ['string'],
@@ -169,14 +156,7 @@ export const getEsqlFn = ({ getStartDependencies }: EsqlFnArguments) => {
169156
},
170157
fn(
171158
input,
172-
{
173-
query,
174-
/* timezone, */ timeField,
175-
locale,
176-
titleForInspector,
177-
descriptionForInspector,
178-
ignoreGlobalFilters,
179-
},
159+
{ query, timeField, locale, titleForInspector, descriptionForInspector, ignoreGlobalFilters },
180160
{ abortSignal, inspectorAdapters, getKibanaRequest, getSearchSessionId }
181161
) {
182162
return defer(() =>
@@ -197,17 +177,18 @@ export const getEsqlFn = ({ getStartDependencies }: EsqlFnArguments) => {
197177
// and the query is not set with ?? in the query, we should set it
198178
// https://github.com/elastic/elasticsearch/pull/122459
199179
const fixedQuery = fixESQLQueryWithVariables(query, input?.esqlVariables ?? []);
180+
const esQueryConfigs = getEsQueryConfig(
181+
uiSettings as Parameters<typeof getEsQueryConfig>[0]
182+
);
200183
const params: ESQLSearchParams = {
201184
query: fixedQuery,
202-
// time_zone: timezone,
185+
time_zone: esQueryConfigs.dateFormatTZ
186+
? getTimeZoneFromSettings(esQueryConfigs.dateFormatTZ)
187+
: 'UTC',
203188
locale,
204189
include_execution_metadata: true,
205190
};
206191
if (input) {
207-
const esQueryConfigs = getEsQueryConfig(
208-
uiSettings as Parameters<typeof getEsQueryConfig>[0]
209-
);
210-
211192
const namedParams = getNamedParams(fixedQuery, input.timeRange, input.esqlVariables);
212193

213194
if (namedParams.length) {

src/platform/plugins/shared/esql/public/triggers/esql_controls/control_flyout/index.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,6 @@ export function ESQLControlsFlyout({
220220
search={search}
221221
valuesRetrieval={valuesField}
222222
timeRange={timeRange}
223-
currentApp={currentApp}
224223
esqlVariables={esqlVariables}
225224
/>
226225
) : (

src/platform/plugins/shared/esql/public/triggers/esql_controls/control_flyout/value_control_form.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import type { ISearchGeneric } from '@kbn/search-types';
3030
import { isEqual } from 'lodash';
3131
import React, { useCallback, useEffect, useState } from 'react';
3232
import useMountedState from 'react-use/lib/useMountedState';
33+
import { UI_SETTINGS } from '@kbn/data-plugin/public';
3334
import { ESQLLangEditor } from '../../../create_editor';
3435
import type { ServiceDeps } from '../../../kibana_services';
3536
import { ChooseColumnPopover } from './choose_column_popover';
@@ -45,7 +46,6 @@ interface ValueControlFormProps {
4546
initialState?: ESQLControlState;
4647
valuesRetrieval?: string;
4748
timeRange?: TimeRange;
48-
currentApp?: string;
4949
esqlVariables: ESQLControlVariable[];
5050
}
5151

@@ -67,7 +67,6 @@ export function ValueControlForm({
6767
setControlState,
6868
valuesRetrieval,
6969
timeRange,
70-
currentApp,
7170
esqlVariables,
7271
}: ValueControlFormProps) {
7372
const isMounted = useMountedState();
@@ -158,13 +157,15 @@ export function ValueControlForm({
158157
const onValuesQuerySubmit = useCallback(
159158
async (query: string) => {
160159
try {
160+
const timezone = core.uiSettings.get<'Browser' | string>(UI_SETTINGS.DATEFORMAT_TZ);
161161
getESQLResults({
162162
esqlQuery: query,
163163
search,
164164
signal: undefined,
165165
filter: undefined,
166166
dropNullColumns: true,
167167
timeRange,
168+
timezone,
168169
variables: esqlVariables,
169170
}).then((results) => {
170171
if (!isMounted()) {
@@ -195,7 +196,7 @@ export function ValueControlForm({
195196
setEsqlQueryErrors([e]);
196197
}
197198
},
198-
[isMounted, search, timeRange, esqlVariables]
199+
[isMounted, search, timeRange, esqlVariables, core.uiSettings]
199200
);
200201

201202
const setSuggestedQuery = useCallback(async () => {

0 commit comments

Comments
 (0)