From 2c5f926bd897aab339f3ce229014fd762292a430 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=AD=90=E5=87=A1?= <113765024+xiaou61@users.noreply.github.com> Date: Fri, 28 Aug 2026 12:35:47 +0800 Subject: [PATCH] fix(grid): pad short timestamp fractions --- .../dataGrid/dataGridCellCoercion.spec.ts | 30 +++++++++++++++++++ .../src/lib/dataGrid/dataGridCellCoercion.ts | 10 +++++++ 2 files changed, 40 insertions(+) diff --git a/apps/desktop/src/lib/__tests__/dataGrid/dataGridCellCoercion.spec.ts b/apps/desktop/src/lib/__tests__/dataGrid/dataGridCellCoercion.spec.ts index 742414768c..1673c450ec 100644 --- a/apps/desktop/src/lib/__tests__/dataGrid/dataGridCellCoercion.spec.ts +++ b/apps/desktop/src/lib/__tests__/dataGrid/dataGridCellCoercion.spec.ts @@ -31,6 +31,36 @@ describe("dataGridCellDisplayText", () => { }), ).toBeUndefined(); }); + + it.each([ + ["2026-08-28 12:34:56.1", "2026-08-28 12:34:56.100"], + ["2026-08-28 12:34:56.12+08:00", "2026-08-28 12:34:56.120+08:00"], + ])("pads short timestamp fractions for display", (value, expected) => { + expect( + dataGridCellDisplayText({ + value, + databaseType: "mysql", + columnInfo: { data_type: "timestamp" }, + }), + ).toBe(expected); + }); + + it("leaves full-precision and non-timestamp values unchanged", () => { + expect( + dataGridCellDisplayText({ + value: "2026-08-28 12:34:56.1234", + databaseType: "mysql", + columnInfo: { data_type: "timestamp(6)" }, + }), + ).toBeUndefined(); + expect( + dataGridCellDisplayText({ + value: "2026-08-28 12:34:56.1", + databaseType: "mysql", + columnInfo: { data_type: "varchar(64)" }, + }), + ).toBeUndefined(); + }); }); describe("coerceDataGridCellValue", () => { diff --git a/apps/desktop/src/lib/dataGrid/dataGridCellCoercion.ts b/apps/desktop/src/lib/dataGrid/dataGridCellCoercion.ts index f5518bbb05..c41580ffa6 100644 --- a/apps/desktop/src/lib/dataGrid/dataGridCellCoercion.ts +++ b/apps/desktop/src/lib/dataGrid/dataGridCellCoercion.ts @@ -93,12 +93,22 @@ export function dataGridCellDisplayText(options: { value: GridCellValue; databas if (Array.isArray(options.value) && options.databaseType === "postgres" && isPostgresArrayColumn(options.columnInfo, options.value)) { return formatPostgresArrayText(options.value); } + if (typeof options.value === "string") { + const timestampDisplay = normalizeTimestampFractionDisplayText(options.value, options.columnInfo?.data_type); + if (timestampDisplay !== options.value) return timestampDisplay; + } if (typeof options.value === "string" && isOracleDateColumn(options.databaseType, options.columnInfo)) { return formatOracleDateDisplayText(options.value); } return undefined; } +function normalizeTimestampFractionDisplayText(value: string, dataType: string | undefined): string { + if (!/^timestamp(?:\s*\([^)]*\))?(?:\s+(?:with|without)\s+time\s+zone)?$/i.test(dataType?.trim() ?? "")) return value; + const match = value.match(/^(\d{4}[-/]\d{1,2}[-/]\d{1,2}[ T]\d{1,2}:\d{1,2}:\d{1,2})\.(\d{1,2})(Z|[+-]\d{2}:?\d{2})?$/); + return match ? `${match[1]}.${match[2].padEnd(3, "0")}${match[3] ?? ""}` : value; +} + function coercePostgresArrayValue(options: CoerceDataGridCellValueOptions): unknown[] | undefined { if (options.databaseType !== "postgres") return undefined; if (!isPostgresArrayColumn(options.columnInfo, options.oldValue)) return undefined;