diff --git a/web/src/components/logstream/schema.vue b/web/src/components/logstream/schema.vue
index 2e6bf706b3..830a598025 100644
--- a/web/src/components/logstream/schema.vue
+++ b/web/src/components/logstream/schema.vue
@@ -41,7 +41,7 @@ along with this program. If not, see .
- {{ t("logStream.utc") }}
+ {{ displayTimezone }}
{{ indexData.stats.doc_time_min }}
@@ -792,7 +792,10 @@ import { computed, defineComponent, onBeforeMount, ref, watch } from "vue";
import { raw, useI18nTyped, type I18nText } from "@/types/i18n";
import { useStore } from "vuex";
import useTheme from "@/composables/useTheme";
-import { convertUnixToDateFormat as convertUnixToFormat, formatTimestamp } from "@/utils/date";
+import {
+ convertUnixToDateFormat as convertUnixToFormat,
+ formatTimestampInTimezone,
+} from "@/utils/date";
import streamService from "../../services/stream";
import segment from "../../services/segment_analytics";
import {
@@ -917,6 +920,11 @@ export default defineComponent({
const { t } = useI18nTyped();
const store = useStore();
const { isDark } = useTheme();
+ // Timezone used for the stream-stats time range: the user's selected
+ // timezone, falling back to the browser's zone (never a hardcoded "UTC").
+ const displayTimezone = computed(
+ () => store.state.timezone || Intl.DateTimeFormat().resolvedOptions().timeZone,
+ );
const indexData: any = ref(defaultValue());
const updateSettingsForm: any = ref(null);
const isCloud = config.isCloud;
@@ -1355,13 +1363,15 @@ export default defineComponent({
indexData.value.stats.original_doc_time_max = streamResponse.stats.doc_time_max;
indexData.value.stats.original_doc_time_min = streamResponse.stats.doc_time_min;
- indexData.value.stats.doc_time_max = formatTimestamp(
+ indexData.value.stats.doc_time_max = formatTimestampInTimezone(
parseInt(streamResponse.stats.doc_time_max),
"YYYY-MM-DDTHH:mm:ss:SS",
+ displayTimezone.value,
);
- indexData.value.stats.doc_time_min = formatTimestamp(
+ indexData.value.stats.doc_time_min = formatTimestampInTimezone(
parseInt(streamResponse.stats.doc_time_min),
"YYYY-MM-DDTHH:mm:ss:SS",
+ displayTimezone.value,
);
indexData.value.defined_schema_fields = streamResponse.settings.defined_schema_fields || [];
@@ -2439,6 +2449,7 @@ export default defineComponent({
t,
raw,
store,
+ displayTimezone,
config,
dateChangeValue,
isCloud,
diff --git a/web/src/utils/date.ts b/web/src/utils/date.ts
index 99c7d5d423..42c18b3461 100644
--- a/web/src/utils/date.ts
+++ b/web/src/utils/date.ts
@@ -14,6 +14,7 @@
// along with this program. If not, see .
import { format as dfFormat, sub } from "date-fns";
+import { formatInTimeZone } from "date-fns-tz";
import { DateTime as _DateTime } from "luxon";
// ---------------------------------------------------------------------------
@@ -267,6 +268,15 @@ export function formatTimestamp(us: number, format: string): string {
return formatDate(us / 1000, format);
}
+/**
+ * Format a microsecond timestamp in a specific IANA timezone.
+ * Mirrors formatTimestamp but renders in the given timezone instead of the
+ * browser's local zone, so the displayed value matches a timezone label.
+ */
+export function formatTimestampInTimezone(us: number, format: string, timezone: string): string {
+ return formatInTimeZone(us / 1000, timezone, normalizeFormat(format));
+}
+
/**
* Format a nanosecond timestamp to a string.
* Converts nanoseconds to milliseconds internally.