From 826db5a04561d708a5dddcdcb7c0ecfb77381341 Mon Sep 17 00:00:00 2001 From: Angelelz Date: Sat, 16 Dec 2023 01:50:00 -0500 Subject: [PATCH 01/10] [Pg] Patched Postgres.js driver to not parse the dates --- drizzle-orm/src/postgres-js/driver.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drizzle-orm/src/postgres-js/driver.ts b/drizzle-orm/src/postgres-js/driver.ts index 9726bef8f..669ff1032 100644 --- a/drizzle-orm/src/postgres-js/driver.ts +++ b/drizzle-orm/src/postgres-js/driver.ts @@ -20,6 +20,14 @@ export function drizzle = Record = {}, ): PostgresJsDatabase { + const transparentParser = (val: any) => val; + + // Override postgres.js default date parsers + client.options.parsers['1184'] = transparentParser; + client.options.parsers['1082'] = transparentParser; + client.options.parsers['1083'] = transparentParser; + client.options.parsers['1114'] = transparentParser; + const dialect = new PgDialect(); let logger; if (config.logger === true) { From a21639ba6c9862f5518137858036045dd3871f44 Mon Sep 17 00:00:00 2001 From: Angelelz Date: Sat, 16 Dec 2023 01:50:32 -0500 Subject: [PATCH 02/10] [Pg] Correct handling of the timestamps and timestamptz types --- drizzle-orm/src/pg-core/columns/timestamp.ts | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/drizzle-orm/src/pg-core/columns/timestamp.ts b/drizzle-orm/src/pg-core/columns/timestamp.ts index 3060bfb3f..a4942408e 100644 --- a/drizzle-orm/src/pg-core/columns/timestamp.ts +++ b/drizzle-orm/src/pg-core/columns/timestamp.ts @@ -58,12 +58,16 @@ export class PgTimestamp> exte return `timestamp${precision}${this.withTimezone ? ' with time zone' : ''}`; } - override mapFromDriverValue = (value: string): Date => { - return new Date(this.withTimezone ? value : value + '+0000'); + override mapFromDriverValue = (value: string | Date | null): Date | null => { + if (value === null) return null; + if (typeof value === 'string') { + return new Date(Date.parse(this.withTimezone ? value : value + 'Z')); + } + return value; }; override mapToDriverValue = (value: Date): string => { - return this.withTimezone ? value.toUTCString() : value.toISOString(); + return value.toISOString(); }; } @@ -121,6 +125,14 @@ export class PgTimestampString { + // eslint-disable-next-line no-instanceof/no-instanceof + if (value instanceof Date) { + return value.toISOString(); + } + return value; + }; } export type Precision = 0 | 1 | 2 | 3 | 4 | 5 | 6; From 8dab8a4c7a8657e029625a4d01d7587c54a8e127 Mon Sep 17 00:00:00 2001 From: Angelelz Date: Sat, 16 Dec 2023 01:51:02 -0500 Subject: [PATCH 03/10] [Pg] Added test in mostr drivers --- drizzle-typebox/package.json | 2 +- drizzle-valibot/package.json | 2 +- drizzle-zod/package.json | 2 +- integration-tests/package.json | 2 +- integration-tests/tests/awsdatapi.test.ts | 110 ++++++++++++- integration-tests/tests/neon-http.test.ts | 96 +++++++++++ integration-tests/tests/pg-proxy.test.ts | 128 ++++++++++++++- integration-tests/tests/pg.test.ts | 159 ++++++++++++++++-- integration-tests/tests/postgres.js.test.ts | 172 ++++++++++++++++++++ integration-tests/vitest.config.ts | 9 +- 10 files changed, 653 insertions(+), 29 deletions(-) diff --git a/drizzle-typebox/package.json b/drizzle-typebox/package.json index cdeee64f9..a0fc7e28d 100644 --- a/drizzle-typebox/package.json +++ b/drizzle-typebox/package.json @@ -9,7 +9,7 @@ "test:types": "cd tests && tsc", "pack": "(cd dist && npm pack --pack-destination ..) && rm -f package.tgz && mv *.tgz package.tgz", "publish": "npm publish package.tgz", - "test": "ava tests" + "test": "NODE_OPTIONS='--loader=tsx --no-warnings' ava" }, "exports": { ".": { diff --git a/drizzle-valibot/package.json b/drizzle-valibot/package.json index 278e95a11..86d074934 100644 --- a/drizzle-valibot/package.json +++ b/drizzle-valibot/package.json @@ -9,7 +9,7 @@ "test:types": "cd tests && tsc", "pack": "(cd dist && npm pack --pack-destination ..) && rm -f package.tgz && mv *.tgz package.tgz", "publish": "npm publish package.tgz", - "test": "ava tests" + "test": "NODE_OPTIONS='--loader=tsx --no-warnings' ava" }, "exports": { ".": { diff --git a/drizzle-zod/package.json b/drizzle-zod/package.json index df6e47325..b5d21582f 100644 --- a/drizzle-zod/package.json +++ b/drizzle-zod/package.json @@ -9,7 +9,7 @@ "test:types": "cd tests && tsc", "pack": "(cd dist && npm pack --pack-destination ..) && rm -f package.tgz && mv *.tgz package.tgz", "publish": "npm publish package.tgz", - "test": "ava tests" + "test": "NODE_OPTIONS='--loader=tsx --no-warnings' ava" }, "exports": { ".": { diff --git a/integration-tests/package.json b/integration-tests/package.json index 745d1cbe0..ab3ffde7a 100644 --- a/integration-tests/package.json +++ b/integration-tests/package.json @@ -5,7 +5,7 @@ "type": "module", "scripts": { "test:types": "tsc", - "test": "ava tests --timeout=60s --serial && pnpm test:esm && pnpm test:rqb", + "test": "NODE_OPTIONS='--loader=tsx --no-warnings' ava --timeout=60s --serial && pnpm test:esm && pnpm test:rqb", "test:rqb": "vitest run --no-threads", "test:esm": "node tests/imports.test.mjs && node tests/imports.test.cjs" }, diff --git a/integration-tests/tests/awsdatapi.test.ts b/integration-tests/tests/awsdatapi.test.ts index 1f390eb70..f8c30335f 100644 --- a/integration-tests/tests/awsdatapi.test.ts +++ b/integration-tests/tests/awsdatapi.test.ts @@ -9,7 +9,22 @@ import { asc, eq, name, placeholder, sql, TransactionRollbackError } from 'drizz import type { AwsDataApiPgDatabase } from 'drizzle-orm/aws-data-api/pg'; import { drizzle } from 'drizzle-orm/aws-data-api/pg'; import { migrate } from 'drizzle-orm/aws-data-api/pg/migrator'; -import { alias, boolean, integer, jsonb, pgTable, pgTableCreator, serial, text, timestamp } from 'drizzle-orm/pg-core'; +import { + alias, + boolean, + date, + integer, + interval, + jsonb, + pgTable, + pgTableCreator, + serial, + text, + time, + timestamp, +} from 'drizzle-orm/pg-core'; +import type { Equal } from './utils'; +import { Expect } from './utils'; dotenv.config(); @@ -858,6 +873,99 @@ test.serial('select from raw sql with mapped values', async (t) => { ]); }); +test.serial('all date and time columns', async (t) => { + const { db } = t.context; + + const table = pgTable('all_columns', { + id: serial('id').primaryKey(), + dateString: date('date_string', { mode: 'string' }).notNull(), + time: time('time', { precision: 3 }).notNull(), + datetime: timestamp('datetime').notNull(), + datetimeWTZ: timestamp('datetime_wtz', { withTimezone: true }).notNull(), + datetimeString: timestamp('datetime_string', { mode: 'string' }).notNull(), + datetimeFullPrecision: timestamp('datetime_full_precision', { precision: 6, mode: 'string' }).notNull(), + datetimeWTZString: timestamp('datetime_wtz_string', { withTimezone: true, mode: 'string' }).notNull(), + interval: interval('interval').notNull(), + }); + + await db.execute(sql`drop table if exists ${table}`); + + await db.execute(sql` + create table ${table} ( + id serial primary key, + date_string date not null, + time time(3) not null, + datetime timestamp not null, + datetime_wtz timestamp with time zone not null, + datetime_string timestamp not null, + datetime_full_precision timestamp(6) not null, + datetime_wtz_string timestamp with time zone not null, + interval interval not null + ) + `); + + const someDatetime = new Date('2022-01-01T00:00:00.123Z'); + const fullPrecision = '2022-01-01T00:00:00.123456'; + const someTime = '23:23:12.432'; + + await db.insert(table).values({ + dateString: '2022-01-01', + time: someTime, + datetime: someDatetime, + datetimeWTZ: someDatetime, + datetimeString: '2022-01-01T00:00:00.123Z', + datetimeFullPrecision: fullPrecision, + datetimeWTZString: '2022-01-01T00:00:00.123Z', + interval: '1 day', + }); + + const result = await db.select().from(table); + + Expect< + Equal<{ + id: number; + dateString: string; + time: string; + datetime: Date; + datetimeWTZ: Date; + datetimeString: string; + datetimeFullPrecision: string; + datetimeWTZString: string; + interval: string; + }[], typeof result> + >; + + Expect< + Equal<{ + dateString: string; + time: string; + datetime: Date; + datetimeWTZ: Date; + datetimeString: string; + datetimeFullPrecision: string; + datetimeWTZString: string; + interval: string; + id?: number | undefined; + }, typeof table.$inferInsert> + >; + + t.deepEqual(result, [ + { + id: 1, + dateString: '2022-01-01', + time: someTime, + datetime: someDatetime, + datetimeWTZ: someDatetime, + datetimeString: '2022-01-01 00:00:00.123', + datetimeFullPrecision: fullPrecision.replace('T', ' ').replace('Z', ''), + datetimeWTZString: '2022-01-01 00:00:00.123+00', + interval: '1 day', + }, + ]); + + await db.execute(sql`drop table if exists ${table}`); +}); + test.after.always(async (t) => { const ctx = t.context; await ctx.db.execute(sql`drop table "users"`); diff --git a/integration-tests/tests/neon-http.test.ts b/integration-tests/tests/neon-http.test.ts index 5112fd3b1..fb86548eb 100644 --- a/integration-tests/tests/neon-http.test.ts +++ b/integration-tests/tests/neon-http.test.ts @@ -29,10 +29,12 @@ import { boolean, char, cidr, + date, getMaterializedViewConfig, getViewConfig, inet, integer, + interval, jsonb, macaddr, macaddr8, @@ -44,6 +46,7 @@ import { pgView, serial, text, + time, timestamp, uuid as pgUuid, varchar, @@ -2011,6 +2014,99 @@ test.serial('timestamp timezone', async (t) => { t.assert(Math.abs(users[1]!.createdAt.getTime() - date.getTime()) < 2000); }); +test.serial('all date and time columns', async (t) => { + const { db } = t.context; + + const table = pgTable('all_columns', { + id: serial('id').primaryKey(), + dateString: date('date_string', { mode: 'string' }).notNull(), + time: time('time', { precision: 3 }).notNull(), + datetime: timestamp('datetime').notNull(), + datetimeWTZ: timestamp('datetime_wtz', { withTimezone: true }).notNull(), + datetimeString: timestamp('datetime_string', { mode: 'string' }).notNull(), + datetimeFullPrecision: timestamp('datetime_full_precision', { precision: 6, mode: 'string' }).notNull(), + datetimeWTZString: timestamp('datetime_wtz_string', { withTimezone: true, mode: 'string' }).notNull(), + interval: interval('interval').notNull(), + }); + + await db.execute(sql`drop table if exists ${table}`); + + await db.execute(sql` + create table ${table} ( + id serial primary key, + date_string date not null, + time time(3) not null, + datetime timestamp not null, + datetime_wtz timestamp with time zone not null, + datetime_string timestamp not null, + datetime_full_precision timestamp(6) not null, + datetime_wtz_string timestamp with time zone not null, + interval interval not null + ) + `); + + const someDatetime = new Date('2022-01-01T00:00:00.123Z'); + const fullPrecision = '2022-01-01T00:00:00.123456'; + const someTime = '23:23:12.432'; + + await db.insert(table).values({ + dateString: '2022-01-01', + time: someTime, + datetime: someDatetime, + datetimeWTZ: someDatetime, + datetimeString: '2022-01-01T00:00:00.123Z', + datetimeFullPrecision: fullPrecision.replace('T', ' ').replace('Z', ''), + datetimeWTZString: '2022-01-01T00:00:00.123Z', + interval: '1 day', + }); + + const result = await db.select().from(table); + + Expect< + Equal<{ + id: number; + dateString: string; + time: string; + datetime: Date; + datetimeWTZ: Date; + datetimeString: string; + datetimeFullPrecision: string; + datetimeWTZString: string; + interval: string; + }[], typeof result> + >; + + Expect< + Equal<{ + dateString: string; + time: string; + datetime: Date; + datetimeWTZ: Date; + datetimeString: string; + datetimeFullPrecision: string; + datetimeWTZString: string; + interval: string; + id?: number | undefined; + }, typeof table.$inferInsert> + >; + + t.deepEqual(result, [ + { + id: 1, + dateString: '2022-01-01', + time: someTime, + datetime: someDatetime, + datetimeWTZ: someDatetime, + datetimeString: '2022-01-01 00:00:00.123', + datetimeFullPrecision: fullPrecision.replace('T', ' '), + datetimeWTZString: '2022-01-01 00:00:00.123+00', + interval: '1 day', + }, + ]); + + await db.execute(sql`drop table if exists ${table}`); +}); + test.serial('transaction', async (t) => { const { db } = t.context; diff --git a/integration-tests/tests/pg-proxy.test.ts b/integration-tests/tests/pg-proxy.test.ts index bc494c662..7a1312954 100644 --- a/integration-tests/tests/pg-proxy.test.ts +++ b/integration-tests/tests/pg-proxy.test.ts @@ -25,11 +25,13 @@ import { boolean, char, cidr, + date, getMaterializedViewConfig, getTableConfig, getViewConfig, inet, integer, + interval, jsonb, macaddr, macaddr8, @@ -41,6 +43,7 @@ import { pgView, serial, text, + time, timestamp, unique, uniqueKeyName, @@ -58,7 +61,14 @@ import { Expect } from './utils.ts'; // eslint-disable-next-line drizzle/require-entity-kind class ServerSimulator { - constructor(private db: pg.Client) { } + constructor(private db: pg.Client) { + const { types } = pg; + + types.setTypeParser(types.builtins.TIMESTAMPTZ, (val) => val); + types.setTypeParser(types.builtins.TIMESTAMP, (val) => val); + types.setTypeParser(types.builtins.DATE, (val) => val); + types.setTypeParser(types.builtins.INTERVAL, (val) => val); + } async query(sql: string, params: any[], method: 'all' | 'execute') { if (method === 'all') { @@ -1066,7 +1076,7 @@ test.serial('migrator', async (t) => { await t.throwsAsync(async () => { await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }); }, { - message: 'relation "users12" does not exist' + message: 'relation "users12" does not exist', }); await migrate(db, async (queries) => { @@ -1109,10 +1119,11 @@ test.serial('insert via db.execute + returning', async (t) => { const { db } = t.context; const inserted = await db.execute<{ id: number; name: string }>( - sql`insert into ${usersTable} (${name( - usersTable.name.name, - ) - }) values (${'John'}) returning ${usersTable.id}, ${usersTable.name}`, + sql`insert into ${usersTable} (${ + name( + usersTable.name.name, + ) + }) values (${'John'}) returning ${usersTable.id}, ${usersTable.name}`, ); t.deepEqual(inserted, [{ id: 1, name: 'John' }]); }); @@ -2085,11 +2096,19 @@ test.serial('select from enum', async (t) => { await db.execute(sql`drop type if exists ${name(equipmentEnum.enumName)}`); await db.execute(sql`drop type if exists ${name(categoryEnum.enumName)}`); - await db.execute(sql`create type ${name(muscleEnum.enumName)} as enum ('abdominals', 'hamstrings', 'adductors', 'quadriceps', 'biceps', 'shoulders', 'chest', 'middle_back', 'calves', 'glutes', 'lower_back', 'lats', 'triceps', 'traps', 'forearms', 'neck', 'abductors')`,); + await db.execute( + sql`create type ${ + name(muscleEnum.enumName) + } as enum ('abdominals', 'hamstrings', 'adductors', 'quadriceps', 'biceps', 'shoulders', 'chest', 'middle_back', 'calves', 'glutes', 'lower_back', 'lats', 'triceps', 'traps', 'forearms', 'neck', 'abductors')`, + ); await db.execute(sql`create type ${name(forceEnum.enumName)} as enum ('isometric', 'isotonic', 'isokinetic')`); await db.execute(sql`create type ${name(levelEnum.enumName)} as enum ('beginner', 'intermediate', 'advanced')`); await db.execute(sql`create type ${name(mechanicEnum.enumName)} as enum ('compound', 'isolation')`); - await db.execute(sql`create type ${name(equipmentEnum.enumName)} as enum ('barbell', 'dumbbell', 'bodyweight', 'machine', 'cable', 'kettlebell')`,); + await db.execute( + sql`create type ${ + name(equipmentEnum.enumName) + } as enum ('barbell', 'dumbbell', 'bodyweight', 'machine', 'cable', 'kettlebell')`, + ); await db.execute(sql`create type ${name(categoryEnum.enumName)} as enum ('upper_body', 'lower_body', 'full_body')`); await db.execute(sql` create table ${exercises} ( @@ -2246,6 +2265,99 @@ test.serial('timestamp timezone', async (t) => { t.assert(Math.abs(users[1]!.createdAt.getTime() - date.getTime()) < 2000); }); +test.serial('all date and time columns', async (t) => { + const { db } = t.context; + + const table = pgTable('all_columns', { + id: serial('id').primaryKey(), + dateString: date('date_string', { mode: 'string' }).notNull(), + time: time('time', { precision: 3 }).notNull(), + datetime: timestamp('datetime').notNull(), + datetimeWTZ: timestamp('datetime_wtz', { withTimezone: true }).notNull(), + datetimeString: timestamp('datetime_string', { mode: 'string' }).notNull(), + datetimeFullPrecision: timestamp('datetime_full_precision', { precision: 6, mode: 'string' }).notNull(), + datetimeWTZString: timestamp('datetime_wtz_string', { withTimezone: true, mode: 'string' }).notNull(), + interval: interval('interval').notNull(), + }); + + await db.execute(sql`drop table if exists ${table}`); + + await db.execute(sql` + create table ${table} ( + id serial primary key, + date_string date not null, + time time(3) not null, + datetime timestamp not null, + datetime_wtz timestamp with time zone not null, + datetime_string timestamp not null, + datetime_full_precision timestamp(6) not null, + datetime_wtz_string timestamp with time zone not null, + interval interval not null + ) + `); + + const someDatetime = new Date('2022-01-01T00:00:00.123Z'); + const fullPrecision = '2022-01-01T00:00:00.123456Z'; + const someTime = '23:23:12.432'; + + await db.insert(table).values({ + dateString: '2022-01-01', + time: someTime, + datetime: someDatetime, + datetimeWTZ: someDatetime, + datetimeString: '2022-01-01T00:00:00.123Z', + datetimeFullPrecision: fullPrecision, + datetimeWTZString: '2022-01-01T00:00:00.123Z', + interval: '1 day', + }); + + const result = await db.select().from(table); + + Expect< + Equal<{ + id: number; + dateString: string; + time: string; + datetime: Date; + datetimeWTZ: Date; + datetimeString: string; + datetimeFullPrecision: string; + datetimeWTZString: string; + interval: string; + }[], typeof result> + >; + + Expect< + Equal<{ + dateString: string; + time: string; + datetime: Date; + datetimeWTZ: Date; + datetimeString: string; + datetimeFullPrecision: string; + datetimeWTZString: string; + interval: string; + id?: number | undefined; + }, typeof table.$inferInsert> + >; + + t.deepEqual(result, [ + { + id: 1, + dateString: '2022-01-01', + time: someTime, + datetime: someDatetime, + datetimeWTZ: someDatetime, + datetimeString: '2022-01-01 00:00:00.123', + datetimeFullPrecision: fullPrecision.replace('T', ' ').replace('Z', ''), + datetimeWTZString: '2022-01-01 00:00:00.123+00', + interval: '1 day', + }, + ]); + + await db.execute(sql`drop table if exists ${table}`); +}); + // TODO: implement transaction // test.serial('transaction', async (t) => { // const { db } = t.context; diff --git a/integration-tests/tests/pg.test.ts b/integration-tests/tests/pg.test.ts index 3c1b1c9d7..04dc51303 100644 --- a/integration-tests/tests/pg.test.ts +++ b/integration-tests/tests/pg.test.ts @@ -9,25 +9,25 @@ import { arrayContains, arrayOverlaps, asc, + avg, + avgDistinct, + count, + countDistinct, eq, gt, gte, inArray, lt, + max, + min, name, placeholder, type SQL, sql, type SQLWrapper, - TransactionRollbackError, - count, - countDistinct, - avg, - avgDistinct, sum, sumDistinct, - max, - min, + TransactionRollbackError, } from 'drizzle-orm'; import { drizzle, type NodePgDatabase } from 'drizzle-orm/node-postgres'; import { migrate } from 'drizzle-orm/node-postgres/migrator'; @@ -36,6 +36,7 @@ import { boolean, char, cidr, + date, except, exceptAll, foreignKey, @@ -46,10 +47,12 @@ import { integer, intersect, intersectAll, + interval, jsonb, macaddr, macaddr8, type PgColumn, + pgEnum, pgMaterializedView, pgTable, pgTableCreator, @@ -57,6 +60,7 @@ import { primaryKey, serial, text, + time, timestamp, union, unionAll, @@ -64,7 +68,6 @@ import { uniqueKeyName, uuid as pgUuid, varchar, - pgEnum, } from 'drizzle-orm/pg-core'; import getPort from 'get-port'; import pg from 'pg'; @@ -149,7 +152,7 @@ const aggregateTable = pgTable('aggregate_table', { a: integer('a'), b: integer('b'), c: integer('c'), - nullOnly: integer('null_only') + nullOnly: integer('null_only'), }); interface Context { @@ -523,7 +526,7 @@ test.serial('select distinct', async (t) => { const usersDistinctTable = pgTable('users_distinct', { id: integer('id').notNull(), name: text('name').notNull(), - age: integer('age').notNull() + age: integer('age').notNull(), }); await db.execute(sql`drop table if exists ${usersDistinctTable}`); @@ -534,7 +537,7 @@ test.serial('select distinct', async (t) => { { id: 1, name: 'John', age: 24 }, { id: 2, name: 'John', age: 25 }, { id: 1, name: 'Jane', age: 24 }, - { id: 1, name: 'Jane', age: 26 } + { id: 1, name: 'Jane', age: 26 }, ]); const users1 = await db.selectDistinct().from(usersDistinctTable).orderBy( usersDistinctTable.id, @@ -547,8 +550,8 @@ test.serial('select distinct', async (t) => { usersDistinctTable, ).orderBy(usersDistinctTable.name); const users4 = await db.selectDistinctOn([usersDistinctTable.id, usersDistinctTable.age]).from( - usersDistinctTable - ).orderBy(usersDistinctTable.id, usersDistinctTable.age) + usersDistinctTable, + ).orderBy(usersDistinctTable.id, usersDistinctTable.age); await db.execute(sql`drop table ${usersDistinctTable}`); @@ -556,7 +559,7 @@ test.serial('select distinct', async (t) => { { id: 1, name: 'Jane', age: 24 }, { id: 1, name: 'Jane', age: 26 }, { id: 1, name: 'John', age: 24 }, - { id: 2, name: 'John', age: 25 } + { id: 2, name: 'John', age: 25 }, ]); t.deepEqual(users2.length, 2); @@ -570,7 +573,7 @@ test.serial('select distinct', async (t) => { t.deepEqual(users4, [ { id: 1, name: 'John', age: 24 }, { id: 1, name: 'Jane', age: 26 }, - { id: 2, name: 'John', age: 25 } + { id: 2, name: 'John', age: 25 }, ]); }); @@ -2206,6 +2209,132 @@ test.serial('select from enum', async (t) => { await db.execute(sql`drop type ${name(categoryEnum.enumName)}`); }); +test.serial('timestamp and date in placeholders', async (t) => { + const { db } = t.context; + + const datesTable = pgTable('dates', { + id: serial('id').primaryKey(), + timestamp: timestamp('timestamp', { precision: 3 }).notNull(), + }); + + db.execute(sql`drop table if exists ${datesTable}`); + + db.execute(sql`create table ${datesTable} (id serial primary key, timestamp timestamp(3) not null)`); + + const date = new Date(); + await db.insert(datesTable).values({ + timestamp: date, + }); + + const prepared = db.select().from(datesTable).where(gte(datesTable.timestamp, sql.placeholder('timestamp'))).prepare( + 'prepared', + ); + + const result = await prepared.execute({ timestamp: new Date() }); + + t.deepEqual(result, [ + { + id: 1, + timestamp: date, + }, + ]); + + db.execute(sql`drop table if exists ${datesTable}`); +}); + +test.serial('all date and time columns', async (t) => { + const { db } = t.context; + + const table = pgTable('all_columns', { + id: serial('id').primaryKey(), + dateString: date('date_string', { mode: 'string' }).notNull(), + time: time('time', { precision: 3 }).notNull(), + datetime: timestamp('datetime').notNull(), + datetimeWTZ: timestamp('datetime_wtz', { withTimezone: true }).notNull(), + datetimeString: timestamp('datetime_string', { mode: 'string' }).notNull(), + datetimeFullPrecision: timestamp('datetime_full_precision', { precision: 6, mode: 'string' }).notNull(), + datetimeWTZString: timestamp('datetime_wtz_string', { withTimezone: true, mode: 'string' }).notNull(), + interval: interval('interval').notNull(), + }); + + await db.execute(sql`drop table if exists ${table}`); + + await db.execute(sql` + create table ${table} ( + id serial primary key, + date_string date not null, + time time(3) not null, + datetime timestamp not null, + datetime_wtz timestamp with time zone not null, + datetime_string timestamp not null, + datetime_full_precision timestamp(6) not null, + datetime_wtz_string timestamp with time zone not null, + interval interval not null + ) + `); + + const someDatetime = new Date('2022-01-01T00:00:00.123Z'); + const fullPrecision = '2022-01-01T00:00:00.123456Z'; + const someTime = '23:23:12.432'; + + await db.insert(table).values({ + dateString: '2022-01-01', + time: someTime, + datetime: someDatetime, + datetimeWTZ: someDatetime, + datetimeString: '2022-01-01T00:00:00.123Z', + datetimeFullPrecision: fullPrecision, + datetimeWTZString: '2022-01-01T00:00:00.123Z', + interval: '1 day', + }); + + const result = await db.select().from(table); + + Expect< + Equal<{ + id: number; + dateString: string; + time: string; + datetime: Date; + datetimeWTZ: Date; + datetimeString: string; + datetimeFullPrecision: string; + datetimeWTZString: string; + interval: string; + }[], typeof result> + >; + + Expect< + Equal<{ + dateString: string; + time: string; + datetime: Date; + datetimeWTZ: Date; + datetimeString: string; + datetimeFullPrecision: string; + datetimeWTZString: string; + interval: string; + id?: number | undefined; + }, typeof table.$inferInsert> + >; + + t.deepEqual(result, [ + { + id: 1, + dateString: '2022-01-01', + time: someTime, + datetime: someDatetime, + datetimeWTZ: someDatetime, + datetimeString: '2022-01-01 00:00:00.123', + datetimeFullPrecision: fullPrecision.replace('T', ' ').replace('Z', ''), + datetimeWTZString: '2022-01-01 00:00:00.123+00', + interval: '1 day', + }, + ]); + + await db.execute(sql`drop table if exists ${table}`); +}); + test.serial('orderBy with aliased column', (t) => { const { db } = t.context; diff --git a/integration-tests/tests/postgres.js.test.ts b/integration-tests/tests/postgres.js.test.ts index a1f979f60..0c6fe1765 100644 --- a/integration-tests/tests/postgres.js.test.ts +++ b/integration-tests/tests/postgres.js.test.ts @@ -25,9 +25,11 @@ import { import { alias, boolean, + date, getMaterializedViewConfig, getViewConfig, integer, + interval, jsonb, type PgColumn, pgEnum, @@ -37,6 +39,7 @@ import { pgView, serial, text, + time, timestamp, uuid as pgUuid, varchar, @@ -1795,6 +1798,39 @@ test.serial('select from enum', async (t) => { await db.execute(sql`drop type ${name(categoryEnum.enumName)}`); }); +test.serial('timestamp and date in placeholders', async (t) => { + const { db } = t.context; + + const datesTable = pgTable('dates', { + id: serial('id').primaryKey(), + timestamp: timestamp('timestamp', { precision: 3 }).notNull(), + }); + + db.execute(sql`drop table if exists ${datesTable}`); + + db.execute(sql`create table ${datesTable} (id serial primary key, timestamp timestamp(3) not null)`); + + const date = new Date(); + await db.insert(datesTable).values({ + timestamp: date, + }); + + const prepared = db.select().from(datesTable).where(eq(datesTable.timestamp, sql.placeholder('timestamp'))).prepare( + 'prepared', + ); + + const result = await prepared.execute({ timestamp: date }); + + t.deepEqual(result, [ + { + id: 1, + timestamp: date, + }, + ]); + + db.execute(sql`drop table if exists ${datesTable}`); +}); + test.serial('orderBy with aliased column', (t) => { const { db } = t.context; @@ -1850,6 +1886,142 @@ test.serial('select from sql', async (t) => { // beta }); +test.serial('timestamp timezone', async (t) => { + const { db } = t.context; + + const usersTableWithAndWithoutTimezone = pgTable('users_test_with_and_without_timezone', { + id: serial('id').primaryKey(), + name: text('name').notNull(), + createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(), + updatedAt: timestamp('updated_at', { withTimezone: false }).notNull().defaultNow(), + }); + + await db.execute(sql`drop table if exists ${usersTableWithAndWithoutTimezone}`); + + await db.execute( + sql` + create table users_test_with_and_without_timezone ( + id serial not null primary key, + name text not null, + created_at timestamptz not null default now(), + updated_at timestamp not null default now() + ) + `, + ); + + const date = new Date(Date.parse('2020-01-01T00:00:00+04:00')); + + await db.insert(usersTableWithAndWithoutTimezone).values({ name: 'With default times' }); + await db.insert(usersTableWithAndWithoutTimezone).values({ + name: 'Without default times', + createdAt: date, + updatedAt: date, + }); + const users = await db.select().from(usersTableWithAndWithoutTimezone); + + // check that the timestamps are set correctly for default times + t.assert(Math.abs(users[0]!.updatedAt.getTime() - Date.now()) < 2000); + t.assert(Math.abs(users[0]!.createdAt.getTime() - Date.now()) < 2000); + + // check that the timestamps are set correctly for non default times + t.assert(Math.abs(users[1]!.updatedAt.getTime() - date.getTime()) < 2000); + t.assert(Math.abs(users[1]!.createdAt.getTime() - date.getTime()) < 2000); +}); + +test.serial('all date and time columns', async (t) => { + const { db } = t.context; + + const table = pgTable('all_columns', { + id: serial('id').primaryKey(), + dateString: date('date_string', { mode: 'string' }).notNull(), + time: time('time', { precision: 3 }).notNull(), + datetime: timestamp('datetime').notNull(), + datetimeWTZ: timestamp('datetime_wtz', { withTimezone: true }).notNull(), + datetimeString: timestamp('datetime_string', { mode: 'string' }).notNull(), + datetimeFullPrecision: timestamp('datetime_full_precision', { precision: 6, mode: 'string' }).notNull(), + datetimeWTZString: timestamp('datetime_wtz_string', { withTimezone: true, mode: 'string' }).notNull(), + interval: interval('interval').notNull(), + }); + + await db.execute(sql`drop table if exists ${table}`); + + await db.execute(sql` + create table ${table} ( + id serial primary key, + date_string date not null, + time time(3) not null, + datetime timestamp not null, + datetime_wtz timestamp with time zone not null, + datetime_string timestamp not null, + datetime_full_precision timestamp(6) not null, + datetime_wtz_string timestamp with time zone not null, + interval interval not null + ) + `); + + const someDatetime = new Date('2022-01-01T00:00:00.123Z'); + const fullPrecision = '2022-01-01T00:00:00.123456Z'; + const someTime = '23:23:12.432'; + + await db.insert(table).values({ + dateString: '2022-01-01', + time: someTime, + datetime: someDatetime, + datetimeWTZ: someDatetime, + datetimeString: '2022-01-01T00:00:00.123Z', + datetimeFullPrecision: fullPrecision, + datetimeWTZString: '2022-01-01T00:00:00.123Z', + interval: '1 day', + }); + + const result = await db.select().from(table); + + Expect< + Equal<{ + id: number; + dateString: string; + time: string; + datetime: Date; + datetimeWTZ: Date; + datetimeString: string; + datetimeFullPrecision: string; + datetimeWTZString: string; + interval: string; + }[], typeof result> + >; + + Expect< + Equal<{ + dateString: string; + time: string; + datetime: Date; + datetimeWTZ: Date; + datetimeString: string; + datetimeFullPrecision: string; + datetimeWTZString: string; + interval: string; + id?: number | undefined; + }, typeof table.$inferInsert> + >; + + t.deepEqual(result, [ + { + id: 1, + dateString: '2022-01-01', + time: someTime, + datetime: someDatetime, + datetimeWTZ: someDatetime, + datetimeString: '2022-01-01 00:00:00.123', + // Postgres trim the microseconds for some reason + datetimeFullPrecision: '2022-01-01 00:00:00.123', + datetimeWTZString: '2022-01-01 00:00:00.123+00', + interval: '1 day', + }, + ]); + + await db.execute(sql`drop table if exists ${table}`); +}); + test.serial('transaction', async (t) => { const { db } = t.context; diff --git a/integration-tests/vitest.config.ts b/integration-tests/vitest.config.ts index 261754895..12b7a1eb3 100644 --- a/integration-tests/vitest.config.ts +++ b/integration-tests/vitest.config.ts @@ -1,10 +1,17 @@ +import 'dotenv/config'; import { viteCommonjs } from '@originjs/vite-plugin-commonjs'; import tsconfigPaths from 'vite-tsconfig-paths'; import { defineConfig } from 'vitest/config'; export default defineConfig({ test: { - include: ['tests/relational/**/*.test.ts', 'tests/libsql-batch.test.ts', 'tests/d1-batch.test.ts', 'tests/replicas/**/*', 'tests/imports/**/*'], + include: [ + 'tests/relational/**/*.test.ts', + 'tests/libsql-batch.test.ts', + 'tests/d1-batch.test.ts', + 'tests/replicas/**/*', + 'tests/imports/**/*', + ], exclude: [ ...(process.env.SKIP_PLANETSCALE_TESTS ? ['tests/relational/mysql.planetscale.test.ts'] : []), 'tests/relational/vercel.test.ts', From 064a2525855489b014e08fd073bc2de1a3fdb138 Mon Sep 17 00:00:00 2001 From: Angelelz Date: Sat, 16 Dec 2023 12:46:03 -0500 Subject: [PATCH 04/10] [Pg] Cleaned tests --- integration-tests/tests/pg.test.ts | 33 --------------------- integration-tests/tests/postgres.js.test.ts | 33 --------------------- 2 files changed, 66 deletions(-) diff --git a/integration-tests/tests/pg.test.ts b/integration-tests/tests/pg.test.ts index 04dc51303..36076a405 100644 --- a/integration-tests/tests/pg.test.ts +++ b/integration-tests/tests/pg.test.ts @@ -2209,39 +2209,6 @@ test.serial('select from enum', async (t) => { await db.execute(sql`drop type ${name(categoryEnum.enumName)}`); }); -test.serial('timestamp and date in placeholders', async (t) => { - const { db } = t.context; - - const datesTable = pgTable('dates', { - id: serial('id').primaryKey(), - timestamp: timestamp('timestamp', { precision: 3 }).notNull(), - }); - - db.execute(sql`drop table if exists ${datesTable}`); - - db.execute(sql`create table ${datesTable} (id serial primary key, timestamp timestamp(3) not null)`); - - const date = new Date(); - await db.insert(datesTable).values({ - timestamp: date, - }); - - const prepared = db.select().from(datesTable).where(gte(datesTable.timestamp, sql.placeholder('timestamp'))).prepare( - 'prepared', - ); - - const result = await prepared.execute({ timestamp: new Date() }); - - t.deepEqual(result, [ - { - id: 1, - timestamp: date, - }, - ]); - - db.execute(sql`drop table if exists ${datesTable}`); -}); - test.serial('all date and time columns', async (t) => { const { db } = t.context; diff --git a/integration-tests/tests/postgres.js.test.ts b/integration-tests/tests/postgres.js.test.ts index 0c6fe1765..1094075c4 100644 --- a/integration-tests/tests/postgres.js.test.ts +++ b/integration-tests/tests/postgres.js.test.ts @@ -1798,39 +1798,6 @@ test.serial('select from enum', async (t) => { await db.execute(sql`drop type ${name(categoryEnum.enumName)}`); }); -test.serial('timestamp and date in placeholders', async (t) => { - const { db } = t.context; - - const datesTable = pgTable('dates', { - id: serial('id').primaryKey(), - timestamp: timestamp('timestamp', { precision: 3 }).notNull(), - }); - - db.execute(sql`drop table if exists ${datesTable}`); - - db.execute(sql`create table ${datesTable} (id serial primary key, timestamp timestamp(3) not null)`); - - const date = new Date(); - await db.insert(datesTable).values({ - timestamp: date, - }); - - const prepared = db.select().from(datesTable).where(eq(datesTable.timestamp, sql.placeholder('timestamp'))).prepare( - 'prepared', - ); - - const result = await prepared.execute({ timestamp: date }); - - t.deepEqual(result, [ - { - id: 1, - timestamp: date, - }, - ]); - - db.execute(sql`drop table if exists ${datesTable}`); -}); - test.serial('orderBy with aliased column', (t) => { const { db } = t.context; From 1b97252c39aa15243d9324d1520cb2b9825f13d5 Mon Sep 17 00:00:00 2001 From: Angelelz Date: Tue, 19 Dec 2023 00:05:25 -0500 Subject: [PATCH 05/10] [Pg] [Postgres-js] fixed serializing timestamps as well --- drizzle-orm/src/postgres-js/driver.ts | 10 +++++----- integration-tests/tests/postgres.js.test.ts | 3 +-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/drizzle-orm/src/postgres-js/driver.ts b/drizzle-orm/src/postgres-js/driver.ts index 669ff1032..ae1b48a21 100644 --- a/drizzle-orm/src/postgres-js/driver.ts +++ b/drizzle-orm/src/postgres-js/driver.ts @@ -22,11 +22,11 @@ export function drizzle = Record { const transparentParser = (val: any) => val; - // Override postgres.js default date parsers - client.options.parsers['1184'] = transparentParser; - client.options.parsers['1082'] = transparentParser; - client.options.parsers['1083'] = transparentParser; - client.options.parsers['1114'] = transparentParser; + // Override postgres.js default date parsers: https://github.com/porsager/postgres/discussions/761 + for (const type of ['1184', '1082', '1083', '1114']) { + client.options.parsers[type as any] = transparentParser; + client.options.serializers[type as any] = transparentParser; + } const dialect = new PgDialect(); let logger; diff --git a/integration-tests/tests/postgres.js.test.ts b/integration-tests/tests/postgres.js.test.ts index 1094075c4..017247d35 100644 --- a/integration-tests/tests/postgres.js.test.ts +++ b/integration-tests/tests/postgres.js.test.ts @@ -1979,8 +1979,7 @@ test.serial('all date and time columns', async (t) => { datetime: someDatetime, datetimeWTZ: someDatetime, datetimeString: '2022-01-01 00:00:00.123', - // Postgres trim the microseconds for some reason - datetimeFullPrecision: '2022-01-01 00:00:00.123', + datetimeFullPrecision: fullPrecision.replace('T', ' ').replace('Z', ''), datetimeWTZString: '2022-01-01 00:00:00.123+00', interval: '1 day', }, From e33f6490d8ddf73fffea20ad292fed2d74fd08a3 Mon Sep 17 00:00:00 2001 From: Angelelz Date: Wed, 27 Dec 2023 19:42:55 -0500 Subject: [PATCH 06/10] Reverted mapping in timestamp.ts but kept the mapToDriverValue to avoid losing millisecond precision --- drizzle-orm/src/pg-core/columns/timestamp.ts | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/drizzle-orm/src/pg-core/columns/timestamp.ts b/drizzle-orm/src/pg-core/columns/timestamp.ts index a4942408e..6df0c3be6 100644 --- a/drizzle-orm/src/pg-core/columns/timestamp.ts +++ b/drizzle-orm/src/pg-core/columns/timestamp.ts @@ -58,12 +58,8 @@ export class PgTimestamp> exte return `timestamp${precision}${this.withTimezone ? ' with time zone' : ''}`; } - override mapFromDriverValue = (value: string | Date | null): Date | null => { - if (value === null) return null; - if (typeof value === 'string') { - return new Date(Date.parse(this.withTimezone ? value : value + 'Z')); - } - return value; + override mapFromDriverValue = (value: string): Date | null => { + return new Date(this.withTimezone ? value : value + '+0000'); }; override mapToDriverValue = (value: Date): string => { @@ -125,14 +121,6 @@ export class PgTimestampString { - // eslint-disable-next-line no-instanceof/no-instanceof - if (value instanceof Date) { - return value.toISOString(); - } - return value; - }; } export type Precision = 0 | 1 | 2 | 3 | 4 | 5 | 6; From ce0c95fec7f23596726f5911eaac0171342992a9 Mon Sep 17 00:00:00 2001 From: Angelelz Date: Thu, 28 Dec 2023 22:21:26 -0500 Subject: [PATCH 07/10] [Pg] Added requested additional tests --- integration-tests/tests/awsdatapi.test.ts | 163 ++++++++++++++++ integration-tests/tests/neon-http.test.ts | 163 ++++++++++++++++ integration-tests/tests/pg-proxy.test.ts | 160 ++++++++++++++++ integration-tests/tests/pg.test.ts | 196 ++++++++++++++++++-- integration-tests/tests/postgres.js.test.ts | 160 ++++++++++++++++ 5 files changed, 824 insertions(+), 18 deletions(-) diff --git a/integration-tests/tests/awsdatapi.test.ts b/integration-tests/tests/awsdatapi.test.ts index f8c30335f..1e2cbafad 100644 --- a/integration-tests/tests/awsdatapi.test.ts +++ b/integration-tests/tests/awsdatapi.test.ts @@ -966,6 +966,169 @@ test.serial('all date and time columns', async (t) => { await db.execute(sql`drop table if exists ${table}`); }); +test.serial('all date and time columns with timezone', async (t) => { + const { db } = t.context; + + const table = pgTable('all_columns', { + id: serial('id').primaryKey(), + timestamp: timestamp('timestamp_string', { mode: 'string', withTimezone: true, precision: 6 }).notNull(), + timestampAsDate: timestamp('timestamp_date', { withTimezone: true, precision: 3 }).notNull(), + timestampTimeZones: timestamp('timestamp_date_2', { withTimezone: true, precision: 3 }).notNull(), + }); + + await db.execute(sql`drop table if exists ${table}`); + + await db.execute(sql` + create table ${table} ( + id serial primary key, + timestamp_string timestamp(6) with time zone not null, + timestamp_date timestamp(3) with time zone not null, + timestamp_date_2 timestamp(3) with time zone not null + ) + `); + + const timestampString = '2022-01-01 00:00:00.123456-0200'; + const timestampDate = new Date(); + const timestampDateWTZ = new Date('2022-01-01 00:00:00.123 +0500'); + + const timestampString2 = '2022-01-01 00:00:00.123456-0400'; + const timestampDate2 = new Date(); + const timestampDateWTZ2 = new Date('2022-01-01 00:00:00.123 +0200'); + + await db.insert(table).values([ + { timestamp: timestampString, timestampAsDate: timestampDate, timestampTimeZones: timestampDateWTZ }, + { timestamp: timestampString2, timestampAsDate: timestampDate2, timestampTimeZones: timestampDateWTZ2 }, + ]); + + const result = await db.select().from(table); + const result2 = await db.execute<{ + id: number; + timestamp_string: string; + timestamp_date: string; + timestamp_date_2: string; + }>(sql`select * from ${table}`); + + // Whatever you put in, you get back when you're using the date mode + // But when using the string mode, postgres returns a string transformed into UTC + t.deepEqual(result, [ + { + id: 1, + timestamp: '2022-01-01 02:00:00.123456+00', + timestampAsDate: timestampDate, + timestampTimeZones: timestampDateWTZ, + }, + { + id: 2, + timestamp: '2022-01-01 04:00:00.123456+00', + timestampAsDate: timestampDate2, + timestampTimeZones: timestampDateWTZ2, + }, + ]); + + t.deepEqual(result2.records, [ + { + id: 1, + timestamp_string: '2022-01-01 02:00:00.123456+00', + timestamp_date: timestampDate.toISOString().replace('T', ' ').replace('Z', '') + '+00', + timestamp_date_2: timestampDateWTZ.toISOString().replace('T', ' ').replace('Z', '') + '+00', + }, + { + id: 2, + timestamp_string: '2022-01-01 04:00:00.123456+00', + timestamp_date: timestampDate2.toISOString().replace('T', ' ').replace('Z', '') + '+00', + timestamp_date_2: timestampDateWTZ2.toISOString().replace('T', ' ').replace('Z', '') + '+00', + }, + ]); + + t.deepEqual( + result[0]?.timestampTimeZones.getTime(), + new Date((result2.records?.[0] as any).timestamp_date_2 as any).getTime(), + ); + + await db.execute(sql`drop table if exists ${table}`); +}); + +test.serial('all date and time columns without timezone', async (t) => { + const { db } = t.context; + + const table = pgTable('all_columns', { + id: serial('id').primaryKey(), + timestampString: timestamp('timestamp_string', { mode: 'string', precision: 6 }).notNull(), + timestampString2: timestamp('timestamp_string2', { precision: 3, mode: 'string' }).notNull(), + timestampDate: timestamp('timestamp_date', { precision: 3 }).notNull(), + }); + + await db.execute(sql`drop table if exists ${table}`); + + await db.execute(sql` + create table ${table} ( + id serial primary key, + timestamp_string timestamp(6) not null, + timestamp_string2 timestamp(3) not null, + timestamp_date timestamp(3) not null + ) + `); + + const timestampString = '2022-01-01 00:00:00.123456'; + const timestampString2 = '2022-01-02 00:00:00.123 -0300'; + const timestampDate = new Date('2022-01-01 00:00:00.123Z'); + + const timestampString_2 = '2022-01-01 00:00:00.123456'; + const timestampString2_2 = '2022-01-01 00:00:00.123 -0300'; + const timestampDate2 = new Date('2022-01-01 00:00:00.123 +0200'); + + await db.insert(table).values([ + { timestampString, timestampString2, timestampDate }, + { timestampString: timestampString_2, timestampString2: timestampString2_2, timestampDate: timestampDate2 }, + ]); + + const result = await db.select().from(table); + const result2 = await db.execute<{ + id: number; + timestamp_string: string; + timestamp_string2: string; + timestamp_date: string; + }>(sql`select * from ${table}`); + + // Whatever you put in, you get back when you're using the date mode + // But when using the string mode, postgres returns a string transformed into UTC + t.deepEqual(result, [ + { + id: 1, + timestampString: timestampString, + timestampString2: '2022-01-02 00:00:00.123', + timestampDate: timestampDate, + }, + { + id: 2, + timestampString: timestampString_2, + timestampString2: '2022-01-01 00:00:00.123', + timestampDate: timestampDate2, + }, + ]); + + t.deepEqual(result2.records, [ + { + id: 1, + timestamp_string: timestampString, + timestamp_string2: '2022-01-02 00:00:00.123', + timestamp_date: timestampDate.toISOString().replace('T', ' ').replace('Z', ''), + }, + { + id: 2, + timestamp_string: timestampString_2, + timestamp_string2: '2022-01-01 00:00:00.123', + timestamp_date: timestampDate2.toISOString().replace('T', ' ').replace('Z', ''), + }, + ]); + + t.deepEqual((result2.records?.[0] as any).timestamp_string, '2022-01-01 00:00:00.123456'); + // need to add the 'Z', otherwise javascript assumes it's in local time + t.deepEqual(new Date((result2.records?.[0] as any).timestamp_date + 'Z' as any).getTime(), timestampDate.getTime()); + + await db.execute(sql`drop table if exists ${table}`); +}); + test.after.always(async (t) => { const ctx = t.context; await ctx.db.execute(sql`drop table "users"`); diff --git a/integration-tests/tests/neon-http.test.ts b/integration-tests/tests/neon-http.test.ts index fb86548eb..2fbfa6083 100644 --- a/integration-tests/tests/neon-http.test.ts +++ b/integration-tests/tests/neon-http.test.ts @@ -2107,6 +2107,169 @@ test.serial('all date and time columns', async (t) => { await db.execute(sql`drop table if exists ${table}`); }); +test.serial('all date and time columns with timezone', async (t) => { + const { db } = t.context; + + const table = pgTable('all_columns', { + id: serial('id').primaryKey(), + timestamp: timestamp('timestamp_string', { mode: 'string', withTimezone: true, precision: 6 }).notNull(), + timestampAsDate: timestamp('timestamp_date', { withTimezone: true, precision: 3 }).notNull(), + timestampTimeZones: timestamp('timestamp_date_2', { withTimezone: true, precision: 3 }).notNull(), + }); + + await db.execute(sql`drop table if exists ${table}`); + + await db.execute(sql` + create table ${table} ( + id serial primary key, + timestamp_string timestamp(6) with time zone not null, + timestamp_date timestamp(3) with time zone not null, + timestamp_date_2 timestamp(3) with time zone not null + ) + `); + + const timestampString = '2022-01-01 00:00:00.123456-0200'; + const timestampDate = new Date(); + const timestampDateWTZ = new Date('2022-01-01 00:00:00.123 +0500'); + + const timestampString2 = '2022-01-01 00:00:00.123456-0400'; + const timestampDate2 = new Date(); + const timestampDateWTZ2 = new Date('2022-01-01 00:00:00.123 +0200'); + + await db.insert(table).values([ + { timestamp: timestampString, timestampAsDate: timestampDate, timestampTimeZones: timestampDateWTZ }, + { timestamp: timestampString2, timestampAsDate: timestampDate2, timestampTimeZones: timestampDateWTZ2 }, + ]); + + const result = await db.select().from(table); + const result2 = await db.execute<{ + id: number; + timestamp_string: string; + timestamp_date: string; + timestamp_date_2: string; + }>(sql`select * from ${table}`); + + // Whatever you put in, you get back when you're using the date mode + // But when using the string mode, postgres returns a string transformed into UTC + t.deepEqual(result, [ + { + id: 1, + timestamp: '2022-01-01 02:00:00.123456+00', + timestampAsDate: timestampDate, + timestampTimeZones: timestampDateWTZ, + }, + { + id: 2, + timestamp: '2022-01-01 04:00:00.123456+00', + timestampAsDate: timestampDate2, + timestampTimeZones: timestampDateWTZ2, + }, + ]); + + t.deepEqual(result2.rows, [ + { + id: 1, + timestamp_string: '2022-01-01 02:00:00.123456+00', + timestamp_date: timestampDate.toISOString().replace('T', ' ').replace('Z', '') + '+00', + timestamp_date_2: timestampDateWTZ.toISOString().replace('T', ' ').replace('Z', '') + '+00', + }, + { + id: 2, + timestamp_string: '2022-01-01 04:00:00.123456+00', + timestamp_date: timestampDate2.toISOString().replace('T', ' ').replace('Z', '') + '+00', + timestamp_date_2: timestampDateWTZ2.toISOString().replace('T', ' ').replace('Z', '') + '+00', + }, + ]); + + t.deepEqual( + result[0]?.timestampTimeZones.getTime(), + new Date((result2.rows[0] as any).timestamp_date_2 as any).getTime(), + ); + + await db.execute(sql`drop table if exists ${table}`); +}); + +test.serial('all date and time columns without timezone', async (t) => { + const { db } = t.context; + + const table = pgTable('all_columns', { + id: serial('id').primaryKey(), + timestampString: timestamp('timestamp_string', { mode: 'string', precision: 6 }).notNull(), + timestampString2: timestamp('timestamp_string2', { precision: 3, mode: 'string' }).notNull(), + timestampDate: timestamp('timestamp_date', { precision: 3 }).notNull(), + }); + + await db.execute(sql`drop table if exists ${table}`); + + await db.execute(sql` + create table ${table} ( + id serial primary key, + timestamp_string timestamp(6) not null, + timestamp_string2 timestamp(3) not null, + timestamp_date timestamp(3) not null + ) + `); + + const timestampString = '2022-01-01 00:00:00.123456'; + const timestampString2 = '2022-01-02 00:00:00.123 -0300'; + const timestampDate = new Date('2022-01-01 00:00:00.123Z'); + + const timestampString_2 = '2022-01-01 00:00:00.123456'; + const timestampString2_2 = '2022-01-01 00:00:00.123 -0300'; + const timestampDate2 = new Date('2022-01-01 00:00:00.123 +0200'); + + await db.insert(table).values([ + { timestampString, timestampString2, timestampDate }, + { timestampString: timestampString_2, timestampString2: timestampString2_2, timestampDate: timestampDate2 }, + ]); + + const result = await db.select().from(table); + const result2 = await db.execute<{ + id: number; + timestamp_string: string; + timestamp_string2: string; + timestamp_date: string; + }>(sql`select * from ${table}`); + + // Whatever you put in, you get back when you're using the date mode + // But when using the string mode, postgres returns a string transformed into UTC + t.deepEqual(result, [ + { + id: 1, + timestampString: timestampString, + timestampString2: '2022-01-02 00:00:00.123', + timestampDate: timestampDate, + }, + { + id: 2, + timestampString: timestampString_2, + timestampString2: '2022-01-01 00:00:00.123', + timestampDate: timestampDate2, + }, + ]); + + t.deepEqual(result2.rows, [ + { + id: 1, + timestamp_string: timestampString, + timestamp_string2: '2022-01-02 00:00:00.123', + timestamp_date: timestampDate.toISOString().replace('T', ' ').replace('Z', ''), + }, + { + id: 2, + timestamp_string: timestampString_2, + timestamp_string2: '2022-01-01 00:00:00.123', + timestamp_date: timestampDate2.toISOString().replace('T', ' ').replace('Z', ''), + }, + ]); + + t.deepEqual((result2.rows[0] as any).timestamp_string, '2022-01-01 00:00:00.123456'); + // need to add the 'Z', otherwise javascript assumes it's in local time + t.deepEqual(new Date((result2.rows[0] as any).timestamp_date + 'Z' as any).getTime(), timestampDate.getTime()); + + await db.execute(sql`drop table if exists ${table}`); +}); + test.serial('transaction', async (t) => { const { db } = t.context; diff --git a/integration-tests/tests/pg-proxy.test.ts b/integration-tests/tests/pg-proxy.test.ts index 7a1312954..750d96aba 100644 --- a/integration-tests/tests/pg-proxy.test.ts +++ b/integration-tests/tests/pg-proxy.test.ts @@ -2358,6 +2358,166 @@ test.serial('all date and time columns', async (t) => { await db.execute(sql`drop table if exists ${table}`); }); +test.serial('all date and time columns with timezone', async (t) => { + const { db } = t.context; + + const table = pgTable('all_columns', { + id: serial('id').primaryKey(), + timestamp: timestamp('timestamp_string', { mode: 'string', withTimezone: true, precision: 6 }).notNull(), + timestampAsDate: timestamp('timestamp_date', { withTimezone: true, precision: 3 }).notNull(), + timestampTimeZones: timestamp('timestamp_date_2', { withTimezone: true, precision: 3 }).notNull(), + }); + + await db.execute(sql`drop table if exists ${table}`); + + await db.execute(sql` + create table ${table} ( + id serial primary key, + timestamp_string timestamp(6) with time zone not null, + timestamp_date timestamp(3) with time zone not null, + timestamp_date_2 timestamp(3) with time zone not null + ) + `); + + const timestampString = '2022-01-01 00:00:00.123456-0200'; + const timestampDate = new Date(); + const timestampDateWTZ = new Date('2022-01-01 00:00:00.123 +0500'); + + const timestampString2 = '2022-01-01 00:00:00.123456-0400'; + const timestampDate2 = new Date(); + const timestampDateWTZ2 = new Date('2022-01-01 00:00:00.123 +0200'); + + await db.insert(table).values([ + { timestamp: timestampString, timestampAsDate: timestampDate, timestampTimeZones: timestampDateWTZ }, + { timestamp: timestampString2, timestampAsDate: timestampDate2, timestampTimeZones: timestampDateWTZ2 }, + ]); + + const result = await db.select().from(table); + const result2 = await db.execute<{ + id: number; + timestamp_string: string; + timestamp_date: string; + timestamp_date_2: string; + }>(sql`select * from ${table}`); + + // Whatever you put in, you get back when you're using the date mode + // But when using the string mode, postgres returns a string transformed into UTC + t.deepEqual(result, [ + { + id: 1, + timestamp: '2022-01-01 02:00:00.123456+00', + timestampAsDate: timestampDate, + timestampTimeZones: timestampDateWTZ, + }, + { + id: 2, + timestamp: '2022-01-01 04:00:00.123456+00', + timestampAsDate: timestampDate2, + timestampTimeZones: timestampDateWTZ2, + }, + ]); + + t.deepEqual(result2, [ + { + id: 1, + timestamp_string: '2022-01-01 02:00:00.123456+00', + timestamp_date: timestampDate.toISOString().replace('T', ' ').replace('Z', '') + '+00', + timestamp_date_2: timestampDateWTZ.toISOString().replace('T', ' ').replace('Z', '') + '+00', + }, + { + id: 2, + timestamp_string: '2022-01-01 04:00:00.123456+00', + timestamp_date: timestampDate2.toISOString().replace('T', ' ').replace('Z', '') + '+00', + timestamp_date_2: timestampDateWTZ2.toISOString().replace('T', ' ').replace('Z', '') + '+00', + }, + ]); + + t.deepEqual(result[0]?.timestampTimeZones.getTime(), new Date(result2[0]?.timestamp_date_2 as any).getTime()); + + await db.execute(sql`drop table if exists ${table}`); +}); + +test.serial('all date and time columns without timezone', async (t) => { + const { db } = t.context; + + const table = pgTable('all_columns', { + id: serial('id').primaryKey(), + timestampString: timestamp('timestamp_string', { mode: 'string', precision: 6 }).notNull(), + timestampString2: timestamp('timestamp_string2', { precision: 3, mode: 'string' }).notNull(), + timestampDate: timestamp('timestamp_date', { precision: 3 }).notNull(), + }); + + await db.execute(sql`drop table if exists ${table}`); + + await db.execute(sql` + create table ${table} ( + id serial primary key, + timestamp_string timestamp(6) not null, + timestamp_string2 timestamp(3) not null, + timestamp_date timestamp(3) not null + ) + `); + + const timestampString = '2022-01-01 00:00:00.123456'; + const timestampString2 = '2022-01-02 00:00:00.123 -0300'; + const timestampDate = new Date('2022-01-01 00:00:00.123Z'); + + const timestampString_2 = '2022-01-01 00:00:00.123456'; + const timestampString2_2 = '2022-01-01 00:00:00.123 -0300'; + const timestampDate2 = new Date('2022-01-01 00:00:00.123 +0200'); + + await db.insert(table).values([ + { timestampString, timestampString2, timestampDate }, + { timestampString: timestampString_2, timestampString2: timestampString2_2, timestampDate: timestampDate2 }, + ]); + + const result = await db.select().from(table); + const result2 = await db.execute<{ + id: number; + timestamp_string: string; + timestamp_string2: string; + timestamp_date: string; + }>(sql`select * from ${table}`); + + // Whatever you put in, you get back when you're using the date mode + // But when using the string mode, postgres returns a string transformed into UTC + t.deepEqual(result, [ + { + id: 1, + timestampString: timestampString, + timestampString2: '2022-01-02 00:00:00.123', + timestampDate: timestampDate, + }, + { + id: 2, + timestampString: timestampString_2, + timestampString2: '2022-01-01 00:00:00.123', + timestampDate: timestampDate2, + }, + ]); + + t.deepEqual(result2, [ + { + id: 1, + timestamp_string: timestampString, + timestamp_string2: '2022-01-02 00:00:00.123', + timestamp_date: timestampDate.toISOString().replace('T', ' ').replace('Z', ''), + }, + { + id: 2, + timestamp_string: timestampString_2, + timestamp_string2: '2022-01-01 00:00:00.123', + timestamp_date: timestampDate2.toISOString().replace('T', ' ').replace('Z', ''), + }, + ]); + + t.deepEqual(result2[0]?.timestamp_string, '2022-01-01 00:00:00.123456'); + // need to add the 'Z', otherwise javascript assumes it's in local time + t.deepEqual(new Date(result2[0]?.timestamp_date + 'Z' as any).getTime(), timestampDate.getTime()); + + await db.execute(sql`drop table if exists ${table}`); +}); + // TODO: implement transaction // test.serial('transaction', async (t) => { // const { db } = t.context; diff --git a/integration-tests/tests/pg.test.ts b/integration-tests/tests/pg.test.ts index 36a18c2fd..d051d4fa8 100644 --- a/integration-tests/tests/pg.test.ts +++ b/integration-tests/tests/pg.test.ts @@ -52,6 +52,7 @@ import { jsonb, macaddr, macaddr8, + numeric, type PgColumn, pgEnum, pgMaterializedView, @@ -69,7 +70,6 @@ import { uniqueKeyName, uuid as pgUuid, varchar, - numeric } from 'drizzle-orm/pg-core'; import getPort from 'get-port'; import pg from 'pg'; @@ -1666,7 +1666,7 @@ test.serial('with ... select', async (t) => { productUnits: 16, productSales: 160, }, - ]) + ]); }); test.serial('with ... update', async (t) => { @@ -1675,7 +1675,7 @@ test.serial('with ... update', async (t) => { const products = pgTable('products', { id: serial('id').primaryKey(), price: numeric('price').notNull(), - cheap: boolean('cheap').notNull().default(false) + cheap: boolean('cheap').notNull().default(false), }); await db.execute(sql`drop table if exists ${products}`); @@ -1700,26 +1700,26 @@ test.serial('with ... update', async (t) => { .as( db .select({ - value: sql`avg(${products.price})`.as('value') + value: sql`avg(${products.price})`.as('value'), }) - .from(products) + .from(products), ); const result = await db .with(averagePrice) .update(products) .set({ - cheap: true + cheap: true, }) .where(lt(products.price, sql`(select * from ${averagePrice})`)) .returning({ - id: products.id + id: products.id, }); t.deepEqual(result, [ { id: 1 }, { id: 4 }, - { id: 5 } + { id: 5 }, ]); }); @@ -1728,7 +1728,7 @@ test.serial('with ... insert', async (t) => { const users = pgTable('users', { username: text('username').notNull(), - admin: boolean('admin').notNull() + admin: boolean('admin').notNull(), }); await db.execute(sql`drop table if exists ${users}`); @@ -1739,22 +1739,22 @@ test.serial('with ... insert', async (t) => { .as( db .select({ - value: sql`count(*)`.as('value') + value: sql`count(*)`.as('value'), }) - .from(users) + .from(users), ); const result = await db .with(userCount) .insert(users) .values([ - { username: 'user1', admin: sql`((select * from ${userCount}) = 0)` } + { username: 'user1', admin: sql`((select * from ${userCount}) = 0)` }, ]) .returning({ - admin: users.admin + admin: users.admin, }); - t.deepEqual(result, [{ admin: true }]) + t.deepEqual(result, [{ admin: true }]); }); test.serial('with ... delete', async (t) => { @@ -1776,9 +1776,9 @@ test.serial('with ... delete', async (t) => { .as( db .select({ - value: sql`avg(${orders.amount})`.as('value') + value: sql`avg(${orders.amount})`.as('value'), }) - .from(orders) + .from(orders), ); const result = await db @@ -1786,13 +1786,13 @@ test.serial('with ... delete', async (t) => { .delete(orders) .where(gt(orders.amount, sql`(select * from ${averageAmount})`)) .returning({ - id: orders.id + id: orders.id, }); t.deepEqual(result, [ { id: 6 }, { id: 7 }, - { id: 8 } + { id: 8 }, ]); }); @@ -2480,6 +2480,166 @@ test.serial('all date and time columns', async (t) => { await db.execute(sql`drop table if exists ${table}`); }); +test.serial('all date and time columns with timezone', async (t) => { + const { db } = t.context; + + const table = pgTable('all_columns', { + id: serial('id').primaryKey(), + timestamp: timestamp('timestamp_string', { mode: 'string', withTimezone: true, precision: 6 }).notNull(), + timestampAsDate: timestamp('timestamp_date', { withTimezone: true, precision: 3 }).notNull(), + timestampTimeZones: timestamp('timestamp_date_2', { withTimezone: true, precision: 3 }).notNull(), + }); + + await db.execute(sql`drop table if exists ${table}`); + + await db.execute(sql` + create table ${table} ( + id serial primary key, + timestamp_string timestamp(6) with time zone not null, + timestamp_date timestamp(3) with time zone not null, + timestamp_date_2 timestamp(3) with time zone not null + ) + `); + + const timestampString = '2022-01-01 00:00:00.123456-0200'; + const timestampDate = new Date(); + const timestampDateWTZ = new Date('2022-01-01 00:00:00.123 +0500'); + + const timestampString2 = '2022-01-01 00:00:00.123456-0400'; + const timestampDate2 = new Date(); + const timestampDateWTZ2 = new Date('2022-01-01 00:00:00.123 +0200'); + + await db.insert(table).values([ + { timestamp: timestampString, timestampAsDate: timestampDate, timestampTimeZones: timestampDateWTZ }, + { timestamp: timestampString2, timestampAsDate: timestampDate2, timestampTimeZones: timestampDateWTZ2 }, + ]); + + const result = await db.select().from(table); + const result2 = await db.execute<{ + id: number; + timestamp_string: string; + timestamp_date: string; + timestamp_date_2: string; + }>(sql`select * from ${table}`); + + // Whatever you put in, you get back when you're using the date mode + // But when using the string mode, postgres returns a string transformed into UTC + t.deepEqual(result, [ + { + id: 1, + timestamp: '2022-01-01 02:00:00.123456+00', + timestampAsDate: timestampDate, + timestampTimeZones: timestampDateWTZ, + }, + { + id: 2, + timestamp: '2022-01-01 04:00:00.123456+00', + timestampAsDate: timestampDate2, + timestampTimeZones: timestampDateWTZ2, + }, + ]); + + t.deepEqual(result2.rows, [ + { + id: 1, + timestamp_string: '2022-01-01 02:00:00.123456+00', + timestamp_date: timestampDate.toISOString().replace('T', ' ').replace('Z', '') + '+00', + timestamp_date_2: timestampDateWTZ.toISOString().replace('T', ' ').replace('Z', '') + '+00', + }, + { + id: 2, + timestamp_string: '2022-01-01 04:00:00.123456+00', + timestamp_date: timestampDate2.toISOString().replace('T', ' ').replace('Z', '') + '+00', + timestamp_date_2: timestampDateWTZ2.toISOString().replace('T', ' ').replace('Z', '') + '+00', + }, + ]); + + t.deepEqual(result[0]?.timestampTimeZones.getTime(), new Date(result2.rows[0]?.timestamp_date_2 as any).getTime()); + + await db.execute(sql`drop table if exists ${table}`); +}); + +test.serial('all date and time columns without timezone', async (t) => { + const { db } = t.context; + + const table = pgTable('all_columns', { + id: serial('id').primaryKey(), + timestampString: timestamp('timestamp_string', { mode: 'string', precision: 6 }).notNull(), + timestampString2: timestamp('timestamp_string2', { precision: 3, mode: 'string' }).notNull(), + timestampDate: timestamp('timestamp_date', { precision: 3 }).notNull(), + }); + + await db.execute(sql`drop table if exists ${table}`); + + await db.execute(sql` + create table ${table} ( + id serial primary key, + timestamp_string timestamp(6) not null, + timestamp_string2 timestamp(3) not null, + timestamp_date timestamp(3) not null + ) + `); + + const timestampString = '2022-01-01 00:00:00.123456'; + const timestampString2 = '2022-01-02 00:00:00.123 -0300'; + const timestampDate = new Date('2022-01-01 00:00:00.123Z'); + + const timestampString_2 = '2022-01-01 00:00:00.123456'; + const timestampString2_2 = '2022-01-01 00:00:00.123 -0300'; + const timestampDate2 = new Date('2022-01-01 00:00:00.123 +0200'); + + await db.insert(table).values([ + { timestampString, timestampString2, timestampDate }, + { timestampString: timestampString_2, timestampString2: timestampString2_2, timestampDate: timestampDate2 }, + ]); + + const result = await db.select().from(table); + const result2 = await db.execute<{ + id: number; + timestamp_string: string; + timestamp_string2: string; + timestamp_date: string; + }>(sql`select * from ${table}`); + + // Whatever you put in, you get back when you're using the date mode + // But when using the string mode, postgres returns a string transformed into UTC + t.deepEqual(result, [ + { + id: 1, + timestampString: timestampString, + timestampString2: '2022-01-02 00:00:00.123', + timestampDate: timestampDate, + }, + { + id: 2, + timestampString: timestampString_2, + timestampString2: '2022-01-01 00:00:00.123', + timestampDate: timestampDate2, + }, + ]); + + t.deepEqual(result2.rows, [ + { + id: 1, + timestamp_string: timestampString, + timestamp_string2: '2022-01-02 00:00:00.123', + timestamp_date: timestampDate.toISOString().replace('T', ' ').replace('Z', ''), + }, + { + id: 2, + timestamp_string: timestampString_2, + timestamp_string2: '2022-01-01 00:00:00.123', + timestamp_date: timestampDate2.toISOString().replace('T', ' ').replace('Z', ''), + }, + ]); + + t.deepEqual(result2.rows[0]?.timestamp_string, '2022-01-01 00:00:00.123456'); + // need to add the 'Z', otherwise javascript assumes it's in local time + t.deepEqual(new Date(result2.rows[0]?.timestamp_date + 'Z' as any).getTime(), timestampDate.getTime()); + + await db.execute(sql`drop table if exists ${table}`); +}); + test.serial('orderBy with aliased column', (t) => { const { db } = t.context; diff --git a/integration-tests/tests/postgres.js.test.ts b/integration-tests/tests/postgres.js.test.ts index 8cf9ce985..d17b09c37 100644 --- a/integration-tests/tests/postgres.js.test.ts +++ b/integration-tests/tests/postgres.js.test.ts @@ -2002,6 +2002,166 @@ test.serial('all date and time columns', async (t) => { await db.execute(sql`drop table if exists ${table}`); }); +test.serial('all date and time columns with timezone', async (t) => { + const { db } = t.context; + + const table = pgTable('all_columns', { + id: serial('id').primaryKey(), + timestamp: timestamp('timestamp_string', { mode: 'string', withTimezone: true, precision: 6 }).notNull(), + timestampAsDate: timestamp('timestamp_date', { withTimezone: true, precision: 3 }).notNull(), + timestampTimeZones: timestamp('timestamp_date_2', { withTimezone: true, precision: 3 }).notNull(), + }); + + await db.execute(sql`drop table if exists ${table}`); + + await db.execute(sql` + create table ${table} ( + id serial primary key, + timestamp_string timestamp(6) with time zone not null, + timestamp_date timestamp(3) with time zone not null, + timestamp_date_2 timestamp(3) with time zone not null + ) + `); + + const timestampString = '2022-01-01 00:00:00.123456-0200'; + const timestampDate = new Date(); + const timestampDateWTZ = new Date('2022-01-01 00:00:00.123 +0500'); + + const timestampString2 = '2022-01-01 00:00:00.123456-0400'; + const timestampDate2 = new Date(); + const timestampDateWTZ2 = new Date('2022-01-01 00:00:00.123 +0200'); + + await db.insert(table).values([ + { timestamp: timestampString, timestampAsDate: timestampDate, timestampTimeZones: timestampDateWTZ }, + { timestamp: timestampString2, timestampAsDate: timestampDate2, timestampTimeZones: timestampDateWTZ2 }, + ]); + + const result = await db.select().from(table); + const result2 = await db.execute<{ + id: number; + timestamp_string: string; + timestamp_date: string; + timestamp_date_2: string; + }>(sql`select * from ${table}`); + + // Whatever you put in, you get back when you're using the date mode + // But when using the string mode, postgres returns a string transformed into UTC + t.deepEqual(result, [ + { + id: 1, + timestamp: '2022-01-01 02:00:00.123456+00', + timestampAsDate: timestampDate, + timestampTimeZones: timestampDateWTZ, + }, + { + id: 2, + timestamp: '2022-01-01 04:00:00.123456+00', + timestampAsDate: timestampDate2, + timestampTimeZones: timestampDateWTZ2, + }, + ]); + + t.deepEqual([...result2], [ + { + id: 1, + timestamp_string: '2022-01-01 02:00:00.123456+00', + timestamp_date: timestampDate.toISOString().replace('T', ' ').replace('Z', '') + '+00', + timestamp_date_2: timestampDateWTZ.toISOString().replace('T', ' ').replace('Z', '') + '+00', + }, + { + id: 2, + timestamp_string: '2022-01-01 04:00:00.123456+00', + timestamp_date: timestampDate2.toISOString().replace('T', ' ').replace('Z', '') + '+00', + timestamp_date_2: timestampDateWTZ2.toISOString().replace('T', ' ').replace('Z', '') + '+00', + }, + ]); + + t.deepEqual(result[0]?.timestampTimeZones.getTime(), new Date(result2[0]?.timestamp_date_2 as any).getTime()); + + await db.execute(sql`drop table if exists ${table}`); +}); + +test.serial('all date and time columns without timezone', async (t) => { + const { db } = t.context; + + const table = pgTable('all_columns', { + id: serial('id').primaryKey(), + timestampString: timestamp('timestamp_string', { mode: 'string', precision: 6 }).notNull(), + timestampString2: timestamp('timestamp_string2', { precision: 3, mode: 'string' }).notNull(), + timestampDate: timestamp('timestamp_date', { precision: 3 }).notNull(), + }); + + await db.execute(sql`drop table if exists ${table}`); + + await db.execute(sql` + create table ${table} ( + id serial primary key, + timestamp_string timestamp(6) not null, + timestamp_string2 timestamp(3) not null, + timestamp_date timestamp(3) not null + ) + `); + + const timestampString = '2022-01-01 00:00:00.123456'; + const timestampString2 = '2022-01-02 00:00:00.123 -0300'; + const timestampDate = new Date('2022-01-01 00:00:00.123Z'); + + const timestampString_2 = '2022-01-01 00:00:00.123456'; + const timestampString2_2 = '2022-01-01 00:00:00.123 -0300'; + const timestampDate2 = new Date('2022-01-01 00:00:00.123 +0200'); + + await db.insert(table).values([ + { timestampString, timestampString2, timestampDate }, + { timestampString: timestampString_2, timestampString2: timestampString2_2, timestampDate: timestampDate2 }, + ]); + + const result = await db.select().from(table); + const result2 = await db.execute<{ + id: number; + timestamp_string: string; + timestamp_string2: string; + timestamp_date: string; + }>(sql`select * from ${table}`); + + // Whatever you put in, you get back when you're using the date mode + // But when using the string mode, postgres returns a string transformed into UTC + t.deepEqual(result, [ + { + id: 1, + timestampString: timestampString, + timestampString2: '2022-01-02 00:00:00.123', + timestampDate: timestampDate, + }, + { + id: 2, + timestampString: timestampString_2, + timestampString2: '2022-01-01 00:00:00.123', + timestampDate: timestampDate2, + }, + ]); + + t.deepEqual([...result2], [ + { + id: 1, + timestamp_string: timestampString, + timestamp_string2: '2022-01-02 00:00:00.123', + timestamp_date: timestampDate.toISOString().replace('T', ' ').replace('Z', ''), + }, + { + id: 2, + timestamp_string: timestampString_2, + timestamp_string2: '2022-01-01 00:00:00.123', + timestamp_date: timestampDate2.toISOString().replace('T', ' ').replace('Z', ''), + }, + ]); + + t.deepEqual(result2[0]?.timestamp_string, '2022-01-01 00:00:00.123456'); + // need to add the 'Z', otherwise javascript assumes it's in local time + t.deepEqual(new Date(result2[0]?.timestamp_date + 'Z' as any).getTime(), timestampDate.getTime()); + + await db.execute(sql`drop table if exists ${table}`); +}); + test.serial('transaction', async (t) => { const { db } = t.context; From c7b07d1ce661d7928a853c82538eeaf2142f7183 Mon Sep 17 00:00:00 2001 From: Angelelz Date: Wed, 3 Jan 2024 00:01:24 -0500 Subject: [PATCH 08/10] Added detailed tests --- integration-tests/tests/pg.test.ts | 182 ++++++++++++++++++++ integration-tests/tests/postgres.js.test.ts | 182 ++++++++++++++++++++ 2 files changed, 364 insertions(+) diff --git a/integration-tests/tests/pg.test.ts b/integration-tests/tests/pg.test.ts index d051d4fa8..c3d705b2e 100644 --- a/integration-tests/tests/pg.test.ts +++ b/integration-tests/tests/pg.test.ts @@ -2640,6 +2640,188 @@ test.serial('all date and time columns without timezone', async (t) => { await db.execute(sql`drop table if exists ${table}`); }); +test.serial('test mode string for timestamp with timezone', async (t) => { + const { db } = t.context; + + const table = pgTable('all_columns', { + id: serial('id').primaryKey(), + timestamp: timestamp('timestamp_string', { mode: 'string', withTimezone: true, precision: 6 }).notNull(), + }); + + await db.execute(sql`drop table if exists ${table}`); + + await db.execute(sql` + create table ${table} ( + id serial primary key, + timestamp_string timestamp(6) with time zone not null + ) + `); + + const timestampString = '2022-01-01 00:00:00.123456-0200'; + + // 1. Insert date in string format with timezone in it + await db.insert(table).values([ + { timestamp: timestampString }, + ]); + + // 2. Select date in string format and check that the values are the same + const result = await db.select().from(table); + + // 2.1 Notice that postgres will return the date in UTC, but it is exactly the same + t.deepEqual(result, [{ id: 1, timestamp: '2022-01-01 02:00:00.123456+00' }]); + + // 3. Select as raw query and checke that values are the same + const result2 = await db.execute<{ + id: number; + timestamp_string: string; + }>(sql`select * from ${table}`); + + // 3.1 Notice that postgres will return the date in UTC, but it is exactlt the same + t.deepEqual(result2.rows, [{ id: 1, timestamp_string: '2022-01-01 02:00:00.123456+00' }]); + + await db.execute(sql`drop table if exists ${table}`); +}); + +test.serial('test mode date for timestamp with timezone', async (t) => { + const { db } = t.context; + + const table = pgTable('all_columns', { + id: serial('id').primaryKey(), + timestamp: timestamp('timestamp_string', { mode: 'date', withTimezone: true, precision: 3 }).notNull(), + }); + + await db.execute(sql`drop table if exists ${table}`); + + await db.execute(sql` + create table ${table} ( + id serial primary key, + timestamp_string timestamp(3) with time zone not null + ) + `); + + const timestampString = new Date('2022-01-01 00:00:00.456-0200'); + + // 1. Insert date in string format with timezone in it + await db.insert(table).values([ + { timestamp: timestampString }, + ]); + + // 2. Select date in string format and check that the values are the same + const result = await db.select().from(table); + + // 2.1 Notice that postgres will return the date in UTC, but it is exactly the same + t.deepEqual(result, [{ id: 1, timestamp: timestampString }]); + + // 3. Select as raw query and checke that values are the same + const result2 = await db.execute<{ + id: number; + timestamp_string: string; + }>(sql`select * from ${table}`); + + // 3.1 Notice that postgres will return the date in UTC, but it is exactlt the same + t.deepEqual(result2.rows, [{ id: 1, timestamp_string: '2022-01-01 02:00:00.456+00' }]); + + await db.execute(sql`drop table if exists ${table}`); +}); + +test.serial('test mode string for timestamp with timezone in UTC timezone', async (t) => { + const { db } = t.context; + + // get current timezone from db + const timezone = await db.execute<{ TimeZone: string }>(sql`show timezone`); + + // set timezone to UTC + await db.execute(sql`set time zone 'UTC'`); + + const table = pgTable('all_columns', { + id: serial('id').primaryKey(), + timestamp: timestamp('timestamp_string', { mode: 'string', withTimezone: true, precision: 6 }).notNull(), + }); + + await db.execute(sql`drop table if exists ${table}`); + + await db.execute(sql` + create table ${table} ( + id serial primary key, + timestamp_string timestamp(6) with time zone not null + ) + `); + + const timestampString = '2022-01-01 00:00:00.123456-0200'; + + // 1. Insert date in string format with timezone in it + await db.insert(table).values([ + { timestamp: timestampString }, + ]); + + // 2. Select date in string format and check that the values are the same + const result = await db.select().from(table); + + // 2.1 Notice that postgres will return the date in UTC, but it is exactly the same + t.deepEqual(result, [{ id: 1, timestamp: '2022-01-01 02:00:00.123456+00' }]); + + // 3. Select as raw query and checke that values are the same + const result2 = await db.execute<{ + id: number; + timestamp_string: string; + }>(sql`select * from ${table}`); + + // 3.1 Notice that postgres will return the date in UTC, but it is exactlt the same + t.deepEqual(result2.rows, [{ id: 1, timestamp_string: '2022-01-01 02:00:00.123456+00' }]); + + await db.execute(sql`set time zone '${sql.raw(timezone.rows[0]!.TimeZone)}'`); + + await db.execute(sql`drop table if exists ${table}`); +}); + +test.serial('test mode string for timestamp with timezone in different timezone', async (t) => { + const { db } = t.context; + + // get current timezone from db + const timezone = await db.execute<{ TimeZone: string }>(sql`show timezone`); + + // set timezone to HST (UTC - 10) + await db.execute(sql`set time zone 'HST'`); + + const table = pgTable('all_columns', { + id: serial('id').primaryKey(), + timestamp: timestamp('timestamp_string', { mode: 'string', withTimezone: true, precision: 6 }).notNull(), + }); + + await db.execute(sql`drop table if exists ${table}`); + + await db.execute(sql` + create table ${table} ( + id serial primary key, + timestamp_string timestamp(6) with time zone not null + ) + `); + + const timestampString = '2022-01-01 00:00:00.123456-1000'; + + // 1. Insert date in string format with timezone in it + await db.insert(table).values([ + { timestamp: timestampString }, + ]); + + // 2. Select date in string format and check that the values are the same + const result = await db.select().from(table); + + t.deepEqual(result, [{ id: 1, timestamp: '2022-01-01 00:00:00.123456-10' }]); + + // 3. Select as raw query and checke that values are the same + const result2 = await db.execute<{ + id: number; + timestamp_string: string; + }>(sql`select * from ${table}`); + + t.deepEqual(result2.rows, [{ id: 1, timestamp_string: '2022-01-01 00:00:00.123456-10' }]); + + await db.execute(sql`set time zone '${sql.raw(timezone.rows[0]!.TimeZone)}'`); + + await db.execute(sql`drop table if exists ${table}`); +}); + test.serial('orderBy with aliased column', (t) => { const { db } = t.context; diff --git a/integration-tests/tests/postgres.js.test.ts b/integration-tests/tests/postgres.js.test.ts index d17b09c37..30f4e614f 100644 --- a/integration-tests/tests/postgres.js.test.ts +++ b/integration-tests/tests/postgres.js.test.ts @@ -2162,6 +2162,188 @@ test.serial('all date and time columns without timezone', async (t) => { await db.execute(sql`drop table if exists ${table}`); }); +test.serial('test mode string for timestamp with timezone', async (t) => { + const { db } = t.context; + + const table = pgTable('all_columns', { + id: serial('id').primaryKey(), + timestamp: timestamp('timestamp_string', { mode: 'string', withTimezone: true, precision: 6 }).notNull(), + }); + + await db.execute(sql`drop table if exists ${table}`); + + await db.execute(sql` + create table ${table} ( + id serial primary key, + timestamp_string timestamp(6) with time zone not null + ) + `); + + const timestampString = '2022-01-01 00:00:00.123456-0200'; + + // 1. Insert date in string format with timezone in it + await db.insert(table).values([ + { timestamp: timestampString }, + ]); + + // 2. Select date in string format and check that the values are the same + const result = await db.select().from(table); + + // 2.1 Notice that postgres will return the date in UTC, but it is exactly the same + t.deepEqual(result, [{ id: 1, timestamp: '2022-01-01 02:00:00.123456+00' }]); + + // 3. Select as raw query and checke that values are the same + const result2 = await db.execute<{ + id: number; + timestamp_string: string; + }>(sql`select * from ${table}`); + + // 3.1 Notice that postgres will return the date in UTC, but it is exactlt the same + t.deepEqual([...result2], [{ id: 1, timestamp_string: '2022-01-01 02:00:00.123456+00' }]); + + await db.execute(sql`drop table if exists ${table}`); +}); + +test.serial('test mode date for timestamp with timezone', async (t) => { + const { db } = t.context; + + const table = pgTable('all_columns', { + id: serial('id').primaryKey(), + timestamp: timestamp('timestamp_string', { mode: 'date', withTimezone: true, precision: 3 }).notNull(), + }); + + await db.execute(sql`drop table if exists ${table}`); + + await db.execute(sql` + create table ${table} ( + id serial primary key, + timestamp_string timestamp(3) with time zone not null + ) + `); + + const timestampString = new Date('2022-01-01 00:00:00.456-0200'); + + // 1. Insert date in string format with timezone in it + await db.insert(table).values([ + { timestamp: timestampString }, + ]); + + // 2. Select date in string format and check that the values are the same + const result = await db.select().from(table); + + // 2.1 Notice that postgres will return the date in UTC, but it is exactly the same + t.deepEqual(result, [{ id: 1, timestamp: timestampString }]); + + // 3. Select as raw query and checke that values are the same + const result2 = await db.execute<{ + id: number; + timestamp_string: string; + }>(sql`select * from ${table}`); + + // 3.1 Notice that postgres will return the date in UTC, but it is exactlt the same + t.deepEqual([...result2], [{ id: 1, timestamp_string: '2022-01-01 02:00:00.456+00' }]); + + await db.execute(sql`drop table if exists ${table}`); +}); + +test.serial('test mode string for timestamp with timezone in UTC timezone', async (t) => { + const { db } = t.context; + + // get current timezone from db + const [timezone] = await db.execute<{ TimeZone: string }>(sql`show timezone`); + + // set timezone to UTC + await db.execute(sql`set time zone 'UTC'`); + + const table = pgTable('all_columns', { + id: serial('id').primaryKey(), + timestamp: timestamp('timestamp_string', { mode: 'string', withTimezone: true, precision: 6 }).notNull(), + }); + + await db.execute(sql`drop table if exists ${table}`); + + await db.execute(sql` + create table ${table} ( + id serial primary key, + timestamp_string timestamp(6) with time zone not null + ) + `); + + const timestampString = '2022-01-01 00:00:00.123456-0200'; + + // 1. Insert date in string format with timezone in it + await db.insert(table).values([ + { timestamp: timestampString }, + ]); + + // 2. Select date in string format and check that the values are the same + const result = await db.select().from(table); + + // 2.1 Notice that postgres will return the date in UTC, but it is exactly the same + t.deepEqual(result, [{ id: 1, timestamp: '2022-01-01 02:00:00.123456+00' }]); + + // 3. Select as raw query and checke that values are the same + const result2 = await db.execute<{ + id: number; + timestamp_string: string; + }>(sql`select * from ${table}`); + + // 3.1 Notice that postgres will return the date in UTC, but it is exactlt the same + t.deepEqual([...result2], [{ id: 1, timestamp_string: '2022-01-01 02:00:00.123456+00' }]); + + await db.execute(sql`set time zone '${sql.raw(timezone!.TimeZone)}'`); + + await db.execute(sql`drop table if exists ${table}`); +}); + +test.serial('test mode string for timestamp with timezone in different timezone', async (t) => { + const { db } = t.context; + + // get current timezone from db + const [timezone] = await db.execute<{ TimeZone: string }>(sql`show timezone`); + + // set timezone to HST (UTC - 10) + await db.execute(sql`set time zone 'HST'`); + + const table = pgTable('all_columns', { + id: serial('id').primaryKey(), + timestamp: timestamp('timestamp_string', { mode: 'string', withTimezone: true, precision: 6 }).notNull(), + }); + + await db.execute(sql`drop table if exists ${table}`); + + await db.execute(sql` + create table ${table} ( + id serial primary key, + timestamp_string timestamp(6) with time zone not null + ) + `); + + const timestampString = '2022-01-01 00:00:00.123456-1000'; + + // 1. Insert date in string format with timezone in it + await db.insert(table).values([ + { timestamp: timestampString }, + ]); + + // 2. Select date in string format and check that the values are the same + const result = await db.select().from(table); + + t.deepEqual(result, [{ id: 1, timestamp: '2022-01-01 00:00:00.123456-10' }]); + + // 3. Select as raw query and checke that values are the same + const result2 = await db.execute<{ + id: number; + timestamp_string: string; + }>(sql`select * from ${table}`); + + t.deepEqual([...result2], [{ id: 1, timestamp_string: '2022-01-01 00:00:00.123456-10' }]); + + await db.execute(sql`set time zone '${sql.raw(timezone!.TimeZone)}'`); + + await db.execute(sql`drop table if exists ${table}`); +}); + test.serial('transaction', async (t) => { const { db } = t.context; From c606ebe4909002273789a434f3b6d8889312cb52 Mon Sep 17 00:00:00 2001 From: Angelelz Date: Wed, 3 Jan 2024 21:24:18 -0500 Subject: [PATCH 09/10] Separated tests per cases as requested --- integration-tests/tests/pg-proxy.test.ts | 251 ++++++++++++-------- integration-tests/tests/pg.test.ts | 251 ++++++++++++-------- integration-tests/tests/postgres.js.test.ts | 251 ++++++++++++-------- 3 files changed, 465 insertions(+), 288 deletions(-) diff --git a/integration-tests/tests/pg-proxy.test.ts b/integration-tests/tests/pg-proxy.test.ts index 750d96aba..c98c0c10b 100644 --- a/integration-tests/tests/pg-proxy.test.ts +++ b/integration-tests/tests/pg-proxy.test.ts @@ -2358,14 +2358,12 @@ test.serial('all date and time columns', async (t) => { await db.execute(sql`drop table if exists ${table}`); }); -test.serial('all date and time columns with timezone', async (t) => { +test.serial('all date and time columns with timezone first case mode string', async (t) => { const { db } = t.context; const table = pgTable('all_columns', { id: serial('id').primaryKey(), timestamp: timestamp('timestamp_string', { mode: 'string', withTimezone: true, precision: 6 }).notNull(), - timestampAsDate: timestamp('timestamp_date', { withTimezone: true, precision: 3 }).notNull(), - timestampTimeZones: timestamp('timestamp_date_2', { withTimezone: true, precision: 3 }).notNull(), }); await db.execute(sql`drop table if exists ${table}`); @@ -2373,78 +2371,75 @@ test.serial('all date and time columns with timezone', async (t) => { await db.execute(sql` create table ${table} ( id serial primary key, - timestamp_string timestamp(6) with time zone not null, - timestamp_date timestamp(3) with time zone not null, - timestamp_date_2 timestamp(3) with time zone not null + timestamp_string timestamp(6) with time zone not null ) `); - const timestampString = '2022-01-01 00:00:00.123456-0200'; - const timestampDate = new Date(); - const timestampDateWTZ = new Date('2022-01-01 00:00:00.123 +0500'); - - const timestampString2 = '2022-01-01 00:00:00.123456-0400'; - const timestampDate2 = new Date(); - const timestampDateWTZ2 = new Date('2022-01-01 00:00:00.123 +0200'); - + // 1. Insert date in string format with timezone in it await db.insert(table).values([ - { timestamp: timestampString, timestampAsDate: timestampDate, timestampTimeZones: timestampDateWTZ }, - { timestamp: timestampString2, timestampAsDate: timestampDate2, timestampTimeZones: timestampDateWTZ2 }, + { timestamp: '2022-01-01 02:00:00.123456-0200' }, ]); + // 2, Select in string format and check that values are the same const result = await db.select().from(table); + + t.deepEqual(result, [{ id: 1, timestamp: '2022-01-01 04:00:00.123456+00' }]); + + // 3. Select as raw query and check that values are the same const result2 = await db.execute<{ id: number; timestamp_string: string; - timestamp_date: string; - timestamp_date_2: string; }>(sql`select * from ${table}`); - // Whatever you put in, you get back when you're using the date mode - // But when using the string mode, postgres returns a string transformed into UTC - t.deepEqual(result, [ - { - id: 1, - timestamp: '2022-01-01 02:00:00.123456+00', - timestampAsDate: timestampDate, - timestampTimeZones: timestampDateWTZ, - }, - { - id: 2, - timestamp: '2022-01-01 04:00:00.123456+00', - timestampAsDate: timestampDate2, - timestampTimeZones: timestampDateWTZ2, - }, - ]); + t.deepEqual(result, [{ id: 1, timestamp: '2022-01-01 04:00:00.123456+00' }]); - t.deepEqual(result2, [ - { - id: 1, - timestamp_string: '2022-01-01 02:00:00.123456+00', - timestamp_date: timestampDate.toISOString().replace('T', ' ').replace('Z', '') + '+00', - timestamp_date_2: timestampDateWTZ.toISOString().replace('T', ' ').replace('Z', '') + '+00', - }, - { - id: 2, - timestamp_string: '2022-01-01 04:00:00.123456+00', - timestamp_date: timestampDate2.toISOString().replace('T', ' ').replace('Z', '') + '+00', - timestamp_date_2: timestampDateWTZ2.toISOString().replace('T', ' ').replace('Z', '') + '+00', - }, + t.deepEqual([...result2], [{ id: 1, timestamp_string: '2022-01-01 04:00:00.123456+00' }]); + + await db.execute(sql`drop table if exists ${table}`); +}); + +test.serial('all date and time columns with timezone second case mode date', async (t) => { + const { db } = t.context; + + const table = pgTable('all_columns', { + id: serial('id').primaryKey(), + timestamp: timestamp('timestamp_string', { mode: 'date', withTimezone: true, precision: 3 }).notNull(), + }); + + await db.execute(sql`drop table if exists ${table}`); + + await db.execute(sql` + create table ${table} ( + id serial primary key, + timestamp_string timestamp(3) with time zone not null + ) + `); + + const insertedDate = new Date(); + + // 1. Insert date as new date + await db.insert(table).values([ + { timestamp: insertedDate }, ]); - t.deepEqual(result[0]?.timestampTimeZones.getTime(), new Date(result2[0]?.timestamp_date_2 as any).getTime()); + // 2, Select as date and check that timezones are the same + // There is no way to check timezone in Date object, as it is always represented internally in UTC + const result = await db.select().from(table); + + t.deepEqual(result, [{ id: 1, timestamp: insertedDate }]); + + // 3. Compare both dates + t.deepEqual(insertedDate.getTime(), result[0]?.timestamp.getTime()); await db.execute(sql`drop table if exists ${table}`); }); -test.serial('all date and time columns without timezone', async (t) => { +test.serial('all date and time columns with timezone third case mode date', async (t) => { const { db } = t.context; const table = pgTable('all_columns', { id: serial('id').primaryKey(), - timestampString: timestamp('timestamp_string', { mode: 'string', precision: 6 }).notNull(), - timestampString2: timestamp('timestamp_string2', { precision: 3, mode: 'string' }).notNull(), - timestampDate: timestamp('timestamp_date', { precision: 3 }).notNull(), + timestamp: timestamp('timestamp_string', { mode: 'date', withTimezone: true, precision: 3 }).notNull(), }); await db.execute(sql`drop table if exists ${table}`); @@ -2452,68 +2447,132 @@ test.serial('all date and time columns without timezone', async (t) => { await db.execute(sql` create table ${table} ( id serial primary key, - timestamp_string timestamp(6) not null, - timestamp_string2 timestamp(3) not null, - timestamp_date timestamp(3) not null + timestamp_string timestamp(3) with time zone not null ) `); - const timestampString = '2022-01-01 00:00:00.123456'; - const timestampString2 = '2022-01-02 00:00:00.123 -0300'; - const timestampDate = new Date('2022-01-01 00:00:00.123Z'); + const insertedDate = new Date('2022-01-01 20:00:00.123-04'); // used different time zones, internally is still UTC + const insertedDate2 = new Date('2022-01-02 04:00:00.123+04'); // They are both the same date in different time zones + + // 1. Insert date as new dates with different time zones + await db.insert(table).values([ + { timestamp: insertedDate }, + { timestamp: insertedDate2 }, + ]); + + // 2, Select and compare both dates + const result = await db.select().from(table); + + t.deepEqual(result[0]?.timestamp.getTime(), result[1]?.timestamp.getTime()); - const timestampString_2 = '2022-01-01 00:00:00.123456'; - const timestampString2_2 = '2022-01-01 00:00:00.123 -0300'; - const timestampDate2 = new Date('2022-01-01 00:00:00.123 +0200'); + await db.execute(sql`drop table if exists ${table}`); +}); + +test.serial('all date and time columns without timezone first case mode string', async (t) => { + const { db } = t.context; + + const table = pgTable('all_columns', { + id: serial('id').primaryKey(), + timestamp: timestamp('timestamp_string', { mode: 'string', precision: 6 }).notNull(), + }); + + await db.execute(sql`drop table if exists ${table}`); + + await db.execute(sql` + create table ${table} ( + id serial primary key, + timestamp_string timestamp(6) not null + ) + `); + // 1. Insert date in string format without timezone in it await db.insert(table).values([ - { timestampString, timestampString2, timestampDate }, - { timestampString: timestampString_2, timestampString2: timestampString2_2, timestampDate: timestampDate2 }, + { timestamp: '2022-01-01 02:00:00.123456' }, ]); + // 2, Select in string format and check that values are the same const result = await db.select().from(table); + + t.deepEqual(result, [{ id: 1, timestamp: '2022-01-01 02:00:00.123456' }]); + + // 3. Select as raw query and check that values are the same const result2 = await db.execute<{ id: number; timestamp_string: string; - timestamp_string2: string; - timestamp_date: string; }>(sql`select * from ${table}`); - // Whatever you put in, you get back when you're using the date mode - // But when using the string mode, postgres returns a string transformed into UTC - t.deepEqual(result, [ - { - id: 1, - timestampString: timestampString, - timestampString2: '2022-01-02 00:00:00.123', - timestampDate: timestampDate, - }, - { - id: 2, - timestampString: timestampString_2, - timestampString2: '2022-01-01 00:00:00.123', - timestampDate: timestampDate2, - }, + t.deepEqual([...result2], [{ id: 1, timestamp_string: '2022-01-01 02:00:00.123456' }]); + + await db.execute(sql`drop table if exists ${table}`); +}); + +test.serial('all date and time columns without timezone second case mode string', async (t) => { + const { db } = t.context; + + const table = pgTable('all_columns', { + id: serial('id').primaryKey(), + timestamp: timestamp('timestamp_string', { mode: 'string', precision: 6 }).notNull(), + }); + + await db.execute(sql`drop table if exists ${table}`); + + await db.execute(sql` + create table ${table} ( + id serial primary key, + timestamp_string timestamp(6) with time zone not null + ) + `); + + // 1. Insert date in string format with timezone in it + await db.insert(table).values([ + { timestamp: '2022-01-01T02:00:00.123456-02' }, ]); - t.deepEqual(result2, [ - { - id: 1, - timestamp_string: timestampString, - timestamp_string2: '2022-01-02 00:00:00.123', - timestamp_date: timestampDate.toISOString().replace('T', ' ').replace('Z', ''), - }, - { - id: 2, - timestamp_string: timestampString_2, - timestamp_string2: '2022-01-01 00:00:00.123', - timestamp_date: timestampDate2.toISOString().replace('T', ' ').replace('Z', ''), - }, + // 2, Select as raw query and check that values are the same + const result = await db.execute<{ + id: number; + timestamp_string: string; + }>(sql`select * from ${table}`); + + // Please notice that postgres will transform the date to UTC and it saves it in UTC. + // even if set without time zone, just becuase it was provided a timezone offset, it will transform it to UTC + t.deepEqual([...result], [{ id: 1, timestamp_string: '2022-01-01 04:00:00.123456+00' }]); + + await db.execute(sql`drop table if exists ${table}`); +}); + +test.serial('all date and time columns without timezone third case mode date', async (t) => { + const { db } = t.context; + + const table = pgTable('all_columns', { + id: serial('id').primaryKey(), + timestamp: timestamp('timestamp_string', { mode: 'date', precision: 3 }).notNull(), + }); + + await db.execute(sql`drop table if exists ${table}`); + + await db.execute(sql` + create table ${table} ( + id serial primary key, + timestamp_string timestamp(3) with time zone not null + ) + `); + + const insertedDate = new Date('2022-01-01 20:00:00.123+04'); + + // 1. Insert date as new date + await db.insert(table).values([ + { timestamp: insertedDate }, ]); - t.deepEqual(result2[0]?.timestamp_string, '2022-01-01 00:00:00.123456'); - // need to add the 'Z', otherwise javascript assumes it's in local time - t.deepEqual(new Date(result2[0]?.timestamp_date + 'Z' as any).getTime(), timestampDate.getTime()); + // 2, Select as raw query as string + const result = await db.execute<{ + id: number; + timestamp_string: string; + }>(sql`select * from ${table}`); + + // 3. Compare both dates using orm mapping + t.deepEqual(new Date(result[0]!.timestamp_string).getTime(), insertedDate.getTime()); await db.execute(sql`drop table if exists ${table}`); }); diff --git a/integration-tests/tests/pg.test.ts b/integration-tests/tests/pg.test.ts index c3d705b2e..9955f98cf 100644 --- a/integration-tests/tests/pg.test.ts +++ b/integration-tests/tests/pg.test.ts @@ -2480,14 +2480,12 @@ test.serial('all date and time columns', async (t) => { await db.execute(sql`drop table if exists ${table}`); }); -test.serial('all date and time columns with timezone', async (t) => { +test.serial('all date and time columns with timezone first case mode string', async (t) => { const { db } = t.context; const table = pgTable('all_columns', { id: serial('id').primaryKey(), timestamp: timestamp('timestamp_string', { mode: 'string', withTimezone: true, precision: 6 }).notNull(), - timestampAsDate: timestamp('timestamp_date', { withTimezone: true, precision: 3 }).notNull(), - timestampTimeZones: timestamp('timestamp_date_2', { withTimezone: true, precision: 3 }).notNull(), }); await db.execute(sql`drop table if exists ${table}`); @@ -2495,78 +2493,75 @@ test.serial('all date and time columns with timezone', async (t) => { await db.execute(sql` create table ${table} ( id serial primary key, - timestamp_string timestamp(6) with time zone not null, - timestamp_date timestamp(3) with time zone not null, - timestamp_date_2 timestamp(3) with time zone not null + timestamp_string timestamp(6) with time zone not null ) `); - const timestampString = '2022-01-01 00:00:00.123456-0200'; - const timestampDate = new Date(); - const timestampDateWTZ = new Date('2022-01-01 00:00:00.123 +0500'); - - const timestampString2 = '2022-01-01 00:00:00.123456-0400'; - const timestampDate2 = new Date(); - const timestampDateWTZ2 = new Date('2022-01-01 00:00:00.123 +0200'); - + // 1. Insert date in string format with timezone in it await db.insert(table).values([ - { timestamp: timestampString, timestampAsDate: timestampDate, timestampTimeZones: timestampDateWTZ }, - { timestamp: timestampString2, timestampAsDate: timestampDate2, timestampTimeZones: timestampDateWTZ2 }, + { timestamp: '2022-01-01 02:00:00.123456-0200' }, ]); + // 2, Select in string format and check that values are the same const result = await db.select().from(table); + + t.deepEqual(result, [{ id: 1, timestamp: '2022-01-01 04:00:00.123456+00' }]); + + // 3. Select as raw query and check that values are the same const result2 = await db.execute<{ id: number; timestamp_string: string; - timestamp_date: string; - timestamp_date_2: string; }>(sql`select * from ${table}`); - // Whatever you put in, you get back when you're using the date mode - // But when using the string mode, postgres returns a string transformed into UTC - t.deepEqual(result, [ - { - id: 1, - timestamp: '2022-01-01 02:00:00.123456+00', - timestampAsDate: timestampDate, - timestampTimeZones: timestampDateWTZ, - }, - { - id: 2, - timestamp: '2022-01-01 04:00:00.123456+00', - timestampAsDate: timestampDate2, - timestampTimeZones: timestampDateWTZ2, - }, - ]); + t.deepEqual(result, [{ id: 1, timestamp: '2022-01-01 04:00:00.123456+00' }]); - t.deepEqual(result2.rows, [ - { - id: 1, - timestamp_string: '2022-01-01 02:00:00.123456+00', - timestamp_date: timestampDate.toISOString().replace('T', ' ').replace('Z', '') + '+00', - timestamp_date_2: timestampDateWTZ.toISOString().replace('T', ' ').replace('Z', '') + '+00', - }, - { - id: 2, - timestamp_string: '2022-01-01 04:00:00.123456+00', - timestamp_date: timestampDate2.toISOString().replace('T', ' ').replace('Z', '') + '+00', - timestamp_date_2: timestampDateWTZ2.toISOString().replace('T', ' ').replace('Z', '') + '+00', - }, + t.deepEqual(result2.rows, [{ id: 1, timestamp_string: '2022-01-01 04:00:00.123456+00' }]); + + await db.execute(sql`drop table if exists ${table}`); +}); + +test.serial('all date and time columns with timezone second case mode date', async (t) => { + const { db } = t.context; + + const table = pgTable('all_columns', { + id: serial('id').primaryKey(), + timestamp: timestamp('timestamp_string', { mode: 'date', withTimezone: true, precision: 3 }).notNull(), + }); + + await db.execute(sql`drop table if exists ${table}`); + + await db.execute(sql` + create table ${table} ( + id serial primary key, + timestamp_string timestamp(3) with time zone not null + ) + `); + + const insertedDate = new Date(); + + // 1. Insert date as new date + await db.insert(table).values([ + { timestamp: insertedDate }, ]); - t.deepEqual(result[0]?.timestampTimeZones.getTime(), new Date(result2.rows[0]?.timestamp_date_2 as any).getTime()); + // 2, Select as date and check that timezones are the same + // There is no way to check timezone in Date object, as it is always represented internally in UTC + const result = await db.select().from(table); + + t.deepEqual(result, [{ id: 1, timestamp: insertedDate }]); + + // 3. Compare both dates + t.deepEqual(insertedDate.getTime(), result[0]?.timestamp.getTime()); await db.execute(sql`drop table if exists ${table}`); }); -test.serial('all date and time columns without timezone', async (t) => { +test.serial('all date and time columns with timezone third case mode date', async (t) => { const { db } = t.context; const table = pgTable('all_columns', { id: serial('id').primaryKey(), - timestampString: timestamp('timestamp_string', { mode: 'string', precision: 6 }).notNull(), - timestampString2: timestamp('timestamp_string2', { precision: 3, mode: 'string' }).notNull(), - timestampDate: timestamp('timestamp_date', { precision: 3 }).notNull(), + timestamp: timestamp('timestamp_string', { mode: 'date', withTimezone: true, precision: 3 }).notNull(), }); await db.execute(sql`drop table if exists ${table}`); @@ -2574,68 +2569,132 @@ test.serial('all date and time columns without timezone', async (t) => { await db.execute(sql` create table ${table} ( id serial primary key, - timestamp_string timestamp(6) not null, - timestamp_string2 timestamp(3) not null, - timestamp_date timestamp(3) not null + timestamp_string timestamp(3) with time zone not null ) `); - const timestampString = '2022-01-01 00:00:00.123456'; - const timestampString2 = '2022-01-02 00:00:00.123 -0300'; - const timestampDate = new Date('2022-01-01 00:00:00.123Z'); + const insertedDate = new Date('2022-01-01 20:00:00.123-04'); // used different time zones, internally is still UTC + const insertedDate2 = new Date('2022-01-02 04:00:00.123+04'); // They are both the same date in different time zones + + // 1. Insert date as new dates with different time zones + await db.insert(table).values([ + { timestamp: insertedDate }, + { timestamp: insertedDate2 }, + ]); + + // 2, Select and compare both dates + const result = await db.select().from(table); + + t.deepEqual(result[0]?.timestamp.getTime(), result[1]?.timestamp.getTime()); - const timestampString_2 = '2022-01-01 00:00:00.123456'; - const timestampString2_2 = '2022-01-01 00:00:00.123 -0300'; - const timestampDate2 = new Date('2022-01-01 00:00:00.123 +0200'); + await db.execute(sql`drop table if exists ${table}`); +}); +test.serial('all date and time columns without timezone first case mode string', async (t) => { + const { db } = t.context; + + const table = pgTable('all_columns', { + id: serial('id').primaryKey(), + timestamp: timestamp('timestamp_string', { mode: 'string', precision: 6 }).notNull(), + }); + + await db.execute(sql`drop table if exists ${table}`); + + await db.execute(sql` + create table ${table} ( + id serial primary key, + timestamp_string timestamp(6) not null + ) + `); + + // 1. Insert date in string format without timezone in it await db.insert(table).values([ - { timestampString, timestampString2, timestampDate }, - { timestampString: timestampString_2, timestampString2: timestampString2_2, timestampDate: timestampDate2 }, + { timestamp: '2022-01-01 02:00:00.123456' }, ]); + // 2, Select in string format and check that values are the same const result = await db.select().from(table); + + t.deepEqual(result, [{ id: 1, timestamp: '2022-01-01 02:00:00.123456' }]); + + // 3. Select as raw query and check that values are the same const result2 = await db.execute<{ id: number; timestamp_string: string; - timestamp_string2: string; - timestamp_date: string; }>(sql`select * from ${table}`); - // Whatever you put in, you get back when you're using the date mode - // But when using the string mode, postgres returns a string transformed into UTC - t.deepEqual(result, [ - { - id: 1, - timestampString: timestampString, - timestampString2: '2022-01-02 00:00:00.123', - timestampDate: timestampDate, - }, - { - id: 2, - timestampString: timestampString_2, - timestampString2: '2022-01-01 00:00:00.123', - timestampDate: timestampDate2, - }, + t.deepEqual(result2.rows, [{ id: 1, timestamp_string: '2022-01-01 02:00:00.123456' }]); + + await db.execute(sql`drop table if exists ${table}`); +}); + +test.serial('all date and time columns without timezone second case mode string', async (t) => { + const { db } = t.context; + + const table = pgTable('all_columns', { + id: serial('id').primaryKey(), + timestamp: timestamp('timestamp_string', { mode: 'string', precision: 6 }).notNull(), + }); + + await db.execute(sql`drop table if exists ${table}`); + + await db.execute(sql` + create table ${table} ( + id serial primary key, + timestamp_string timestamp(6) with time zone not null + ) + `); + + // 1. Insert date in string format with timezone in it + await db.insert(table).values([ + { timestamp: '2022-01-01T02:00:00.123456-02' }, ]); - t.deepEqual(result2.rows, [ - { - id: 1, - timestamp_string: timestampString, - timestamp_string2: '2022-01-02 00:00:00.123', - timestamp_date: timestampDate.toISOString().replace('T', ' ').replace('Z', ''), - }, - { - id: 2, - timestamp_string: timestampString_2, - timestamp_string2: '2022-01-01 00:00:00.123', - timestamp_date: timestampDate2.toISOString().replace('T', ' ').replace('Z', ''), - }, + // 2, Select as raw query and check that values are the same + const result = await db.execute<{ + id: number; + timestamp_string: string; + }>(sql`select * from ${table}`); + + // Please notice that postgres will transform the date to UTC and it saves it in UTC. + // even if set without time zone, just becuase it was provided a timezone offset, it will transform it to UTC + t.deepEqual(result.rows, [{ id: 1, timestamp_string: '2022-01-01 04:00:00.123456+00' }]); + + await db.execute(sql`drop table if exists ${table}`); +}); + +test.serial('all date and time columns without timezone third case mode date', async (t) => { + const { db } = t.context; + + const table = pgTable('all_columns', { + id: serial('id').primaryKey(), + timestamp: timestamp('timestamp_string', { mode: 'date', precision: 3 }).notNull(), + }); + + await db.execute(sql`drop table if exists ${table}`); + + await db.execute(sql` + create table ${table} ( + id serial primary key, + timestamp_string timestamp(3) with time zone not null + ) + `); + + const insertedDate = new Date('2022-01-01 20:00:00.123+04'); + + // 1. Insert date as new date + await db.insert(table).values([ + { timestamp: insertedDate }, ]); - t.deepEqual(result2.rows[0]?.timestamp_string, '2022-01-01 00:00:00.123456'); - // need to add the 'Z', otherwise javascript assumes it's in local time - t.deepEqual(new Date(result2.rows[0]?.timestamp_date + 'Z' as any).getTime(), timestampDate.getTime()); + // 2, Select as raw query as string + const result = await db.execute<{ + id: number; + timestamp_string: string; + }>(sql`select * from ${table}`); + + // 3. Compare both dates using orm mapping + t.deepEqual(new Date(result.rows[0]!.timestamp_string).getTime(), insertedDate.getTime()); await db.execute(sql`drop table if exists ${table}`); }); diff --git a/integration-tests/tests/postgres.js.test.ts b/integration-tests/tests/postgres.js.test.ts index 30f4e614f..d079fa3c9 100644 --- a/integration-tests/tests/postgres.js.test.ts +++ b/integration-tests/tests/postgres.js.test.ts @@ -2002,14 +2002,12 @@ test.serial('all date and time columns', async (t) => { await db.execute(sql`drop table if exists ${table}`); }); -test.serial('all date and time columns with timezone', async (t) => { +test.serial('all date and time columns with timezone first case mode string', async (t) => { const { db } = t.context; const table = pgTable('all_columns', { id: serial('id').primaryKey(), timestamp: timestamp('timestamp_string', { mode: 'string', withTimezone: true, precision: 6 }).notNull(), - timestampAsDate: timestamp('timestamp_date', { withTimezone: true, precision: 3 }).notNull(), - timestampTimeZones: timestamp('timestamp_date_2', { withTimezone: true, precision: 3 }).notNull(), }); await db.execute(sql`drop table if exists ${table}`); @@ -2017,78 +2015,75 @@ test.serial('all date and time columns with timezone', async (t) => { await db.execute(sql` create table ${table} ( id serial primary key, - timestamp_string timestamp(6) with time zone not null, - timestamp_date timestamp(3) with time zone not null, - timestamp_date_2 timestamp(3) with time zone not null + timestamp_string timestamp(6) with time zone not null ) `); - const timestampString = '2022-01-01 00:00:00.123456-0200'; - const timestampDate = new Date(); - const timestampDateWTZ = new Date('2022-01-01 00:00:00.123 +0500'); - - const timestampString2 = '2022-01-01 00:00:00.123456-0400'; - const timestampDate2 = new Date(); - const timestampDateWTZ2 = new Date('2022-01-01 00:00:00.123 +0200'); - + // 1. Insert date in string format with timezone in it await db.insert(table).values([ - { timestamp: timestampString, timestampAsDate: timestampDate, timestampTimeZones: timestampDateWTZ }, - { timestamp: timestampString2, timestampAsDate: timestampDate2, timestampTimeZones: timestampDateWTZ2 }, + { timestamp: '2022-01-01 02:00:00.123456-0200' }, ]); + // 2, Select in string format and check that values are the same const result = await db.select().from(table); + + t.deepEqual(result, [{ id: 1, timestamp: '2022-01-01 04:00:00.123456+00' }]); + + // 3. Select as raw query and check that values are the same const result2 = await db.execute<{ id: number; timestamp_string: string; - timestamp_date: string; - timestamp_date_2: string; }>(sql`select * from ${table}`); - // Whatever you put in, you get back when you're using the date mode - // But when using the string mode, postgres returns a string transformed into UTC - t.deepEqual(result, [ - { - id: 1, - timestamp: '2022-01-01 02:00:00.123456+00', - timestampAsDate: timestampDate, - timestampTimeZones: timestampDateWTZ, - }, - { - id: 2, - timestamp: '2022-01-01 04:00:00.123456+00', - timestampAsDate: timestampDate2, - timestampTimeZones: timestampDateWTZ2, - }, - ]); + t.deepEqual(result, [{ id: 1, timestamp: '2022-01-01 04:00:00.123456+00' }]); - t.deepEqual([...result2], [ - { - id: 1, - timestamp_string: '2022-01-01 02:00:00.123456+00', - timestamp_date: timestampDate.toISOString().replace('T', ' ').replace('Z', '') + '+00', - timestamp_date_2: timestampDateWTZ.toISOString().replace('T', ' ').replace('Z', '') + '+00', - }, - { - id: 2, - timestamp_string: '2022-01-01 04:00:00.123456+00', - timestamp_date: timestampDate2.toISOString().replace('T', ' ').replace('Z', '') + '+00', - timestamp_date_2: timestampDateWTZ2.toISOString().replace('T', ' ').replace('Z', '') + '+00', - }, + t.deepEqual([...result2], [{ id: 1, timestamp_string: '2022-01-01 04:00:00.123456+00' }]); + + await db.execute(sql`drop table if exists ${table}`); +}); + +test.serial('all date and time columns with timezone second case mode date', async (t) => { + const { db } = t.context; + + const table = pgTable('all_columns', { + id: serial('id').primaryKey(), + timestamp: timestamp('timestamp_string', { mode: 'date', withTimezone: true, precision: 3 }).notNull(), + }); + + await db.execute(sql`drop table if exists ${table}`); + + await db.execute(sql` + create table ${table} ( + id serial primary key, + timestamp_string timestamp(3) with time zone not null + ) + `); + + const insertedDate = new Date(); + + // 1. Insert date as new date + await db.insert(table).values([ + { timestamp: insertedDate }, ]); - t.deepEqual(result[0]?.timestampTimeZones.getTime(), new Date(result2[0]?.timestamp_date_2 as any).getTime()); + // 2, Select as date and check that timezones are the same + // There is no way to check timezone in Date object, as it is always represented internally in UTC + const result = await db.select().from(table); + + t.deepEqual(result, [{ id: 1, timestamp: insertedDate }]); + + // 3. Compare both dates + t.deepEqual(insertedDate.getTime(), result[0]?.timestamp.getTime()); await db.execute(sql`drop table if exists ${table}`); }); -test.serial('all date and time columns without timezone', async (t) => { +test.serial('all date and time columns with timezone third case mode date', async (t) => { const { db } = t.context; const table = pgTable('all_columns', { id: serial('id').primaryKey(), - timestampString: timestamp('timestamp_string', { mode: 'string', precision: 6 }).notNull(), - timestampString2: timestamp('timestamp_string2', { precision: 3, mode: 'string' }).notNull(), - timestampDate: timestamp('timestamp_date', { precision: 3 }).notNull(), + timestamp: timestamp('timestamp_string', { mode: 'date', withTimezone: true, precision: 3 }).notNull(), }); await db.execute(sql`drop table if exists ${table}`); @@ -2096,68 +2091,132 @@ test.serial('all date and time columns without timezone', async (t) => { await db.execute(sql` create table ${table} ( id serial primary key, - timestamp_string timestamp(6) not null, - timestamp_string2 timestamp(3) not null, - timestamp_date timestamp(3) not null + timestamp_string timestamp(3) with time zone not null ) `); - const timestampString = '2022-01-01 00:00:00.123456'; - const timestampString2 = '2022-01-02 00:00:00.123 -0300'; - const timestampDate = new Date('2022-01-01 00:00:00.123Z'); + const insertedDate = new Date('2022-01-01 20:00:00.123-04'); // used different time zones, internally is still UTC + const insertedDate2 = new Date('2022-01-02 04:00:00.123+04'); // They are both the same date in different time zones + + // 1. Insert date as new dates with different time zones + await db.insert(table).values([ + { timestamp: insertedDate }, + { timestamp: insertedDate2 }, + ]); + + // 2, Select and compare both dates + const result = await db.select().from(table); + + t.deepEqual(result[0]?.timestamp.getTime(), result[1]?.timestamp.getTime()); - const timestampString_2 = '2022-01-01 00:00:00.123456'; - const timestampString2_2 = '2022-01-01 00:00:00.123 -0300'; - const timestampDate2 = new Date('2022-01-01 00:00:00.123 +0200'); + await db.execute(sql`drop table if exists ${table}`); +}); +test.serial('all date and time columns without timezone first case mode string', async (t) => { + const { db } = t.context; + + const table = pgTable('all_columns', { + id: serial('id').primaryKey(), + timestamp: timestamp('timestamp_string', { mode: 'string', precision: 6 }).notNull(), + }); + + await db.execute(sql`drop table if exists ${table}`); + + await db.execute(sql` + create table ${table} ( + id serial primary key, + timestamp_string timestamp(6) not null + ) + `); + + // 1. Insert date in string format without timezone in it await db.insert(table).values([ - { timestampString, timestampString2, timestampDate }, - { timestampString: timestampString_2, timestampString2: timestampString2_2, timestampDate: timestampDate2 }, + { timestamp: '2022-01-01 02:00:00.123456' }, ]); + // 2, Select in string format and check that values are the same const result = await db.select().from(table); + + t.deepEqual(result, [{ id: 1, timestamp: '2022-01-01 02:00:00.123456' }]); + + // 3. Select as raw query and check that values are the same const result2 = await db.execute<{ id: number; timestamp_string: string; - timestamp_string2: string; - timestamp_date: string; }>(sql`select * from ${table}`); - // Whatever you put in, you get back when you're using the date mode - // But when using the string mode, postgres returns a string transformed into UTC - t.deepEqual(result, [ - { - id: 1, - timestampString: timestampString, - timestampString2: '2022-01-02 00:00:00.123', - timestampDate: timestampDate, - }, - { - id: 2, - timestampString: timestampString_2, - timestampString2: '2022-01-01 00:00:00.123', - timestampDate: timestampDate2, - }, + t.deepEqual([...result2], [{ id: 1, timestamp_string: '2022-01-01 02:00:00.123456' }]); + + await db.execute(sql`drop table if exists ${table}`); +}); + +test.serial('all date and time columns without timezone second case mode string', async (t) => { + const { db } = t.context; + + const table = pgTable('all_columns', { + id: serial('id').primaryKey(), + timestamp: timestamp('timestamp_string', { mode: 'string', precision: 6 }).notNull(), + }); + + await db.execute(sql`drop table if exists ${table}`); + + await db.execute(sql` + create table ${table} ( + id serial primary key, + timestamp_string timestamp(6) with time zone not null + ) + `); + + // 1. Insert date in string format with timezone in it + await db.insert(table).values([ + { timestamp: '2022-01-01T02:00:00.123456-02' }, ]); - t.deepEqual([...result2], [ - { - id: 1, - timestamp_string: timestampString, - timestamp_string2: '2022-01-02 00:00:00.123', - timestamp_date: timestampDate.toISOString().replace('T', ' ').replace('Z', ''), - }, - { - id: 2, - timestamp_string: timestampString_2, - timestamp_string2: '2022-01-01 00:00:00.123', - timestamp_date: timestampDate2.toISOString().replace('T', ' ').replace('Z', ''), - }, + // 2, Select as raw query and check that values are the same + const result = await db.execute<{ + id: number; + timestamp_string: string; + }>(sql`select * from ${table}`); + + // Please notice that postgres will transform the date to UTC and it saves it in UTC. + // even if set without time zone, just becuase it was provided a timezone offset, it will transform it to UTC + t.deepEqual([...result], [{ id: 1, timestamp_string: '2022-01-01 04:00:00.123456+00' }]); + + await db.execute(sql`drop table if exists ${table}`); +}); + +test.serial('all date and time columns without timezone third case mode date', async (t) => { + const { db } = t.context; + + const table = pgTable('all_columns', { + id: serial('id').primaryKey(), + timestamp: timestamp('timestamp_string', { mode: 'date', precision: 3 }).notNull(), + }); + + await db.execute(sql`drop table if exists ${table}`); + + await db.execute(sql` + create table ${table} ( + id serial primary key, + timestamp_string timestamp(3) with time zone not null + ) + `); + + const insertedDate = new Date('2022-01-01 20:00:00.123+04'); + + // 1. Insert date as new date + await db.insert(table).values([ + { timestamp: insertedDate }, ]); - t.deepEqual(result2[0]?.timestamp_string, '2022-01-01 00:00:00.123456'); - // need to add the 'Z', otherwise javascript assumes it's in local time - t.deepEqual(new Date(result2[0]?.timestamp_date + 'Z' as any).getTime(), timestampDate.getTime()); + // 2, Select as raw query as string + const result = await db.execute<{ + id: number; + timestamp_string: string; + }>(sql`select * from ${table}`); + + // 3. Compare both dates using orm mapping + t.deepEqual(new Date(result[0]!.timestamp_string).getTime(), insertedDate.getTime()); await db.execute(sql`drop table if exists ${table}`); }); From b29a5e1a837cbbb45be5e2caf3564c0493c28d08 Mon Sep 17 00:00:00 2001 From: Angelelz Date: Thu, 4 Jan 2024 18:34:19 -0500 Subject: [PATCH 10/10] Fixed test without timezones --- integration-tests/tests/pg-proxy.test.ts | 52 ++------------------- integration-tests/tests/pg.test.ts | 52 ++------------------- integration-tests/tests/postgres.js.test.ts | 52 ++------------------- 3 files changed, 15 insertions(+), 141 deletions(-) diff --git a/integration-tests/tests/pg-proxy.test.ts b/integration-tests/tests/pg-proxy.test.ts index c98c0c10b..c7e87bed7 100644 --- a/integration-tests/tests/pg-proxy.test.ts +++ b/integration-tests/tests/pg-proxy.test.ts @@ -2358,46 +2358,6 @@ test.serial('all date and time columns', async (t) => { await db.execute(sql`drop table if exists ${table}`); }); -test.serial('all date and time columns with timezone first case mode string', async (t) => { - const { db } = t.context; - - const table = pgTable('all_columns', { - id: serial('id').primaryKey(), - timestamp: timestamp('timestamp_string', { mode: 'string', withTimezone: true, precision: 6 }).notNull(), - }); - - await db.execute(sql`drop table if exists ${table}`); - - await db.execute(sql` - create table ${table} ( - id serial primary key, - timestamp_string timestamp(6) with time zone not null - ) - `); - - // 1. Insert date in string format with timezone in it - await db.insert(table).values([ - { timestamp: '2022-01-01 02:00:00.123456-0200' }, - ]); - - // 2, Select in string format and check that values are the same - const result = await db.select().from(table); - - t.deepEqual(result, [{ id: 1, timestamp: '2022-01-01 04:00:00.123456+00' }]); - - // 3. Select as raw query and check that values are the same - const result2 = await db.execute<{ - id: number; - timestamp_string: string; - }>(sql`select * from ${table}`); - - t.deepEqual(result, [{ id: 1, timestamp: '2022-01-01 04:00:00.123456+00' }]); - - t.deepEqual([...result2], [{ id: 1, timestamp_string: '2022-01-01 04:00:00.123456+00' }]); - - await db.execute(sql`drop table if exists ${table}`); -}); - test.serial('all date and time columns with timezone second case mode date', async (t) => { const { db } = t.context; @@ -2519,7 +2479,7 @@ test.serial('all date and time columns without timezone second case mode string' await db.execute(sql` create table ${table} ( id serial primary key, - timestamp_string timestamp(6) with time zone not null + timestamp_string timestamp(6) not null ) `); @@ -2534,9 +2494,7 @@ test.serial('all date and time columns without timezone second case mode string' timestamp_string: string; }>(sql`select * from ${table}`); - // Please notice that postgres will transform the date to UTC and it saves it in UTC. - // even if set without time zone, just becuase it was provided a timezone offset, it will transform it to UTC - t.deepEqual([...result], [{ id: 1, timestamp_string: '2022-01-01 04:00:00.123456+00' }]); + t.deepEqual([...result], [{ id: 1, timestamp_string: '2022-01-01 02:00:00.123456' }]); await db.execute(sql`drop table if exists ${table}`); }); @@ -2554,7 +2512,7 @@ test.serial('all date and time columns without timezone third case mode date', a await db.execute(sql` create table ${table} ( id serial primary key, - timestamp_string timestamp(3) with time zone not null + timestamp_string timestamp(3) not null ) `); @@ -2571,8 +2529,8 @@ test.serial('all date and time columns without timezone third case mode date', a timestamp_string: string; }>(sql`select * from ${table}`); - // 3. Compare both dates using orm mapping - t.deepEqual(new Date(result[0]!.timestamp_string).getTime(), insertedDate.getTime()); + // 3. Compare both dates using orm mapping - Need to add 'Z' to tell JS that it is UTC + t.deepEqual(new Date(result[0]!.timestamp_string + 'Z').getTime(), insertedDate.getTime()); await db.execute(sql`drop table if exists ${table}`); }); diff --git a/integration-tests/tests/pg.test.ts b/integration-tests/tests/pg.test.ts index 9955f98cf..3b31d7d60 100644 --- a/integration-tests/tests/pg.test.ts +++ b/integration-tests/tests/pg.test.ts @@ -2480,46 +2480,6 @@ test.serial('all date and time columns', async (t) => { await db.execute(sql`drop table if exists ${table}`); }); -test.serial('all date and time columns with timezone first case mode string', async (t) => { - const { db } = t.context; - - const table = pgTable('all_columns', { - id: serial('id').primaryKey(), - timestamp: timestamp('timestamp_string', { mode: 'string', withTimezone: true, precision: 6 }).notNull(), - }); - - await db.execute(sql`drop table if exists ${table}`); - - await db.execute(sql` - create table ${table} ( - id serial primary key, - timestamp_string timestamp(6) with time zone not null - ) - `); - - // 1. Insert date in string format with timezone in it - await db.insert(table).values([ - { timestamp: '2022-01-01 02:00:00.123456-0200' }, - ]); - - // 2, Select in string format and check that values are the same - const result = await db.select().from(table); - - t.deepEqual(result, [{ id: 1, timestamp: '2022-01-01 04:00:00.123456+00' }]); - - // 3. Select as raw query and check that values are the same - const result2 = await db.execute<{ - id: number; - timestamp_string: string; - }>(sql`select * from ${table}`); - - t.deepEqual(result, [{ id: 1, timestamp: '2022-01-01 04:00:00.123456+00' }]); - - t.deepEqual(result2.rows, [{ id: 1, timestamp_string: '2022-01-01 04:00:00.123456+00' }]); - - await db.execute(sql`drop table if exists ${table}`); -}); - test.serial('all date and time columns with timezone second case mode date', async (t) => { const { db } = t.context; @@ -2641,7 +2601,7 @@ test.serial('all date and time columns without timezone second case mode string' await db.execute(sql` create table ${table} ( id serial primary key, - timestamp_string timestamp(6) with time zone not null + timestamp_string timestamp(6) not null ) `); @@ -2656,9 +2616,7 @@ test.serial('all date and time columns without timezone second case mode string' timestamp_string: string; }>(sql`select * from ${table}`); - // Please notice that postgres will transform the date to UTC and it saves it in UTC. - // even if set without time zone, just becuase it was provided a timezone offset, it will transform it to UTC - t.deepEqual(result.rows, [{ id: 1, timestamp_string: '2022-01-01 04:00:00.123456+00' }]); + t.deepEqual(result.rows, [{ id: 1, timestamp_string: '2022-01-01 02:00:00.123456' }]); await db.execute(sql`drop table if exists ${table}`); }); @@ -2676,7 +2634,7 @@ test.serial('all date and time columns without timezone third case mode date', a await db.execute(sql` create table ${table} ( id serial primary key, - timestamp_string timestamp(3) with time zone not null + timestamp_string timestamp(3) not null ) `); @@ -2693,8 +2651,8 @@ test.serial('all date and time columns without timezone third case mode date', a timestamp_string: string; }>(sql`select * from ${table}`); - // 3. Compare both dates using orm mapping - t.deepEqual(new Date(result.rows[0]!.timestamp_string).getTime(), insertedDate.getTime()); + // 3. Compare both dates using orm mapping - Need to add 'Z' to tell JS that it is UTC + t.deepEqual(new Date(result.rows[0]!.timestamp_string + 'Z').getTime(), insertedDate.getTime()); await db.execute(sql`drop table if exists ${table}`); }); diff --git a/integration-tests/tests/postgres.js.test.ts b/integration-tests/tests/postgres.js.test.ts index d079fa3c9..0fd0c45ea 100644 --- a/integration-tests/tests/postgres.js.test.ts +++ b/integration-tests/tests/postgres.js.test.ts @@ -2002,46 +2002,6 @@ test.serial('all date and time columns', async (t) => { await db.execute(sql`drop table if exists ${table}`); }); -test.serial('all date and time columns with timezone first case mode string', async (t) => { - const { db } = t.context; - - const table = pgTable('all_columns', { - id: serial('id').primaryKey(), - timestamp: timestamp('timestamp_string', { mode: 'string', withTimezone: true, precision: 6 }).notNull(), - }); - - await db.execute(sql`drop table if exists ${table}`); - - await db.execute(sql` - create table ${table} ( - id serial primary key, - timestamp_string timestamp(6) with time zone not null - ) - `); - - // 1. Insert date in string format with timezone in it - await db.insert(table).values([ - { timestamp: '2022-01-01 02:00:00.123456-0200' }, - ]); - - // 2, Select in string format and check that values are the same - const result = await db.select().from(table); - - t.deepEqual(result, [{ id: 1, timestamp: '2022-01-01 04:00:00.123456+00' }]); - - // 3. Select as raw query and check that values are the same - const result2 = await db.execute<{ - id: number; - timestamp_string: string; - }>(sql`select * from ${table}`); - - t.deepEqual(result, [{ id: 1, timestamp: '2022-01-01 04:00:00.123456+00' }]); - - t.deepEqual([...result2], [{ id: 1, timestamp_string: '2022-01-01 04:00:00.123456+00' }]); - - await db.execute(sql`drop table if exists ${table}`); -}); - test.serial('all date and time columns with timezone second case mode date', async (t) => { const { db } = t.context; @@ -2163,7 +2123,7 @@ test.serial('all date and time columns without timezone second case mode string' await db.execute(sql` create table ${table} ( id serial primary key, - timestamp_string timestamp(6) with time zone not null + timestamp_string timestamp(6) not null ) `); @@ -2178,9 +2138,7 @@ test.serial('all date and time columns without timezone second case mode string' timestamp_string: string; }>(sql`select * from ${table}`); - // Please notice that postgres will transform the date to UTC and it saves it in UTC. - // even if set without time zone, just becuase it was provided a timezone offset, it will transform it to UTC - t.deepEqual([...result], [{ id: 1, timestamp_string: '2022-01-01 04:00:00.123456+00' }]); + t.deepEqual([...result], [{ id: 1, timestamp_string: '2022-01-01 02:00:00.123456' }]); await db.execute(sql`drop table if exists ${table}`); }); @@ -2198,7 +2156,7 @@ test.serial('all date and time columns without timezone third case mode date', a await db.execute(sql` create table ${table} ( id serial primary key, - timestamp_string timestamp(3) with time zone not null + timestamp_string timestamp(3) not null ) `); @@ -2215,8 +2173,8 @@ test.serial('all date and time columns without timezone third case mode date', a timestamp_string: string; }>(sql`select * from ${table}`); - // 3. Compare both dates using orm mapping - t.deepEqual(new Date(result[0]!.timestamp_string).getTime(), insertedDate.getTime()); + // 3. Compare both dates using orm mapping - Need to add 'Z' to tell JS that it is UTC + t.deepEqual(new Date(result[0]!.timestamp_string + 'Z').getTime(), insertedDate.getTime()); await db.execute(sql`drop table if exists ${table}`); });