feat(custom-tz): added the ability to set UTC as the timezone when making custom time range queries (#18011)
parent
ebe77518a5
commit
c6b2fc5d2c
|
|
@ -494,7 +494,7 @@ workflows:
|
|||
hourly-e2e:
|
||||
triggers:
|
||||
- schedule:
|
||||
cron: '0 * * * *'
|
||||
cron: "0 * * * *"
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
|
|
|
|||
10
CHANGELOG.md
10
CHANGELOG.md
|
|
@ -1,3 +1,13 @@
|
|||
## v2.0.0-beta.11 [unreleased]
|
||||
|
||||
### Features
|
||||
|
||||
1. [18011](https://github.com/influxdata/influxdb/pull/18011): Integrate UTC dropdown when making custom time range query
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
### UI Improvements
|
||||
|
||||
## v2.0.0-beta.10 [2020-05-07]
|
||||
|
||||
### Features
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import {STATUS_FIELDS} from 'src/alerting/constants/history'
|
|||
// Utils
|
||||
import {loadStatuses, getInitialState} from 'src/alerting/utils/history'
|
||||
import {getCheckIDs} from 'src/checks/selectors'
|
||||
import {getTimeZone} from 'src/dashboards/selectors'
|
||||
|
||||
// Types
|
||||
import {ResourceIDs} from 'src/checks/reducers'
|
||||
|
|
@ -101,7 +102,7 @@ const CheckHistory: FC<Props> = ({
|
|||
}
|
||||
|
||||
const mstp = (state: AppState, props: OwnProps) => {
|
||||
const timeZone = state.app.persisted.timeZone
|
||||
const timeZone = getTimeZone(state)
|
||||
const checkIDs = getCheckIDs(state)
|
||||
const check = getByID<Check>(state, ResourceType.Checks, props.params.checkID)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,15 @@
|
|||
// Funcs
|
||||
import {getTimeRange} from 'src/dashboards/selectors/index'
|
||||
import {
|
||||
getTimeRange,
|
||||
getTimeRangeWithTimezone,
|
||||
} from 'src/dashboards/selectors/index'
|
||||
import moment from 'moment'
|
||||
|
||||
// Types
|
||||
import {RangeState} from 'src/dashboards/reducers/ranges'
|
||||
import {CurrentDashboardState} from 'src/shared/reducers/currentDashboard'
|
||||
import {TimeRange} from 'src/types'
|
||||
import {TimeRange, TimeZone, CustomTimeRange} from 'src/types'
|
||||
import {AppState as AppPresentationState} from 'src/shared/reducers/app'
|
||||
|
||||
// Constants
|
||||
import {
|
||||
|
|
@ -18,14 +23,32 @@ const untypedGetTimeRangeByDashboardID = getTimeRange as (a: {
|
|||
currentDashboard: CurrentDashboardState
|
||||
}) => TimeRange
|
||||
|
||||
const untypedGetTimeRangeWithTimeZone = getTimeRangeWithTimezone as (a: {
|
||||
ranges: RangeState
|
||||
currentDashboard: CurrentDashboardState
|
||||
app: AppPresentationState
|
||||
}) => TimeRange
|
||||
|
||||
describe('Dashboards.Selector', () => {
|
||||
const dashboardIDs = ['04c6f3976f4b8001', '04c6f3976f4b8000']
|
||||
const dashboardIDs = [
|
||||
'04c6f3976f4b8001',
|
||||
'04c6f3976f4b8000',
|
||||
'04c6f3976f4b8002',
|
||||
]
|
||||
const lower = `2020-05-05T10:00:00${moment().format('Z')}`
|
||||
const upper = `2020-05-05T11:00:00${moment().format('Z')}`
|
||||
const customTimeRange = {
|
||||
lower,
|
||||
upper,
|
||||
type: 'custom',
|
||||
} as CustomTimeRange
|
||||
const ranges: RangeState = {
|
||||
[dashboardIDs[0]]: pastFifteenMinTimeRange,
|
||||
[dashboardIDs[1]]: pastHourTimeRange,
|
||||
[dashboardIDs[2]]: customTimeRange,
|
||||
}
|
||||
|
||||
it('should return the the correct range when a matching dashboard ID is found', () => {
|
||||
it('should return the correct range when a matching dashboard ID is found', () => {
|
||||
const currentDashboard = {id: dashboardIDs[0]}
|
||||
|
||||
expect(
|
||||
|
|
@ -33,7 +56,7 @@ describe('Dashboards.Selector', () => {
|
|||
).toEqual(pastFifteenMinTimeRange)
|
||||
})
|
||||
|
||||
it('should return the the default range when no matching dashboard ID is found', () => {
|
||||
it('should return the default range when no matching dashboard ID is found', () => {
|
||||
const currentDashboard = {id: 'Oogum Boogum'}
|
||||
|
||||
expect(
|
||||
|
|
@ -41,11 +64,58 @@ describe('Dashboards.Selector', () => {
|
|||
).toEqual(DEFAULT_TIME_RANGE)
|
||||
})
|
||||
|
||||
it('should return the the default range when no ranges are passed in', () => {
|
||||
it('should return the default range when no ranges are passed in', () => {
|
||||
const currentDashboard = {id: dashboardIDs[0]}
|
||||
|
||||
expect(
|
||||
untypedGetTimeRangeByDashboardID({ranges: {}, currentDashboard})
|
||||
).toEqual(DEFAULT_TIME_RANGE)
|
||||
})
|
||||
|
||||
it('should return the an unmodified version of the timeRange when the timeZone is local', () => {
|
||||
const currentDashboard = {id: dashboardIDs[2]}
|
||||
const app: AppPresentationState = {
|
||||
ephemeral: {
|
||||
inPresentationMode: false,
|
||||
},
|
||||
persisted: {
|
||||
autoRefresh: 0,
|
||||
showTemplateControlBar: false,
|
||||
navBarState: 'expanded',
|
||||
timeZone: 'Local' as TimeZone,
|
||||
theme: 'dark',
|
||||
},
|
||||
}
|
||||
|
||||
expect(
|
||||
untypedGetTimeRangeWithTimeZone({ranges, currentDashboard, app})
|
||||
).toEqual(customTimeRange)
|
||||
})
|
||||
|
||||
it('should return the timeRange for the same hour with a UTC timezone when the timeZone is UTC', () => {
|
||||
const currentDashboard = {id: dashboardIDs[2]}
|
||||
|
||||
const app: AppPresentationState = {
|
||||
ephemeral: {
|
||||
inPresentationMode: false,
|
||||
},
|
||||
persisted: {
|
||||
autoRefresh: 0,
|
||||
showTemplateControlBar: false,
|
||||
navBarState: 'expanded',
|
||||
timeZone: 'UTC' as TimeZone,
|
||||
theme: 'dark',
|
||||
},
|
||||
}
|
||||
|
||||
const expected = {
|
||||
lower: `2020-05-05T10:00:00Z`,
|
||||
upper: `2020-05-05T11:00:00Z`,
|
||||
type: 'custom',
|
||||
}
|
||||
|
||||
expect(
|
||||
untypedGetTimeRangeWithTimeZone({ranges, currentDashboard, app})
|
||||
).toEqual(expected)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import {get} from 'lodash'
|
||||
|
||||
import {AppState, View, Check, ViewType, TimeRange} from 'src/types'
|
||||
import moment from 'moment'
|
||||
import {AppState, View, Check, ViewType, TimeRange, TimeZone} from 'src/types'
|
||||
import {currentContext} from 'src/shared/selectors/currentContext'
|
||||
|
||||
// Constants
|
||||
|
|
@ -15,6 +15,47 @@ export const getTimeRange = (state: AppState): TimeRange => {
|
|||
return state.ranges[contextID] || DEFAULT_TIME_RANGE
|
||||
}
|
||||
|
||||
export const getTimeRangeWithTimezone = (state: AppState): TimeRange => {
|
||||
const timeRange = getTimeRange(state)
|
||||
const timeZone = getTimeZone(state)
|
||||
|
||||
const newTimeRange = {...timeRange}
|
||||
if (timeRange.type === 'custom' && timeZone === 'UTC') {
|
||||
// conforms dates to account to UTC with proper offset if needed
|
||||
newTimeRange.lower = setTimeToUTC(newTimeRange.lower)
|
||||
newTimeRange.upper = setTimeToUTC(newTimeRange.upper)
|
||||
}
|
||||
return newTimeRange
|
||||
}
|
||||
|
||||
// The purpose of this function is to set a user's custom time range selection
|
||||
// from the local time to the same time in UTC if UTC is selected from the
|
||||
// timezone dropdown. This is feature was original requested here:
|
||||
// https://github.com/influxdata/influxdb/issues/17877
|
||||
// Example: user selected 10-11:00am and sets the dropdown to UTC
|
||||
// Query should run against 10-11:00am UTC rather than querying
|
||||
// 10-11:00am local time (offset depending on timezone)
|
||||
export const setTimeToUTC = (date: string): string => {
|
||||
const offset = new Date(date).getTimezoneOffset()
|
||||
if (offset > 0) {
|
||||
return moment
|
||||
.utc(date)
|
||||
.subtract(offset, 'minutes')
|
||||
.format()
|
||||
}
|
||||
if (offset < 0) {
|
||||
return moment
|
||||
.utc(date)
|
||||
.add(offset, 'minutes')
|
||||
.format()
|
||||
}
|
||||
return moment.utc(date).format()
|
||||
}
|
||||
|
||||
export const getTimeZone = (state: AppState): TimeZone => {
|
||||
return state.app.persisted.timeZone || 'Local'
|
||||
}
|
||||
|
||||
export const getCheckForView = (
|
||||
state: AppState,
|
||||
view: View
|
||||
|
|
|
|||
|
|
@ -3,8 +3,9 @@ import React, {FunctionComponent} from 'react'
|
|||
import {connect} from 'react-redux'
|
||||
import {SelectDropdown, IconFont} from '@influxdata/clockface'
|
||||
|
||||
// Actions
|
||||
// Actions & Selectors
|
||||
import {setTimeZone} from 'src/shared/actions/app'
|
||||
import {getTimeZone} from 'src/dashboards/selectors'
|
||||
|
||||
// Constants
|
||||
import {TIME_ZONES} from 'src/shared/constants/timeZones'
|
||||
|
|
@ -38,7 +39,7 @@ const TimeZoneDropdown: FunctionComponent<Props> = ({
|
|||
}
|
||||
|
||||
const mstp = (state: AppState): StateProps => {
|
||||
return {timeZone: state.app.persisted.timeZone || 'Local'}
|
||||
return {timeZone: getTimeZone(state)}
|
||||
}
|
||||
|
||||
const mdtp = {onSetTimeZone: setTimeZone}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import {fetchDemoDataBuckets} from 'src/cloud/apis/demodata'
|
|||
|
||||
// Utils
|
||||
import {getActiveQuery, getActiveTimeMachine} from 'src/timeMachine/selectors'
|
||||
import {getTimeRange} from 'src/dashboards/selectors'
|
||||
import {getTimeRangeWithTimezone} from 'src/dashboards/selectors'
|
||||
|
||||
// Types
|
||||
import {
|
||||
|
|
@ -222,7 +222,8 @@ export const loadTagSelector = (index: number) => async (
|
|||
const orgID = get(foundBucket, 'orgID', getOrg(getState()).id)
|
||||
|
||||
try {
|
||||
const timeRange = getTimeRange(state)
|
||||
const timeRange = getTimeRangeWithTimezone(state)
|
||||
|
||||
const searchTerm = getActiveTimeMachine(state).queryBuilder.tags[index]
|
||||
.keysSearchTerm
|
||||
|
||||
|
|
@ -287,7 +288,7 @@ const loadTagSelectorValues = (index: number) => async (
|
|||
dispatch(setBuilderTagValuesStatus(index, RemoteDataState.Loading))
|
||||
|
||||
try {
|
||||
const timeRange = getTimeRange(state)
|
||||
const timeRange = getTimeRangeWithTimezone(state)
|
||||
const key = getActiveQuery(getState()).builderConfig.tags[index].key
|
||||
const searchTerm = getActiveTimeMachine(getState()).queryBuilder.tags[index]
|
||||
.valuesSearchTerm
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import {
|
|||
getFillColumnsSelection,
|
||||
getSymbolColumnsSelection,
|
||||
} from 'src/timeMachine/selectors'
|
||||
import {getTimeRange} from 'src/dashboards/selectors'
|
||||
import {getTimeRange, getTimeZone} from 'src/dashboards/selectors'
|
||||
|
||||
// Types
|
||||
import {
|
||||
|
|
@ -164,7 +164,7 @@ const mstp = (state: AppState): StateProps => {
|
|||
const fillColumns = getFillColumnsSelection(state)
|
||||
const symbolColumns = getSymbolColumnsSelection(state)
|
||||
|
||||
const timeZone = state.app.persisted.timeZone
|
||||
const timeZone = getTimeZone(state)
|
||||
|
||||
return {
|
||||
loading,
|
||||
|
|
|
|||
|
|
@ -7,6 +7,11 @@ import {
|
|||
import {AppState} from 'src/types'
|
||||
|
||||
const MOCKSTATE = ({
|
||||
app: {
|
||||
persisted: {
|
||||
timeZone: 'UTC',
|
||||
},
|
||||
},
|
||||
currentDashboard: {
|
||||
id: '',
|
||||
},
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import {get} from 'lodash'
|
|||
// Utils
|
||||
import {getActiveQuery} from 'src/timeMachine/selectors'
|
||||
import {getRangeVariable} from 'src/variables/utils/getTimeRangeVars'
|
||||
import {getTimeRange} from 'src/dashboards/selectors'
|
||||
import {getTimeRange, getTimeRangeWithTimezone} from 'src/dashboards/selectors'
|
||||
import {getWindowPeriodVariable} from 'src/variables/utils/getWindowVars'
|
||||
import {
|
||||
TIME_RANGE_START,
|
||||
|
|
@ -108,11 +108,9 @@ export const getAllVariables = (
|
|||
.concat([TIME_RANGE_START, TIME_RANGE_STOP, WINDOW_PERIOD])
|
||||
.reduce((prev, curr) => {
|
||||
prev.push(getVariable(state, curr))
|
||||
|
||||
return prev
|
||||
}, [])
|
||||
.filter(v => !!v)
|
||||
|
||||
return vars
|
||||
}
|
||||
|
||||
|
|
@ -126,8 +124,7 @@ export const getVariable = (state: AppState, variableID: string): Variable => {
|
|||
}
|
||||
|
||||
if (variableID === TIME_RANGE_START || variableID === TIME_RANGE_STOP) {
|
||||
const timeRange = getTimeRange(state)
|
||||
|
||||
const timeRange = getTimeRangeWithTimezone(state)
|
||||
vari = getRangeVariable(variableID, timeRange)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue