mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-11-21 14:44:00 +00:00
vmui: add flag for default timezone setting (#5611)
* vmui: add flag for default timezone setting #5375 * vmui: validate timezone before client return * Update app/vmselect/vmui.go --------- Co-authored-by: Aliaksandr Valialkin <valyala@victoriametrics.com>
This commit is contained in:
parent
633e6b48ad
commit
eb6def0695
17 changed files with 191 additions and 18 deletions
|
@ -3040,4 +3040,6 @@ Pass `-help` to VictoriaMetrics in order to see the list of supported command-li
|
|||
Optional URL for proxying requests to vmalert. For example, if -vmalert.proxyURL=http://vmalert:8880 , then alerting API requests such as /api/v1/rules from Grafana will be proxied to http://vmalert:8880/api/v1/rules
|
||||
-vmui.customDashboardsPath string
|
||||
Optional path to vmui dashboards. See https://github.com/VictoriaMetrics/VictoriaMetrics/tree/master/app/vmui/packages/vmui/public/dashboards
|
||||
-vmui.defaultTimezone string
|
||||
The default timezone to be used in vmui. Timezone must be a valid IANA Time Zone. For example: America/New_York, Europe/Berlin, Etc/GMT+3 or Local. See https://github.com/VictoriaMetrics/VictoriaMetrics/tree/master/app/vmui#timezone-configuration
|
||||
```
|
||||
|
|
|
@ -426,6 +426,14 @@ func handleStaticAndSimpleRequests(w http.ResponseWriter, r *http.Request, path
|
|||
}
|
||||
return true
|
||||
}
|
||||
if path == "/vmui/timezone" {
|
||||
httpserver.EnableCORS(w, r)
|
||||
if err := handleVMUITimezone(w); err != nil {
|
||||
httpserver.Errorf(w, r, "%s", err)
|
||||
return true
|
||||
}
|
||||
return true
|
||||
}
|
||||
if strings.HasPrefix(path, "/vmui/") {
|
||||
if strings.HasPrefix(path, "/vmui/static/") {
|
||||
// Allow clients caching static contents for long period of time, since it shouldn't change over time.
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/fs"
|
||||
)
|
||||
|
@ -14,6 +15,8 @@ import (
|
|||
var (
|
||||
vmuiCustomDashboardsPath = flag.String("vmui.customDashboardsPath", "", "Optional path to vmui dashboards. "+
|
||||
"See https://github.com/VictoriaMetrics/VictoriaMetrics/tree/master/app/vmui/packages/vmui/public/dashboards")
|
||||
vmuiDefaultTimezone = flag.String("vmui.defaultTimezone", "", "The default timezone to be used in vmui."+
|
||||
"Timezone must be a valid IANA Time Zone. For example: America/New_York, Europe/Berlin, Etc/GMT+3 or Local")
|
||||
)
|
||||
|
||||
// dashboardSettings represents dashboard settings file struct.
|
||||
|
@ -65,6 +68,16 @@ func handleVMUICustomDashboards(w http.ResponseWriter) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func handleVMUITimezone(w http.ResponseWriter) error {
|
||||
tz, err := time.LoadLocation(*vmuiDefaultTimezone)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot load timezone %q: %w", *vmuiDefaultTimezone, err)
|
||||
}
|
||||
response := fmt.Sprintf(`{"timezone": %q}`, tz)
|
||||
writeSuccessResponse(w, []byte(response))
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeSuccessResponse(w http.ResponseWriter, data []byte) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
|
|
@ -7,6 +7,7 @@ Web UI for VictoriaMetrics
|
|||
* [Updating vmui embedded into VictoriaMetrics](#updating-vmui-embedded-into-victoriametrics)
|
||||
* [Predefined dashboards](#predefined-dashboards)
|
||||
* [App mode config options](#app-mode-config-options)
|
||||
* [Timezone configuration](#timezone-configuration)
|
||||
|
||||
----
|
||||
|
||||
|
@ -246,3 +247,39 @@ vmui can be used to paste into other applications
|
|||
```html
|
||||
<div id="root" data-params='{"serverURL":"http://localhost:8428","useTenantID":true,"headerStyles":{"background":"#FFFFFF","color":"#538DE8"},"palette":{"primary":"#538DE8","secondary":"#F76F8E","error":"#FD151B","warning":"#FFB30F","success":"#7BE622","info":"#0F5BFF"}}'></div>
|
||||
```
|
||||
|
||||
----
|
||||
|
||||
## Timezone configuration
|
||||
|
||||
vmui's timezone setting offers flexibility in displaying time data. It can be set through a configuration flag and is adjustable within the vmui interface. This feature caters to various user preferences and time zones.
|
||||
|
||||
### Default Timezone Setting
|
||||
|
||||
#### Via Configuration Flag
|
||||
|
||||
- Set the default timezone using the `--vmui.defaultTimezone` flag.
|
||||
- Accepts a valid IANA Time Zone string (e.g., `America/New_York`, `Europe/Berlin`, `Etc/GMT+3`).
|
||||
- If the flag is unset or invalid, vmui defaults to the browser's local timezone.
|
||||
|
||||
#### User Interface Adjustments
|
||||
|
||||
- Users can change the timezone in the vmui interface.
|
||||
- Any changed setting in the interface overrides the flag's default, persisting for the user.
|
||||
- The timezone specified in the `--vmui.defaultTimezone` flag is included in the vmui's timezone selection dropdown, aiding user choice.
|
||||
|
||||
### Key Points
|
||||
|
||||
- **Fallback to Browser's Local Timezone**: If the flag is not set or an invalid timezone is specified, vmui uses the local timezone of the user's browser.
|
||||
- **User Preference Priority**: User-selected timezones in vmui take precedence over the default set by the flag.
|
||||
- **Cluster Consistency**: Ensure uniform timezone settings across cluster nodes, but individual user interface selections will always override these defaults.
|
||||
|
||||
### Examples
|
||||
|
||||
Setting a default timezone, with user options to change:
|
||||
|
||||
```
|
||||
./victoria-metrics --vmui.defaultTimezone="America/New_York"
|
||||
```
|
||||
|
||||
In this scenario, if a user in Berlin accesses vmui without changing settings, it will default to their browser's local timezone (CET). If they select a different timezone in vmui, this choice will override the `"America/New_York"` setting for that user.
|
||||
|
|
|
@ -29,7 +29,7 @@ const GlobalSettings: FC = () => {
|
|||
|
||||
const appModeEnable = getAppModeEnable();
|
||||
const { serverUrl: stateServerUrl, theme } = useAppState();
|
||||
const { timezone: stateTimezone } = useTimeState();
|
||||
const { timezone: stateTimezone, defaultTimezone } = useTimeState();
|
||||
const { seriesLimits } = useCustomPanelState();
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
@ -78,6 +78,10 @@ const GlobalSettings: FC = () => {
|
|||
setServerUrl(stateServerUrl);
|
||||
}, [stateServerUrl]);
|
||||
|
||||
useEffect(() => {
|
||||
setTimezone(stateTimezone);
|
||||
}, [stateTimezone]);
|
||||
|
||||
const controls = [
|
||||
{
|
||||
show: !appModeEnable && !isLogsApp,
|
||||
|
@ -100,6 +104,7 @@ const GlobalSettings: FC = () => {
|
|||
show: true,
|
||||
component: <Timezones
|
||||
timezoneState={timezone}
|
||||
defaultTimezone={defaultTimezone}
|
||||
onChange={setTimezone}
|
||||
/>
|
||||
},
|
||||
|
|
|
@ -51,6 +51,12 @@ const ServerConfigurator: FC<ServerConfiguratorProps> = ({
|
|||
}
|
||||
}, [enabledStorage]);
|
||||
|
||||
useEffect(() => {
|
||||
if (enabledStorage) {
|
||||
saveToStorage("SERVER_URL", serverUrl);
|
||||
}
|
||||
}, [serverUrl]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="vm-server-configurator__title">
|
||||
|
|
|
@ -12,11 +12,16 @@ import useDeviceDetect from "../../../../hooks/useDeviceDetect";
|
|||
import useBoolean from "../../../../hooks/useBoolean";
|
||||
|
||||
interface TimezonesProps {
|
||||
timezoneState: string
|
||||
onChange: (val: string) => void
|
||||
timezoneState: string;
|
||||
defaultTimezone?: string;
|
||||
onChange: (val: string) => void;
|
||||
}
|
||||
|
||||
const Timezones: FC<TimezonesProps> = ({ timezoneState, onChange }) => {
|
||||
interface PinnedTimezone extends Timezone {
|
||||
title: string
|
||||
}
|
||||
|
||||
const Timezones: FC<TimezonesProps> = ({ timezoneState, defaultTimezone, onChange }) => {
|
||||
const { isMobile } = useDeviceDetect();
|
||||
const timezones = getTimezoneList();
|
||||
|
||||
|
@ -29,6 +34,24 @@ const Timezones: FC<TimezonesProps> = ({ timezoneState, onChange }) => {
|
|||
setFalse: handleCloseList,
|
||||
} = useBoolean(false);
|
||||
|
||||
const pinnedTimezones = useMemo(() => [
|
||||
{
|
||||
title: `Default time (${defaultTimezone})`,
|
||||
region: defaultTimezone,
|
||||
utc: defaultTimezone ? getUTCByTimezone(defaultTimezone) : "UTC"
|
||||
},
|
||||
{
|
||||
title: `Browser Time (${dayjs.tz.guess()})`,
|
||||
region: dayjs.tz.guess(),
|
||||
utc: getUTCByTimezone(dayjs.tz.guess())
|
||||
},
|
||||
{
|
||||
title: "UTC (Coordinated Universal Time)",
|
||||
region: "UTC",
|
||||
utc: "UTC"
|
||||
},
|
||||
].filter(t => t.region) as PinnedTimezone[], [defaultTimezone]);
|
||||
|
||||
const searchTimezones = useMemo(() => {
|
||||
if (!search) return timezones;
|
||||
try {
|
||||
|
@ -40,11 +63,6 @@ const Timezones: FC<TimezonesProps> = ({ timezoneState, onChange }) => {
|
|||
|
||||
const timezonesGroups = useMemo(() => Object.keys(searchTimezones), [searchTimezones]);
|
||||
|
||||
const localTimezone = useMemo(() => ({
|
||||
region: dayjs.tz.guess(),
|
||||
utc: getUTCByTimezone(dayjs.tz.guess())
|
||||
}), []);
|
||||
|
||||
const activeTimezone = useMemo(() => ({
|
||||
region: timezoneState,
|
||||
utc: getUTCByTimezone(timezoneState)
|
||||
|
@ -108,13 +126,16 @@ const Timezones: FC<TimezonesProps> = ({ timezoneState, onChange }) => {
|
|||
onChange={handleChangeSearch}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="vm-timezones-item vm-timezones-list-group-options__item"
|
||||
onClick={createHandlerSetTimezone(localTimezone)}
|
||||
>
|
||||
<div className="vm-timezones-item__title">Browser Time ({localTimezone.region})</div>
|
||||
<div className="vm-timezones-item__utc">{localTimezone.utc}</div>
|
||||
</div>
|
||||
{pinnedTimezones.map((t, i) => t && (
|
||||
<div
|
||||
key={`${i}_${t.region}`}
|
||||
className="vm-timezones-item vm-timezones-list-group-options__item"
|
||||
onClick={createHandlerSetTimezone(t)}
|
||||
>
|
||||
<div className="vm-timezones-item__title">{t.title}</div>
|
||||
<div className="vm-timezones-item__utc">{t.utc}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{timezonesGroups.map(t => (
|
||||
<div
|
||||
|
|
59
app/vmui/packages/vmui/src/hooks/useFetchDefaultTimezone.ts
Normal file
59
app/vmui/packages/vmui/src/hooks/useFetchDefaultTimezone.ts
Normal file
|
@ -0,0 +1,59 @@
|
|||
import { useEffect, useState } from "preact/compat";
|
||||
import { ErrorTypes } from "../types";
|
||||
import { useAppState } from "../state/common/StateContext";
|
||||
import { useTimeDispatch } from "../state/time/TimeStateContext";
|
||||
import { getFromStorage } from "../utils/storage";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
const disabledDefaultTimezone = Boolean(getFromStorage("DISABLED_DEFAULT_TIMEZONE"));
|
||||
|
||||
const useFetchDefaultTimezone = () => {
|
||||
const { serverUrl } = useAppState();
|
||||
const timeDispatch = useTimeDispatch();
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<ErrorTypes | string>("");
|
||||
|
||||
const setTimezone = (timezoneStr: string) => {
|
||||
const timezone = timezoneStr.toLowerCase() === "local" ? dayjs.tz.guess() : timezoneStr;
|
||||
try {
|
||||
dayjs().tz(timezone).isValid();
|
||||
timeDispatch({ type: "SET_DEFAULT_TIMEZONE", payload: timezone });
|
||||
if (disabledDefaultTimezone) return;
|
||||
timeDispatch({ type: "SET_TIMEZONE", payload: timezone });
|
||||
} catch (e) {
|
||||
if (e instanceof Error) setError(`${e.name}: ${e.message}`);
|
||||
}
|
||||
};
|
||||
|
||||
const fetchDefaultTimezone = async () => {
|
||||
if (!serverUrl || process.env.REACT_APP_TYPE) return;
|
||||
setError("");
|
||||
setIsLoading(true);
|
||||
|
||||
try {
|
||||
const response = await fetch(`${serverUrl}/vmui/timezone`);
|
||||
const resp = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
setTimezone(resp.timezone);
|
||||
setIsLoading(false);
|
||||
} else {
|
||||
setError(resp.error);
|
||||
setIsLoading(false);
|
||||
}
|
||||
} catch (e) {
|
||||
setIsLoading(false);
|
||||
if (e instanceof Error) setError(`${e.name}: ${e.message}`);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchDefaultTimezone();
|
||||
}, [serverUrl]);
|
||||
|
||||
return { isLoading, error };
|
||||
};
|
||||
|
||||
export default useFetchDefaultTimezone;
|
||||
|
|
@ -7,7 +7,7 @@ import { getAppModeEnable } from "../../utils/app-mode";
|
|||
import classNames from "classnames";
|
||||
import Footer from "../Footer/Footer";
|
||||
import { routerOptions } from "../../router";
|
||||
import { useFetchDashboards } from "../../pages/PredefinedPanels/hooks/useFetchDashboards";
|
||||
import useFetchDefaultTimezone from "../../hooks/useFetchDefaultTimezone";
|
||||
import useDeviceDetect from "../../hooks/useDeviceDetect";
|
||||
import ControlsAnomalyLayout from "./ControlsAnomalyLayout";
|
||||
|
||||
|
@ -17,7 +17,7 @@ const AnomalyLayout: FC = () => {
|
|||
const { pathname } = useLocation();
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
|
||||
useFetchDashboards();
|
||||
useFetchDefaultTimezone();
|
||||
|
||||
const setDocumentTitle = () => {
|
||||
const defaultTitle = "vmui for vmanomaly";
|
||||
|
|
|
@ -8,12 +8,15 @@ import Footer from "../Footer/Footer";
|
|||
import router, { routerOptions } from "../../router";
|
||||
import useDeviceDetect from "../../hooks/useDeviceDetect";
|
||||
import ControlsLogsLayout from "./ControlsLogsLayout";
|
||||
import useFetchDefaultTimezone from "../../hooks/useFetchDefaultTimezone";
|
||||
|
||||
const LogsLayout: FC = () => {
|
||||
const appModeEnable = getAppModeEnable();
|
||||
const { isMobile } = useDeviceDetect();
|
||||
const { pathname } = useLocation();
|
||||
|
||||
useFetchDefaultTimezone();
|
||||
|
||||
const setDocumentTitle = () => {
|
||||
const defaultTitle = "vmui for VictoriaLogs";
|
||||
const routeTitle = routerOptions[router.logs]?.title;
|
||||
|
|
|
@ -10,6 +10,7 @@ import { routerOptions } from "../../router";
|
|||
import { useFetchDashboards } from "../../pages/PredefinedPanels/hooks/useFetchDashboards";
|
||||
import useDeviceDetect from "../../hooks/useDeviceDetect";
|
||||
import ControlsMainLayout from "./ControlsMainLayout";
|
||||
import useFetchDefaultTimezone from "../../hooks/useFetchDefaultTimezone";
|
||||
|
||||
const MainLayout: FC = () => {
|
||||
const appModeEnable = getAppModeEnable();
|
||||
|
@ -18,6 +19,7 @@ const MainLayout: FC = () => {
|
|||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
|
||||
useFetchDashboards();
|
||||
useFetchDefaultTimezone();
|
||||
|
||||
const setDocumentTitle = () => {
|
||||
const defaultTitle = "vmui";
|
||||
|
|
|
@ -17,6 +17,7 @@ export interface TimeState {
|
|||
period: TimeParams;
|
||||
relativeTime?: string;
|
||||
timezone: string;
|
||||
defaultTimezone?: string;
|
||||
}
|
||||
|
||||
export type TimeAction =
|
||||
|
@ -26,6 +27,7 @@ export type TimeAction =
|
|||
| { type: "RUN_QUERY"}
|
||||
| { type: "RUN_QUERY_TO_NOW"}
|
||||
| { type: "SET_TIMEZONE", payload: string }
|
||||
| { type: "SET_DEFAULT_TIMEZONE", payload: string }
|
||||
|
||||
const timezone = getFromStorage("TIMEZONE") as string || dayjs.tz.guess();
|
||||
setTimezone(timezone);
|
||||
|
@ -90,10 +92,16 @@ export function reducer(state: TimeState, action: TimeAction): TimeState {
|
|||
case "SET_TIMEZONE":
|
||||
setTimezone(action.payload);
|
||||
saveToStorage("TIMEZONE", action.payload);
|
||||
if (state.defaultTimezone) saveToStorage("DISABLED_DEFAULT_TIMEZONE", action.payload !== state.defaultTimezone);
|
||||
return {
|
||||
...state,
|
||||
timezone: action.payload
|
||||
};
|
||||
case "SET_DEFAULT_TIMEZONE":
|
||||
return {
|
||||
...state,
|
||||
defaultTimezone: action.payload
|
||||
};
|
||||
default:
|
||||
throw new Error();
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ export type StorageKeys = "AUTOCOMPLETE"
|
|||
| "SERIES_LIMITS"
|
||||
| "TABLE_COMPACT"
|
||||
| "TIMEZONE"
|
||||
| "DISABLED_DEFAULT_TIMEZONE"
|
||||
| "THEME"
|
||||
| "LOGS_LIMIT"
|
||||
| "EXPLORE_METRICS_TIPS"
|
||||
|
|
|
@ -53,6 +53,8 @@ The sandbox cluster installation is running under the constant load generated by
|
|||
* FEATURE: [vmctl](https://docs.victoriametrics.com/vmctl.html): rename cmd-line flag `vm-native-disable-retries` to `vm-native-disable-per-metric-migration` to better reflect its meaning.
|
||||
* FEATURE: [vmctl](https://docs.victoriametrics.com/vmctl.html): add `-vm-native-src-insecure-skip-verify` and `-vm-native-dst-insecure-skip-verify` command-line flags for native protocol. It can be used for skipping TLS certificate verification when connecting to the source or destination addresses.
|
||||
* FEATURE: [Alerting rules for VictoriaMetrics](https://github.com/VictoriaMetrics/VictoriaMetrics/tree/master/deployment/docker#alerts): add `job` label to `DiskRunsOutOfSpace` alerting rule, so it is easier to understand to which installation the triggered instance belongs.
|
||||
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): add `-vmui.defaultTimezone` flag to set a default timezone. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5375) and [these docs](https://github.com/VictoriaMetrics/VictoriaMetrics/tree/master/app/vmui#timezone-configuration).
|
||||
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): include UTC in the timezone selection dropdown for standardized time referencing. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5375).
|
||||
* FEATURE: add [VictoriaMetrics datasource](https://github.com/VictoriaMetrics/grafana-datasource) to docker compose environment. See [this pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/5363).
|
||||
|
||||
* BUGFIX: properly return errors from [export APIs](https://docs.victoriametrics.com/#how-to-export-time-series). Previously these errors were silently suppressed. See [this pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/5649).
|
||||
|
|
|
@ -1444,6 +1444,8 @@ Below is the output for `/path/to/vmselect -help`:
|
|||
Network timeout for RPC connections from vmselect to vmstorage (Linux only). Lower values reduce the maximum query durations when some vmstorage nodes become unavailable because of networking issues. Read more about TCP_USER_TIMEOUT at https://blog.cloudflare.com/when-tcp-sockets-refuse-to-die/ . See also -vmstorageDialTimeout (default 3s)
|
||||
-vmui.customDashboardsPath string
|
||||
Optional path to vmui dashboards. See https://github.com/VictoriaMetrics/VictoriaMetrics/tree/master/app/vmui/packages/vmui/public/dashboards
|
||||
-vmui.defaultTimezone string
|
||||
The default timezone to be used in vmui. Timezone must be a valid IANA Time Zone. For example: America/New_York, Europe/Berlin, Etc/GMT+3 or Local. See https://github.com/VictoriaMetrics/VictoriaMetrics/tree/master/app/vmui#timezone-configuration
|
||||
```
|
||||
|
||||
### List of command-line flags for vmstorage
|
||||
|
|
|
@ -3043,4 +3043,6 @@ Pass `-help` to VictoriaMetrics in order to see the list of supported command-li
|
|||
Optional URL for proxying requests to vmalert. For example, if -vmalert.proxyURL=http://vmalert:8880 , then alerting API requests such as /api/v1/rules from Grafana will be proxied to http://vmalert:8880/api/v1/rules
|
||||
-vmui.customDashboardsPath string
|
||||
Optional path to vmui dashboards. See https://github.com/VictoriaMetrics/VictoriaMetrics/tree/master/app/vmui/packages/vmui/public/dashboards
|
||||
-vmui.defaultTimezone string
|
||||
The default timezone to be used in vmui. Timezone must be a valid IANA Time Zone. For example: America/New_York, Europe/Berlin, Etc/GMT+3 or Local. See https://github.com/VictoriaMetrics/VictoriaMetrics/tree/master/app/vmui#timezone-configuration
|
||||
```
|
||||
|
|
|
@ -3051,4 +3051,6 @@ Pass `-help` to VictoriaMetrics in order to see the list of supported command-li
|
|||
Optional URL for proxying requests to vmalert. For example, if -vmalert.proxyURL=http://vmalert:8880 , then alerting API requests such as /api/v1/rules from Grafana will be proxied to http://vmalert:8880/api/v1/rules
|
||||
-vmui.customDashboardsPath string
|
||||
Optional path to vmui dashboards. See https://github.com/VictoriaMetrics/VictoriaMetrics/tree/master/app/vmui/packages/vmui/public/dashboards
|
||||
-vmui.defaultTimezone string
|
||||
The default timezone to be used in vmui. Timezone must be a valid IANA Time Zone. For example: America/New_York, Europe/Berlin, Etc/GMT+3 or Local. See https://github.com/VictoriaMetrics/VictoriaMetrics/tree/master/app/vmui#timezone-configuration
|
||||
```
|
||||
|
|
Loading…
Reference in a new issue