Skip to content

[BUGFIX] (dashboards): apply dashboard.spec.timezone to TimeZoneProvider - #298

Merged
celian-garcia merged 2 commits into
perses:mainfrom
colivi:fix/dashboard-timezone-provider
Sep 18, 2026
Merged

celian-garcia merged 2 commits into
perses:mainfrom
colivi:fix/dashboard-timezone-provider

Conversation

@colivi

@colivi colivi commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

Description

"dashboard.spec.timezone" was applied only to the toolbar time picker ("DashboardApp" → "toolBarTimezone"), while charts used "TimeRangeProviderWithQueryParams" → "useTimeZoneParams('local')".

Result: the picker could show GMT+0 / UTC while panels still formatted in browser local.

Approach

Keep time range and timezone as separate concerns (credits to Celian):

  • "TimeRangeProviderWithQueryParams" / "TimeRangeProviderBasic" → range only
  • "TimeZoneProvider" is composed at the view layer

TimeRangeProviderWithQueryParams
└── TimeZoneProvider ← ViewDashboard / ViewExplore
└── charts + toolbars (useTimeZone)

Fix

  • ViewDashboard: resolve spec.timezone || userPreferenceTimezone || 'local' via useTimeZoneParams, wrap children in TimeZoneProvider
  • ViewExplore: same composition (URL / local only — no dashboard spec)
  • Sticky toolbar: seed useTimeZoneParams from TimeZoneProvider context (no hardcoded 'local')
  • Do not nest timezone inside the TimeRange helpers

Hierarchy (charts + toolbar)

dashboard.spec.timezone > user preference > 'local' (URL ?tz= still overrides via useTimeZoneParams)

Checklist

  • Pull request has a descriptive title and context useful to a reviewer.
  • Pull request title follows the [<catalog_entry>] naming convention.
  • All commits have DCO signoffs.

@colivi
colivi requested a review from a team as a code owner September 16, 2026 08:42
@colivi
colivi force-pushed the fix/dashboard-timezone-provider branch 4 times, most recently from 230a098 to 24635a3 Compare September 16, 2026 11:33
Comment thread dashboards/src/utils/timezone.ts Outdated
* Hierarchy (highest wins):
* 1. dashboard.spec.timezone (when non-empty)
* 2. user preference timezone (when non-empty)
* 3. browser local (`'local'`)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This hierarchy means a dashboard defining a timezone will "lock it" disallowing the user to use a custom one?

@colivi colivi Sep 16, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes for the default view, by design (see docs/concepts/timezone.md):
spec.timezone > user preference > local, so ops dashboards can pin UTC for everyone.

Users can still override at runtime via the time-range timezone control / ?tz= (useTimeZoneParams), which takes precedence over the resolved initial value.

setRefreshInterval={setRefreshInterval}
>
<TimeZoneProvider timeZone={timeZone}>{children}</TimeZoneProvider>
{children}

@jgbernalp jgbernalp Sep 16, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a breaking change for consumers of this provider as it no longer uses the timezone provider, this creates the duplication under other components. Instead of removing this, we can add another provider in the case an override (the dashboard spec timezone) is needed. As the hook will take the value from the closest provider

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, restored TimeZoneProvider inside TimeRangeProviderWithQueryParams so existing consumers keep working.
ViewDashboard nests an additional provider with spec.timezone || user pref, so useTimeZone() picks the closest override without a breaking API change.

@colivi
colivi force-pushed the fix/dashboard-timezone-provider branch from 24635a3 to 1f5610d Compare September 16, 2026 13:43
Comment thread explore/src/views/ViewExplore/ViewExplore.tsx Outdated
Comment thread plugin-system/src/runtime/TimeRangeProvider/TimeRangeProviders.tsx Outdated
Comment thread plugin-system/src/runtime/TimeRangeProvider/TimeRangeProviders.tsx Outdated
@colivi
colivi force-pushed the fix/dashboard-timezone-provider branch 2 times, most recently from cfb033a to af07b2b Compare September 16, 2026 16:09
@shahrokni
shahrokni self-requested a review September 17, 2026 08:29
@shahrokni

Copy link
Copy Markdown
Contributor

⚠️ I'd like to review. Please keep it on hold and do not merge!

Comment thread dashboards/src/utils/timezone.ts Outdated
Comment on lines +30 to +38
const fromDashboard = dashboardTimezone?.trim();
if (fromDashboard) {
return fromDashboard;
}
const fromUser = userPreferenceTimezone?.trim();
if (fromUser) {
return fromUser;
}
return 'local';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion

Could it be simplified?

return (dashboardTimezone?.trim() || userPreferenceTimezone?.trim() || "local");

@shahrokni shahrokni left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image

I am not sure why the second TimeZoneProvider has been added. In the hierarchy, the TimeRangeProviderWithQueryParams already provides the tz provider.

Apart from that, in my opinion, an ideal implementation would take the tz from

  1. try to fetch tz from the query params, does not exist?
  2. try to fetch it from spec.tz , does not exist?
  3. try to fetch it from the user preferences, does not exits
  4. set it to local
  5. Update tz to the last read value, establish a loop of retrieve/update. (at this point the tz could not be technically undefined anymore. It is showing either spec, user prefrences, or local)

Whenever the time zone is edited by the user the tz query param should be updated. Subsequently, the updated tz should be retrieved and used accordingly. Let's take a look at the fllowing code:

/* initialTimeZone  is set by spec || user preference || local */
export function useTimeZoneParams(initialTimeZone?: string): { timeZone: string; setTimeZone: (tz: string) => void } {
  const [query, setQuery] = useQueryParams(timeZoneQueryConfig, { updateType: 'replaceIn' });
  const { tz } = query;

   /* >>>>> interesting line <<<<< */
  const timeZone = (tz as string | undefined) ?? initialTimeZone ?? 'local';

  const setTimeZone = useCallback(
    (newTz: string) => {
      setQuery({ tz: newTz });
    },
    [setQuery],
  );

  return { timeZone, setTimeZone };
}

Pass resolveDashboardTimeZone(spec, userPref) as initialTimeZone into
TimeRangeProviderWithQueryParams (single TimeZoneProvider).

Runtime order via useTimeZoneParams:
1. URL ?tz=
2. dashboard.spec.timezone || user preference || local

No second nested TimeZoneProvider. Sticky toolbar seeds from context.

Signed-off-by: colivi <charles.olivi@gmail.com>
@colivi
colivi force-pushed the fix/dashboard-timezone-provider branch from 3d08b55 to a578a87 Compare September 17, 2026 14:22
@shahrokni

Copy link
Copy Markdown
Contributor

Tested and no issue found

image

Scenario 1

Changed the tz

  1. dashbaord was updated
  2. query param was updated

Scenario 2

Copy/Paste the url and open it in a new tab
tz reflects in url, toolbar and dashbaoard

@celian-garcia
celian-garcia merged commit 4a2420c into perses:main Sep 18, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Development

Successfully merging this pull request may close these issues.

4 participants