From 77ea64792467ff60e8e4007ad48a57aa1d42fecb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eren=20=C3=87akar?= Date: Fri, 5 Jun 2026 02:24:33 +0300 Subject: [PATCH 1/2] Ensure UTC session timezone in PrismaPg connections (#4285) @prisma/adapter-pg's internal formatDateTime function converts JS Date objects to PostgreSQL TIMESTAMPTZ strings without appending a timezone offset. When the database session timezone is not UTC (e.g. UTC+8), PostgreSQL misinterprets the value, causing stored timestamps to be shifted backward. Fix by appending 'options=-c timezone=UTC' to the connection URL in all three places that create a PrismaPg adapter, ensuring every session interprets timezone-naive timestamp strings as UTC. --- scripts/check-db.js | 8 ++++++++ scripts/seed/index.ts | 10 +++++++++- src/lib/prisma.ts | 29 +++++++++++++++++++++++++++-- 3 files changed, 44 insertions(+), 3 deletions(-) diff --git a/scripts/check-db.js b/scripts/check-db.js index 100cba0317..b0fa287226 100644 --- a/scripts/check-db.js +++ b/scripts/check-db.js @@ -15,6 +15,14 @@ if (process.env.SKIP_DB_CHECK) { const url = new URL(process.env.DATABASE_URL); +const existingOptions = url.searchParams.get('options') || ''; +if (!existingOptions.includes('timezone')) { + url.searchParams.set( + 'options', + existingOptions ? `${existingOptions} -c timezone=UTC` : '-c timezone=UTC', + ); +} + const adapter = new PrismaPg( { connectionString: url.toString() }, { schema: url.searchParams.get('schema') }, diff --git a/scripts/seed/index.ts b/scripts/seed/index.ts index 2357ca4d9d..5a8f899caa 100644 --- a/scripts/seed/index.ts +++ b/scripts/seed/index.ts @@ -291,7 +291,15 @@ function createPrismaClient(): PrismaClient { ); } - const adapter = new PrismaPg({ connectionString: url }, { schema }); + const existingOptions = connectionUrl.searchParams.get('options') || ''; + if (!existingOptions.includes('timezone')) { + connectionUrl.searchParams.set( + 'options', + existingOptions ? `${existingOptions} -c timezone=UTC` : '-c timezone=UTC', + ); + } + + const adapter = new PrismaPg({ connectionString: connectionUrl.toString() }, { schema }); return new PrismaClient({ adapter, diff --git a/src/lib/prisma.ts b/src/lib/prisma.ts index 6cdb33cfd2..09db9b8dae 100644 --- a/src/lib/prisma.ts +++ b/src/lib/prisma.ts @@ -551,7 +551,21 @@ function getClient() { const schema = getSchema(); - const baseAdapter = new PrismaPg({ connectionString: url }, { schema }); + // Ensure consistent UTC session timezone so that TIMESTAMPTZ values + // written by @prisma/adapter-pg are not mis-interpreted by PostgreSQL. + // The adapter's formatDateTime omits the timezone offset, causing + // PostgreSQL to assume the session timezone (default: server timezone). + // See https://github.com/umami-software/umami/issues/4285 + const connectionUrl = new URL(url); + const existingOptions = connectionUrl.searchParams.get('options') || ''; + if (!existingOptions.includes('timezone')) { + connectionUrl.searchParams.set( + 'options', + existingOptions ? `${existingOptions} -c timezone=UTC` : '-c timezone=UTC', + ); + } + + const baseAdapter = new PrismaPg({ connectionString: connectionUrl.toString() }, { schema }); const baseClient = new PrismaClient({ adapter: baseAdapter, @@ -569,7 +583,18 @@ function getClient() { return baseClient; } - const replicaAdapter = new PrismaPg({ connectionString: replicaUrl }, { schema }); + const replicaConnectionUrl = new URL(replicaUrl); + const replicaExistingOptions = replicaConnectionUrl.searchParams.get('options') || ''; + if (!replicaExistingOptions.includes('timezone')) { + replicaConnectionUrl.searchParams.set( + 'options', + replicaExistingOptions + ? `${replicaExistingOptions} -c timezone=UTC` + : '-c timezone=UTC', + ); + } + + const replicaAdapter = new PrismaPg({ connectionString: replicaConnectionUrl.toString() }, { schema }); const replicaClient = new PrismaClient({ adapter: replicaAdapter, From 99818c970db22cc52abcafa0f2ef71b08f445042 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eren=20=C3=87akar?= Date: Fri, 5 Jun 2026 02:31:39 +0300 Subject: [PATCH 2/2] Update src/lib/prisma.ts Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- src/lib/prisma.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/prisma.ts b/src/lib/prisma.ts index 09db9b8dae..57e4074659 100644 --- a/src/lib/prisma.ts +++ b/src/lib/prisma.ts @@ -558,7 +558,7 @@ function getClient() { // See https://github.com/umami-software/umami/issues/4285 const connectionUrl = new URL(url); const existingOptions = connectionUrl.searchParams.get('options') || ''; - if (!existingOptions.includes('timezone')) { + if (!/(^|\s)-c\s+timezone=/i.test(existingOptions)) { connectionUrl.searchParams.set( 'options', existingOptions ? `${existingOptions} -c timezone=UTC` : '-c timezone=UTC',