mirror of
https://github.com/supabase/supabase.git
synced 2026-05-08 18:00:20 -04:00
d2017189e1
## Problem
The "INSERTED AT (UTC)" column in the Database Migrations UI showed
local time instead of UTC. For a user in Buenos Aires (UTC-3), a
migration timestamped at UTC 08:33:31 would show 05:33:31 in the table.
The tooltip's relative time also showed "in 3 hours" (future) for a
migration that had already run, because the UTC offset was applied in
the wrong direction.
Root cause: `parseMigrationVersion` parsed the version string (format
`YYYYMMDDHHmmss`, which the Supabase CLI generates in UTC) using
`dayjs()` without the UTC flag, so dayjs interpreted the digits as local
time.
## Fix
- Changed `parseMigrationVersion` to use `dayjs.utc()` so the version
string is correctly interpreted as a UTC timestamp.
- Updated the label formatter in `Migrations.tsx` to use
`.utc().format()`, so the displayed time matches the column header
("INSERTED AT (UTC)").
- Added the dayjs UTC plugin setup to the test file and added a
regression test that asserts `toISOString()` returns the correct UTC
time.
## Before
<img width="1102" height="658" alt="CleanShot 2026-04-08 at 12 41 18@2x"
src="https://github.com/user-attachments/assets/5eccdfb1-757c-4794-b24f-6a2c71f483dc"
/>
## After
<img width="1126" height="612" alt="CleanShot 2026-04-08 at 12 42 03@2x"
src="https://github.com/user-attachments/assets/6f3da69f-ace5-4758-b025-b49d8b325034"
/>
## How to test
- Set your browser/OS timezone to something other than UTC (e.g.
America/Buenos_Aires, UTC-3).
- Open the Database Migrations page for a project that has migrations.
- The "INSERTED AT (UTC)" column should show the UTC time matching the
version number digits (e.g. version `20260406083331` should show `06 Apr
2026, 08:33:31`).
- Hover over the timestamp. The tooltip should show the same value for
"UTC", a correctly offset value for your local timezone, and a relative
time that reflects the past (e.g. "3 hours ago", not "in 3 hours").
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Migration timestamps now parsed and displayed in UTC for consistent,
accurate labels; unparsable versions show "Unknown".
* **New Features**
* Improved migration version labeling for clearer, uniformly formatted
date/time shown in the UI.
* **Tests**
* Expanded tests for migration parsing and label formatting; test setup
updated for UTC handling.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
97 lines
3.1 KiB
TypeScript
97 lines
3.1 KiB
TypeScript
import dayjs from 'dayjs'
|
|
import utc from 'dayjs/plugin/utc'
|
|
import { describe, expect, it } from 'vitest'
|
|
|
|
import { formatMigrationVersionLabel, parseMigrationVersion } from './migration-utils'
|
|
|
|
dayjs.extend(utc)
|
|
|
|
describe('parseMigrationVersion', () => {
|
|
it('should parse valid migration version in YYYYMMDDHHmmss format', () => {
|
|
const result = parseMigrationVersion('20231128095400')
|
|
|
|
expect(result).not.toBeNull()
|
|
expect(result?.isValid()).toBe(true)
|
|
expect(result?.year()).toBe(2023)
|
|
expect(result?.month()).toBe(10)
|
|
expect(result?.date()).toBe(28)
|
|
expect(result?.hour()).toBe(9)
|
|
expect(result?.minute()).toBe(54)
|
|
expect(result?.second()).toBe(0)
|
|
})
|
|
|
|
it('should return undefined for invalid version format like "001"', () => {
|
|
const result = parseMigrationVersion('001')
|
|
|
|
expect(result).toBeUndefined()
|
|
})
|
|
|
|
it('should return undefined for invalid version format like "002"', () => {
|
|
const result = parseMigrationVersion('002')
|
|
|
|
expect(result).toBeUndefined()
|
|
})
|
|
|
|
it('should return undefined for invalid version format like "003"', () => {
|
|
const result = parseMigrationVersion('003')
|
|
|
|
expect(result).toBeUndefined()
|
|
})
|
|
|
|
it('should return undefined for empty string', () => {
|
|
const result = parseMigrationVersion('')
|
|
|
|
expect(result).toBeUndefined()
|
|
})
|
|
|
|
it('should return undefined for random string', () => {
|
|
const result = parseMigrationVersion('not-a-date')
|
|
|
|
expect(result).toBeUndefined()
|
|
})
|
|
|
|
it('should return undefined for partial date format', () => {
|
|
const result = parseMigrationVersion('20231128')
|
|
|
|
expect(result).toBeUndefined()
|
|
})
|
|
|
|
it('should handle edge case date values that dayjs can parse', () => {
|
|
// dayjs is lenient and will wrap invalid values to valid dates
|
|
// This is acceptable for our use case - the main goal is to reject
|
|
// non-date formats like "001", "002", etc.
|
|
const result = parseMigrationVersion('20231399000000')
|
|
|
|
expect(result).not.toBeNull()
|
|
expect(result?.isValid()).toBe(true)
|
|
})
|
|
|
|
it('should allow chaining dayjs methods when valid', () => {
|
|
const result = parseMigrationVersion('20231128095400')
|
|
|
|
expect(result?.fromNow()).toBeDefined()
|
|
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')
|
|
})
|
|
})
|