diff --git a/frontend/snapshots.yml b/frontend/snapshots.yml
index de63cf19d27d..40b9ddfd540e 100644
--- a/frontend/snapshots.yml
+++ b/frontend/snapshots.yml
@@ -5453,9 +5453,9 @@ snapshots:
products-tracing-operationstable--loading--light:
hash: v1.k794b7964.04bcc55cb9522d72f548084a92d35c0aba20ff4995949bd8b2ee8686f96cb6ac.P_KhZ-T4DKSiE8XyxMJSgUEUR9bDfDvgYnGqSOiR860
products-tracing-virtualizedspanlist--default--dark:
- hash: v1.k794b7964.2d26cb574fb8b9011fcf44c766f491adb06c254b9bc1bb70fa1a14bf757eb72d.HK_kiCD_GlyKPWUd6GcCvJJ5tVWaz6t45kZwDWUYg_4
+ hash: v1.k794b7964.d6cf5ccbc379f67404b237b878878ce7e60512d45869c05c24dfab7784335406.zEh7cw_wZ10BGEuJkOUdjqS8SiWt7386-Cuz4Qc0Les
products-tracing-virtualizedspanlist--default--light:
- hash: v1.k794b7964.21b6c26c19954d60a3447075f70b9c9dcfb5f6032f94c85fa223f728ee02bf1c.dAAs5oIhp4WcsPd9uA5wTgaPWyEeuf7PV46c6WFv-9g
+ hash: v1.k794b7964.7ec7e24fc514e8eac101ce26ef491c0becbbe2cc6aeb8759c1c451b5977e6aa7.cP3_uVsCx_-JFlTOLhjShsAIyaSC9JRkJ4Rnlgog0Kk
products-tracing-virtualizedspanlist--empty--dark:
hash: v1.k794b7964.5590e8e6497de2fefefa74cf07edd5f814a798deb8f7e5f0102a383f5e8ac6c8.ZvuooUS3qT6kDX8yW2_oQ1iDgO1DwVFMAWM517bhCHw
products-tracing-virtualizedspanlist--empty--light:
diff --git a/products/tracing/frontend/TracingLatencyHeatmap.tsx b/products/tracing/frontend/TracingLatencyHeatmap.tsx
index e038cd8a9207..72683f77b71f 100644
--- a/products/tracing/frontend/TracingLatencyHeatmap.tsx
+++ b/products/tracing/frontend/TracingLatencyHeatmap.tsx
@@ -6,6 +6,7 @@ import { Heatmap, type HeatmapBrushData, useChartTheme } from '@posthog/quill-ch
import { dayjs } from 'lib/dayjs'
import { shortTimeZone } from 'lib/utils/timezones'
+import { TRACING_DATE_TIME_FORMAT } from './dateFormats'
import type { TracingLatencyHeatmapData } from './durationBuckets'
const MAX_X_TICKS = 6
@@ -38,7 +39,7 @@ export function TracingLatencyHeatmap({
} else if (hoursDiff <= 48) {
return 'HH:mm'
}
- return 'D MMM HH:mm'
+ return 'MM-DD HH:mm'
}, [data.timeBuckets])
const tickStep = Math.max(1, Math.ceil(data.timeBuckets.length / MAX_X_TICKS))
@@ -57,7 +58,7 @@ export function TracingLatencyHeatmap({
(label: string): string => {
const d = displayTimezone ? dayjs(label).tz(displayTimezone) : dayjs(label)
const tz = displayTimezone === 'UTC' ? 'UTC' : (shortTimeZone(displayTimezone, d.toDate()) ?? 'Local')
- return `${d.format('D MMM YYYY HH:mm:ss')} ${tz}`
+ return `${d.format(TRACING_DATE_TIME_FORMAT)} ${tz}`
},
[displayTimezone]
)
diff --git a/products/tracing/frontend/TracingOperationScene.tsx b/products/tracing/frontend/TracingOperationScene.tsx
index b65091639645..bf1c0ab9c9e0 100644
--- a/products/tracing/frontend/TracingOperationScene.tsx
+++ b/products/tracing/frontend/TracingOperationScene.tsx
@@ -4,7 +4,7 @@ import { IconChevronLeft, IconChevronRight } from '@posthog/icons'
import { LemonButton, LemonSegmentedButton, LemonTag, Link, SpinnerOverlay } from '@posthog/lemon-ui'
import { DateFilter } from 'lib/components/DateFilter/DateFilter'
-import { humanFriendlyDetailedTime } from 'lib/utils/datetime'
+import { TZLabel } from 'lib/components/TZLabel'
import { humanFriendlyNumber } from 'lib/utils/numbers'
import { SceneExport } from 'scenes/sceneTypes'
import { urls } from 'scenes/urls'
@@ -14,6 +14,7 @@ import { SceneDivider } from '~/layout/scenes/components/SceneDivider'
import { SceneTitleSection } from '~/layout/scenes/components/SceneTitleSection'
import { ProductKey } from '~/queries/schema/schema-general'
+import { TRACING_DATE_FORMAT, TRACING_DISPLAY_TIMEZONE, TRACING_TIME_FORMAT } from './dateFormats'
import { formatBucketLabel } from './durationBuckets'
import { OperationHistogram } from './OperationHistogram'
import { errorRate, formatErrorRate } from './OperationsTable'
@@ -162,7 +163,7 @@ export function TracingOperationScene(): JSX.Element {
@@ -216,7 +217,13 @@ export function TracingOperationScene(): JSX.Element {
/>
{currentSample && (
- {humanFriendlyDetailedTime(currentSample.timestamp)}
+
{formatDuration(currentSample.duration_nano)}
{currentSample.status_code === 2 && Error}
diff --git a/products/tracing/frontend/TracingScene.tsx b/products/tracing/frontend/TracingScene.tsx
index cb520e66e964..c94aaa780af0 100644
--- a/products/tracing/frontend/TracingScene.tsx
+++ b/products/tracing/frontend/TracingScene.tsx
@@ -21,6 +21,7 @@ import { ComparisonBar } from './components/Comparison/ComparisonBar'
import { FacetRail } from './components/FacetRail/FacetRail'
import { TraceDrawer } from './components/TraceDrawer/TraceDrawer'
import { VirtualizedSpanList } from './components/VirtualizedSpanList/VirtualizedSpanList'
+import { TRACING_DISPLAY_TIMEZONE } from './dateFormats'
import { tracingEmptyState } from './emptyState/tracingEmptyState'
import { OperationsTable } from './OperationsTable'
import { TraceCompareFlame } from './TraceCompareFlame'
@@ -203,7 +204,7 @@ function TracingSceneContents(): JSX.Element {
sparklineData={sparklineData}
sparklineLoading={sparklineLoading || (isDurationMode && !showHeatmap && durationHistogramLoading)}
onDateRangeChange={setDateRange}
- displayTimezone="UTC"
+ displayTimezone={TRACING_DISPLAY_TIMEZONE}
compare={compareConfig}
visibleRowDateRange={visibleRowDateRange}
durationHistogram={isDurationMode && !showHeatmap ? durationHistogramData : null}
diff --git a/products/tracing/frontend/TracingSparkline.tsx b/products/tracing/frontend/TracingSparkline.tsx
index 21dcc2ad9364..2daac3be6c1a 100644
--- a/products/tracing/frontend/TracingSparkline.tsx
+++ b/products/tracing/frontend/TracingSparkline.tsx
@@ -25,6 +25,7 @@ import { shortTimeZone } from 'lib/utils/timezones'
import { DateRange } from '~/queries/schema/schema-general'
+import { TRACING_DATE_TIME_FORMAT } from './dateFormats'
import {
type TracingDurationHistogramData,
type TracingLatencyHeatmapData,
@@ -116,7 +117,7 @@ export function TracingSparkline({
(label: string): string => {
const d = displayTimezone ? dayjs(label).tz(displayTimezone) : dayjs(label)
const tz = displayTimezone === 'UTC' ? 'UTC' : (shortTimeZone(displayTimezone, d.toDate()) ?? 'Local')
- return `${d.format('D MMM YYYY HH:mm:ss')} ${tz}`
+ return `${d.format(TRACING_DATE_TIME_FORMAT)} ${tz}`
},
[displayTimezone]
)
diff --git a/products/tracing/frontend/components/Comparison/ComparisonBar.tsx b/products/tracing/frontend/components/Comparison/ComparisonBar.tsx
index 3ee1e9de6dcc..493605af6825 100644
--- a/products/tracing/frontend/components/Comparison/ComparisonBar.tsx
+++ b/products/tracing/frontend/components/Comparison/ComparisonBar.tsx
@@ -5,6 +5,7 @@ import { LemonButton, LemonTag } from '@posthog/lemon-ui'
import { dayjs } from 'lib/dayjs'
+import { TRACING_DATE_FORMAT } from '../../dateFormats'
import { COMPARE_CURRENT_BORDER, COMPARE_PREVIOUS_BORDER } from '../../SparklineCompareOverlay'
import { type OverlayWindow, TIME_COMPARE_PRESET_DEFS, tracingFiltersLogic } from '../../tracingFiltersLogic'
@@ -13,8 +14,8 @@ import { type OverlayWindow, TIME_COMPARE_PRESET_DEFS, tracingFiltersLogic } fro
function formatComparisonWindow(window: OverlayWindow): string {
const start = dayjs(window.startMs).utc()
const end = dayjs(window.endMs).utc()
- const endFormat = start.isSame(end, 'day') ? 'HH:mm' : 'MMM D, HH:mm'
- return `${start.format('MMM D, HH:mm')} – ${end.format(endFormat)} UTC`
+ const endFormat = start.isSame(end, 'day') ? 'HH:mm' : `${TRACING_DATE_FORMAT} HH:mm`
+ return `${start.format(`${TRACING_DATE_FORMAT} HH:mm`)} – ${end.format(endFormat)} UTC`
}
function ComparisonPill({ color, label, detail }: { color: string; label: string; detail: string }): JSX.Element {
diff --git a/products/tracing/frontend/components/TraceDrawer/SpanSummaryHeader.tsx b/products/tracing/frontend/components/TraceDrawer/SpanSummaryHeader.tsx
index 2b260a3caf76..b1825fd52b90 100644
--- a/products/tracing/frontend/components/TraceDrawer/SpanSummaryHeader.tsx
+++ b/products/tracing/frontend/components/TraceDrawer/SpanSummaryHeader.tsx
@@ -4,8 +4,10 @@ import { LemonTag } from '@posthog/lemon-ui'
import { getSeriesColor } from 'lib/colors'
import { CopyToClipboardInline } from 'lib/components/CopyToClipboard'
+import { TZLabel } from 'lib/components/TZLabel'
import { dayjs } from 'lib/dayjs'
+import { TRACING_DATE_FORMAT, TRACING_DISPLAY_TIMEZONE, TRACING_TIME_FORMAT } from '../../dateFormats'
import { deriveSpanSummary } from '../../spanSummary'
import { formatDuration } from '../../TraceWaterfallView'
import type { Span } from '../../types'
@@ -80,12 +82,18 @@ export function SpanSummaryHeader({
)}
{formatDuration(summary.durationNano)}
- {/* UTC to match the waterfall/sparkline (displayTimezone="UTC"). end shows time-only —
- same day as start in all but pathological spans, so the date would just be noise. */}
+ {/* UTC to match the waterfall/sparkline. end shows time-only — same day as start in
+ all but pathological spans, so the date would just be noise. */}
- {dayjs(summary.timestamp).tz('UTC').format('MMM D HH:mm:ss.SSS')}
+
{' → '}
- {dayjs(summary.endTimestamp).tz('UTC').format('HH:mm:ss.SSS')}
+ {dayjs(summary.endTimestamp).tz(TRACING_DISPLAY_TIMEZONE).format(TRACING_TIME_FORMAT)}
diff --git a/products/tracing/frontend/components/VirtualizedSpanList/VirtualizedSpanList.tsx b/products/tracing/frontend/components/VirtualizedSpanList/VirtualizedSpanList.tsx
index a40ce23bad54..fc33da1f0dab 100644
--- a/products/tracing/frontend/components/VirtualizedSpanList/VirtualizedSpanList.tsx
+++ b/products/tracing/frontend/components/VirtualizedSpanList/VirtualizedSpanList.tsx
@@ -5,9 +5,11 @@ import { LemonTag } from '@posthog/lemon-ui'
import { AutoSizer } from 'lib/components/AutoSizer'
import { SizeProps } from 'lib/components/AutoSizer/AutoSizer'
+import { TZLabel } from 'lib/components/TZLabel'
import { SortingIndicator } from 'lib/lemon-ui/LemonTable/sorting'
import { cn } from 'lib/utils/css-classes'
+import { TRACING_DATE_FORMAT, TRACING_DISPLAY_TIMEZONE, TRACING_TIME_FORMAT } from '../../dateFormats'
import { formatDuration } from '../../TraceWaterfallView'
import type { TracingOrderBy, TracingOrderDirection } from '../../tracingFiltersLogic'
import { SPAN_KIND_LABELS, STATUS_CODE_LABELS } from '../../types'
@@ -25,7 +27,7 @@ const LOAD_MORE_THRESHOLD = 10
// Default column widths (px), in render order. Anyone can drag a column wider or narrower from here.
const SPAN_COLUMNS: ResizableColumnSpec[] = [
- { key: 'timestamp', width: 190 },
+ { key: 'timestamp', width: 215 },
{ key: 'name', width: 320, grow: true },
{ key: 'service', width: 200 },
{ key: 'kind', width: 90 },
@@ -164,7 +166,17 @@ function SpanRow({
role="button"
tabIndex={0}
>
- {new Date(span.timestamp).toLocaleString()}
+
+
+
+
+
{span.name}
diff --git a/products/tracing/frontend/dateFormats.ts b/products/tracing/frontend/dateFormats.ts
new file mode 100644
index 000000000000..d46b20e4c277
--- /dev/null
+++ b/products/tracing/frontend/dateFormats.ts
@@ -0,0 +1,11 @@
+// Tracing shows timestamps the way Logs does: an ISO-ordered date and a 24-hour clock. A
+// month-first date is ambiguous outside the US.
+export const TRACING_DATE_FORMAT = 'YYYY-MM-DD'
+export const TRACING_TIME_FORMAT = 'HH:mm:ss.SSS'
+
+// Charts and range pills drop the milliseconds. A bucket edge is never sub-second.
+export const TRACING_DATE_TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss'
+
+// Every chart, axis, and timestamp in the product reads in UTC, so a span lines up with the
+// sparkline above it. TZLabel converts to the reader's own timezone on hover.
+export const TRACING_DISPLAY_TIMEZONE = 'UTC'
diff --git a/products/tracing/frontend/tracingDataLogic.ts b/products/tracing/frontend/tracingDataLogic.ts
index 1debf7c055d2..52175f0f5590 100644
--- a/products/tracing/frontend/tracingDataLogic.ts
+++ b/products/tracing/frontend/tracingDataLogic.ts
@@ -29,6 +29,7 @@ import { PropertyGroupFilter } from '~/types'
import type { DateRange } from '../../../frontend/src/queries/schema/schema-general'
import type { UniversalFiltersGroup } from '../../../frontend/src/types'
+import { TRACING_DATE_FORMAT } from './dateFormats'
import {
type DurationHistogramRow,
type LatencyHeatmapRow,
@@ -1264,7 +1265,7 @@ export const tracingDataLogic = kea([
(accumulator, currentItem) => {
if (currentItem.time !== lastTime) {
labels.push(
- humanFriendlyDetailedTime(currentItem.time, 'YYYY-MM-DD', 'HH:mm:ss', {
+ humanFriendlyDetailedTime(currentItem.time, TRACING_DATE_FORMAT, 'HH:mm:ss', {
timestampStyle: 'absolute',
})
)