diff --git a/packages/views/common/timezone-select.tsx b/packages/views/common/timezone-select.tsx
new file mode 100644
index 0000000000..b6eab16f37
--- /dev/null
+++ b/packages/views/common/timezone-select.tsx
@@ -0,0 +1,112 @@
+"use client";
+
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@multica/ui/components/ui/select";
+
+// Common IANA zones surfaced as quick picks. Used as the fallback option set
+// when Intl.supportedValuesOf is not available, and promoted to the top of
+// the list when it is.
+const COMMON_TIMEZONES = [
+ "UTC",
+ "America/Los_Angeles",
+ "America/Denver",
+ "America/Chicago",
+ "America/New_York",
+ "America/Sao_Paulo",
+ "Europe/London",
+ "Europe/Berlin",
+ "Europe/Paris",
+ "Europe/Moscow",
+ "Africa/Cairo",
+ "Asia/Dubai",
+ "Asia/Kolkata",
+ "Asia/Bangkok",
+ "Asia/Shanghai",
+ "Asia/Singapore",
+ "Asia/Tokyo",
+ "Australia/Sydney",
+ "Pacific/Auckland",
+];
+
+export function browserTimezone(): string {
+ try {
+ const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
+ return tz || "UTC";
+ } catch {
+ return "UTC";
+ }
+}
+
+type IntlWithSupportedValues = typeof Intl & {
+ supportedValuesOf?: (key: "timeZone") => string[];
+};
+
+function supportedTimezones(): string[] {
+ try {
+ const supported = (Intl as IntlWithSupportedValues).supportedValuesOf?.(
+ "timeZone",
+ );
+ return supported && supported.length > 0 ? supported : COMMON_TIMEZONES;
+ } catch {
+ return COMMON_TIMEZONES;
+ }
+}
+
+export function timezoneOptions(current: string): string[] {
+ const browser = browserTimezone();
+ return Array.from(
+ new Set([current, browser, ...COMMON_TIMEZONES, ...supportedTimezones()]),
+ ).filter(Boolean);
+}
+
+// Shared single-select timezone picker. Surfaces the browser-resolved zone
+// with a translated suffix (passed in by the caller — the picker itself stays
+// i18n-namespace agnostic), followed by a curated set of common IANA zones
+// and everything Intl.supportedValuesOf exposes.
+export function TimezoneSelect({
+ value,
+ onValueChange,
+ browserSuffix,
+ disabled,
+ triggerClassName,
+}: {
+ value: string;
+ onValueChange: (next: string) => void;
+ browserSuffix: string;
+ disabled?: boolean;
+ triggerClassName?: string;
+}) {
+ const browser = browserTimezone();
+ const options = timezoneOptions(value);
+ const render = (tz: string) =>
+ tz === browser ? `${tz}${browserSuffix}` : tz;
+
+ return (
+
+ );
+}
diff --git a/packages/views/dashboard/components/dashboard-page.tsx b/packages/views/dashboard/components/dashboard-page.tsx
index 096b71bcb9..cda7fee430 100644
--- a/packages/views/dashboard/components/dashboard-page.tsx
+++ b/packages/views/dashboard/components/dashboard-page.tsx
@@ -25,6 +25,10 @@ import { KpiCard } from "../../runtimes/components/shared";
import { DailyCostChart } from "../../runtimes/components/charts";
import { ProjectIcon } from "../../projects/components/project-icon";
import { ActorAvatar } from "../../common/actor-avatar";
+import {
+ TimezoneSelect,
+ browserTimezone,
+} from "../../common/timezone-select";
import { formatTokens } from "../../runtimes/utils";
import { useT } from "../../i18n";
import {
@@ -106,9 +110,14 @@ function Segmented({
*/
export function DashboardPage() {
const { t } = useT("usage");
+ const { t: tRuntimes } = useT("runtimes");
const wsId = useWorkspaceId();
const [days, setDays] = useState(30);
const [projectValue, setProjectValue] = useState(ALL_PROJECTS);
+ // Default to the browser's resolved zone so day-boundary buckets match the
+ // user's local clock on first render. Pure client-state — the rollup queries
+ // are zone-agnostic today; this is the UI affordance the user can pin.
+ const [timezone, setTimezone] = useState(() => browserTimezone());
// The user can save model prices from the runtimes page; re-render when
// they do so the dashboard reflects the new rates.
@@ -176,12 +185,18 @@ export function DashboardPage() {
return (
-
-
-
-
{t(($) => $.title)}
+ {/* h-auto + min-h-12 + flex-wrap: the toolbar (project filter, range
+ switch, timezone select) overflows the single h-12 row on narrow
+ and medium widths once the timezone picker is added — letting the
+ right cluster wrap underneath keeps every control reachable
+ without an off-screen bleed. Wider viewports still render the
+ original single row. */}
+
+