From 90976b798848ccc5bcb9c9b67a302f51609882ad Mon Sep 17 00:00:00 2001
From: "kemal.earth" <606977+kemaldotearth@users.noreply.github.com>
Date: Thu, 25 Sep 2025 15:35:54 +0100
Subject: [PATCH] fix(studio): human readable formatting for total consumed
times (#39011)
* fix: human readable formatting for total consumed times
* chore: accidental turkish spelling of performance
* fix: imports for renamed utils file
* feat: add tests for formatDuration util
---
.../QueryPerformance/QueryDetail.tsx | 21 +++------------
.../QueryPerformance.utils.test.ts | 24 +++++++++++++++++
.../QueryPerformance.utils.ts | 26 +++++++++++++++++++
.../QueryPerformance/QueryPerformanceGrid.tsx | 9 +++----
4 files changed, 56 insertions(+), 24 deletions(-)
create mode 100644 apps/studio/components/interfaces/QueryPerformance/QueryPerformance.utils.test.ts
create mode 100644 apps/studio/components/interfaces/QueryPerformance/QueryPerformance.utils.ts
diff --git a/apps/studio/components/interfaces/QueryPerformance/QueryDetail.tsx b/apps/studio/components/interfaces/QueryPerformance/QueryDetail.tsx
index 78f7b82912..f35e1e9e30 100644
--- a/apps/studio/components/interfaces/QueryPerformance/QueryDetail.tsx
+++ b/apps/studio/components/interfaces/QueryPerformance/QueryDetail.tsx
@@ -11,6 +11,7 @@ import {
QUERY_PERFORMANCE_COLUMNS,
QUERY_PERFORMANCE_REPORT_TYPES,
} from './QueryPerformance.constants'
+import { formatDuration } from './QueryPerformance.utils'
interface QueryDetailProps {
reportType: QUERY_PERFORMANCE_REPORT_TYPES
@@ -41,22 +42,6 @@ export const QueryDetail = ({ selectedRow, onClickViewSuggestion }: QueryDetailP
const [isExpanded, setIsExpanded] = useState(false)
- const formatDuration = (seconds: number) => {
- const dur = dayjs.duration(seconds, 'seconds')
-
- const minutes = Math.floor(dur.asMinutes())
- const remainingSeconds = dur.seconds() + dur.milliseconds() / 1000
-
- const parts = []
- if (minutes > 0) parts.push(`${minutes}m`)
- if (remainingSeconds > 0) {
- const formattedSeconds = remainingSeconds.toFixed(2)
- parts.push(`${formattedSeconds}s`)
- }
-
- return parts.join(' ')
- }
-
return (
@@ -146,10 +131,10 @@ export const QueryDetail = ({ selectedRow, onClickViewSuggestion }: QueryDetailP
- {formatDuration(totalTime / 1000)}
+ {formatDuration(totalTime)}
) : (
diff --git a/apps/studio/components/interfaces/QueryPerformance/QueryPerformance.utils.test.ts b/apps/studio/components/interfaces/QueryPerformance/QueryPerformance.utils.test.ts
new file mode 100644
index 0000000000..fe898f94d9
--- /dev/null
+++ b/apps/studio/components/interfaces/QueryPerformance/QueryPerformance.utils.test.ts
@@ -0,0 +1,24 @@
+import { describe, it, expect } from 'vitest'
+import { formatDuration } from './QueryPerformance.utils'
+
+describe('formatDuration', () => {
+ it('should format seconds', () => {
+ expect(formatDuration(1000)).toBe('1.00s')
+ expect(formatDuration(30000)).toBe('30.00s')
+ })
+
+ it('should format minutes and seconds', () => {
+ expect(formatDuration(60000)).toBe('1m')
+ expect(formatDuration(125000)).toBe('2m 5s')
+ })
+
+ it('should format hours, minutes and seconds', () => {
+ expect(formatDuration(3600000)).toBe('1h')
+ expect(formatDuration(3661000)).toBe('1h 1m 1s')
+ })
+
+ it('should format days, hours, minutes and seconds', () => {
+ expect(formatDuration(86400000)).toBe('1d')
+ expect(formatDuration(90061000)).toBe('1d 1h 1m 1s')
+ })
+})
diff --git a/apps/studio/components/interfaces/QueryPerformance/QueryPerformance.utils.ts b/apps/studio/components/interfaces/QueryPerformance/QueryPerformance.utils.ts
new file mode 100644
index 0000000000..ce7d211123
--- /dev/null
+++ b/apps/studio/components/interfaces/QueryPerformance/QueryPerformance.utils.ts
@@ -0,0 +1,26 @@
+import dayjs from 'dayjs'
+import duration from 'dayjs/plugin/duration'
+
+dayjs.extend(duration)
+
+export const formatDuration = (milliseconds: number) => {
+ const duration = dayjs.duration(milliseconds, 'milliseconds')
+
+ const days = Math.floor(duration.asDays())
+ const hours = duration.hours()
+ const minutes = duration.minutes()
+ const seconds = duration.seconds()
+ const totalSeconds = duration.asSeconds()
+
+ if (totalSeconds < 60) {
+ return `${totalSeconds.toFixed(2)}s`
+ }
+
+ const parts = []
+ if (days > 0) parts.push(`${days}d`)
+ if (hours > 0) parts.push(`${hours}h`)
+ if (minutes > 0) parts.push(`${minutes}m`)
+ if (seconds > 0) parts.push(`${seconds}s`)
+
+ return parts.length > 0 ? parts.join(' ') : '0s'
+}
diff --git a/apps/studio/components/interfaces/QueryPerformance/QueryPerformanceGrid.tsx b/apps/studio/components/interfaces/QueryPerformance/QueryPerformanceGrid.tsx
index 62e3bb4973..fc8122627a 100644
--- a/apps/studio/components/interfaces/QueryPerformance/QueryPerformanceGrid.tsx
+++ b/apps/studio/components/interfaces/QueryPerformance/QueryPerformanceGrid.tsx
@@ -32,6 +32,7 @@ import {
QUERY_PERFORMANCE_ROLE_DESCRIPTION,
} from './QueryPerformance.constants'
import { useQueryPerformanceSort } from './hooks/useQueryPerformanceSort'
+import { formatDuration } from './QueryPerformance.utils'
interface QueryPerformanceGridProps {
queryPerformanceQuery: DbQueryHook
@@ -173,14 +174,10 @@ export const QueryPerformanceGrid = ({ queryPerformanceQuery }: QueryPerformance
/
- {(totalTime / 1000).toLocaleString(undefined, {
- minimumFractionDigits: 2,
- maximumFractionDigits: 2,
- })}
- s
+ {formatDuration(totalTime)}
) : (