diff --git a/apps/studio/components/interfaces/Database/Migrations/Migrations.tsx b/apps/studio/components/interfaces/Database/Migrations/Migrations.tsx index 8cc432d903b..f67be422436 100644 --- a/apps/studio/components/interfaces/Database/Migrations/Migrations.tsx +++ b/apps/studio/components/interfaces/Database/Migrations/Migrations.tsx @@ -27,7 +27,7 @@ import { InlineLink } from '@/components/ui/InlineLink' import { DatabaseMigration, useMigrationsQuery } from '@/data/database/migrations-query' import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject' import { DOCS_URL } from '@/lib/constants' -import { parseMigrationVersion } from '@/lib/migration-utils' +import { formatMigrationVersionLabel, parseMigrationVersion } from '@/lib/migration-utils' const Migrations = () => { const [search, setSearch] = useState('') @@ -119,9 +119,7 @@ const Migrations = () => { {migrations.length > 0 ? ( migrations.map((migration) => { const versionDayjs = parseMigrationVersion(migration.version) - const label = versionDayjs - ? versionDayjs.format('DD MMM YYYY, HH:mm:ss') - : 'Unknown' + const label = formatMigrationVersionLabel(migration.version) const insertedAt = versionDayjs ? versionDayjs.toISOString() : undefined return ( diff --git a/apps/studio/lib/migration-utils.test.ts b/apps/studio/lib/migration-utils.test.ts index 77a4dcaa781..a23a71913d2 100644 --- a/apps/studio/lib/migration-utils.test.ts +++ b/apps/studio/lib/migration-utils.test.ts @@ -1,7 +1,10 @@ import dayjs from 'dayjs' +import utc from 'dayjs/plugin/utc' import { describe, expect, it } from 'vitest' -import { parseMigrationVersion } from './migration-utils' +import { formatMigrationVersionLabel, parseMigrationVersion } from './migration-utils' + +dayjs.extend(utc) describe('parseMigrationVersion', () => { it('should parse valid migration version in YYYYMMDDHHmmss format', () => { @@ -70,4 +73,24 @@ describe('parseMigrationVersion', () => { expect(result?.toISOString()).toBeDefined() expect(result?.format('DD MMM YYYY')).toBe('28 Nov 2023') }) + + it('should parse version digits as UTC so the UTC time is preserved exactly', () => { + // Migration versions are stored as UTC in the DB. Parsing without UTC mode + // would shift the time by the viewer's local offset, showing wrong UTC values. + const result = parseMigrationVersion('20231128095400') + + expect(result?.toISOString()).toBe('2023-11-28T09:54:00.000Z') + }) +}) + +describe('formatMigrationVersionLabel', () => { + it('should format a valid version as a UTC date string', () => { + expect(formatMigrationVersionLabel('20231128095400')).toBe('28 Nov 2023, 09:54:00') + }) + + it('should return Unknown for an invalid version', () => { + expect(formatMigrationVersionLabel('001')).toBe('Unknown') + expect(formatMigrationVersionLabel(null)).toBe('Unknown') + expect(formatMigrationVersionLabel(undefined)).toBe('Unknown') + }) }) diff --git a/apps/studio/lib/migration-utils.ts b/apps/studio/lib/migration-utils.ts index dceef55ab64..dc829d3be23 100644 --- a/apps/studio/lib/migration-utils.ts +++ b/apps/studio/lib/migration-utils.ts @@ -6,7 +6,7 @@ import dayjs, { Dayjs } from 'dayjs' * However, some projects may have custom version formats (e.g., "001", "002") that cannot be parsed as dates. * * @param version - Migration version string - * @returns Dayjs object if the version is a valid datetime, null otherwise + * @returns Dayjs object (UTC mode) if the version is a valid datetime, undefined otherwise * * @example * const parsed = parseMigrationVersion('20231128095400') @@ -16,7 +16,7 @@ import dayjs, { Dayjs } from 'dayjs' * } * * @example - * const invalid = parseMigrationVersion('001') // returns null + * const invalid = parseMigrationVersion('001') // returns undefined */ export function parseMigrationVersion(version: string | null | undefined): Dayjs | undefined { if (!version) return undefined @@ -24,6 +24,15 @@ export function parseMigrationVersion(version: string | null | undefined): Dayjs // Must contain only digits if (!/^\d{14}$/.test(version)) return undefined - const parsed = dayjs(version, 'YYYYMMDDHHmmss', true) + const parsed = dayjs.utc(version, 'YYYYMMDDHHmmss', true) return parsed.isValid() ? parsed : undefined } + +/** + * Formats a migration version string as a human-readable UTC date label. + * Returns 'Unknown' if the version cannot be parsed. + */ +export function formatMigrationVersionLabel(version: string | null | undefined): string { + const parsed = parseMigrationVersion(version) + return parsed ? parsed.format('DD MMM YYYY, HH:mm:ss') : 'Unknown' +} diff --git a/apps/studio/pages/project/[ref]/functions/[functionSlug]/invocations.tsx b/apps/studio/pages/project/[ref]/functions/[functionSlug]/invocations.tsx index 8f8ed161d6d..db6a67fb74f 100644 --- a/apps/studio/pages/project/[ref]/functions/[functionSlug]/invocations.tsx +++ b/apps/studio/pages/project/[ref]/functions/[functionSlug]/invocations.tsx @@ -21,7 +21,7 @@ export const LogPage: NextPageWithLayout = () => { condensedLayout projectRef={ref as string} queryType="fn_edge" - filterOverride={{ function_id: selectedFunction.id }} + filterOverride={{ 'request.pathname': `/functions/v1/${selectedFunction.slug}` }} /> )