Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Pg] Fix all datetime mappings #1657

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions drizzle-orm/src/pg-core/columns/timestamp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,16 @@ export class PgTimestamp<T extends ColumnBaseConfig<'date', '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();
};
}

Expand Down Expand Up @@ -121,6 +125,14 @@ export class PgTimestampString<T extends ColumnBaseConfig<'string', 'PgTimestamp
const precision = this.precision === undefined ? '' : `(${this.precision})`;
return `timestamp${precision}${this.withTimezone ? ' with time zone' : ''}`;
}

override mapFromDriverValue = (value: string | Date | null): string | null => {
// 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;
Expand Down
8 changes: 8 additions & 0 deletions drizzle-orm/src/postgres-js/driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ export function drizzle<TSchema extends Record<string, unknown> = Record<string,
client: Sql,
config: DrizzleConfig<TSchema> = {},
): PostgresJsDatabase<TSchema> {
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) {
Expand Down
2 changes: 1 addition & 1 deletion drizzle-typebox/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
".": {
Expand Down
2 changes: 1 addition & 1 deletion drizzle-valibot/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
".": {
Expand Down
2 changes: 1 addition & 1 deletion drizzle-zod/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
".": {
Expand Down
2 changes: 1 addition & 1 deletion integration-tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
},
Expand Down
110 changes: 109 additions & 1 deletion integration-tests/tests/awsdatapi.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down Expand Up @@ -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"`);
Expand Down
96 changes: 96 additions & 0 deletions integration-tests/tests/neon-http.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,12 @@ import {
boolean,
char,
cidr,
date,
getMaterializedViewConfig,
getViewConfig,
inet,
integer,
interval,
jsonb,
macaddr,
macaddr8,
Expand All @@ -44,6 +46,7 @@ import {
pgView,
serial,
text,
time,
timestamp,
uuid as pgUuid,
varchar,
Expand Down Expand Up @@ -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;

Expand Down
Loading