Repository: prisma/prisma (TypeScript)

Issue:
`driverAdapters` wrongly converts 4-digit years < 0100 to 2 digits when using Postgres
### Bug description

Prisma's behavior with and without the `driverAdapters` feature enabled differs when trying to set values for dates before the year 0100:
- With `driverAdapters` disabled, it correctly inserts the date
- With `driverAdapters` **enabled**, it seems to chop off the 2 leading zeros and tries to insert a date with a 2-digit year, which then fails

While I've personally only tested it with Postgres 16, based on the error I'm getting, I would assume that it at least fails for other Postgres versions as well, but I'm not sure about other DBMS.

### Severity

🚨 Critical: Data loss, app crash, security issue

### Reproduction

<!-- Link to CodeSandbox, GitHub repo, or step-by-step instructions -->

Repo is here: https://github.com/clemens/prisma-bug-repro

Step by step below.

---

Create a simple model with a single date field, e.g.

``` prisma
model Person {
  id                  String       @id @default(dbgenerated("gen_random_uuid()"))
  dateOfBirth DateTime @db.Date
}
```

Then create a record with a date before 0100, e.g.

``` ts
await prisma.person.create({ dateOfBirth: "0099-12-31" })
```

=> Without `driverAdapters` feature, this passes.

---

With `driverAdapters`:

Enable `driverAdapters` preview feature in `schema.prisma` and potentially `prisma.config.ts`:

``` prisma
generator client {
  // …
  previewFeatures = ["driverAdapters"]
}
```

=> With the feature enabled this fails with a Postgres error of `date/time field value out of range: "0-01-01"`

### Expected vs. Actual Behavior

With `driverAdapter` and `queryCompiler`:

``` ts
await prisma.person.create({ data: { dateOfBirth: "0000-01-01" } }) // DriverAdapterError: date/time field value out of range: "0-01-01"
await prisma.person.create({ data: { dateOfBirth: "0099-01-01" } }) // DriverAdapterError: date/time field value out of range: "99-01-01"
await prisma.person.create({ data: { dateOfBirth: "0100-01-01" } }) // works
```

With just `driverAdapter`:

``` ts
await prisma.person.create({ data: { dateOfBirth: "0000-01-01" } }) // ConnectorError(ConnectorError { user_facing_error: None, kind: QueryError(PostgresError { code: "22008", message: "date/time field value out of range: \"00-01-01\"", severity: "ERROR", detail: None, column: None, hint: Some("Perhaps you need a different \"datestyle\" setting.") }), transient: false })
await prisma.person.create({ data: { dateOfBirth: "0099-01-01" } }) // ConnectorError(ConnectorError { user_facing_error: None, kind: QueryError(PostgresError { code: "22008", message: "date/time field value out of range: \"99-01-01\"", severity: "ERROR", detail: None, column: None, hint: Some("Perhaps you need a different \"datestyle\" setting.") }), transient: false })
await prisma.person.create({ data: { dateOfBirth: "0100-01-01" } }) // works
```

Without either:

``` ts
await prisma.person.create({ data: { dateOfBirth: "0000-01-01" } }) // works
await prisma.person.create({ data: { dateOfBirth: "0099-01-01" } }) // works
await prisma.person.create({ data: { dateOfBirth: "0100-01-01" } }) // works
```

### Frequency

Consistently reproducible

### Does this occur in development or production?

Both development and production

### Is this a regression?

<!-- e.g., "Yes, last worked in Prisma 5.1.0, broke in 5.2.0" -->
In a way "yes", because driver adapters is a new feature that's, from what I understand, intended to replace the current (non-driver adapters) approach, so if this bug stayed in place, it would break as soon as this new approach becomes mandatory.

### Workaround

<!-- e.g., "Downgrading to Prisma 5.1.0 fixes it" or "No workaround found" -->
None, other than disabling the `driverAdapters` preview feature.

### Prisma Schema & Queries

Already posted above.

### Prisma Config

_No response_

### Logs & Debug Info

```
prisma:client checkPlatformCaching:postinstall false +2ms
prisma:client checkPlatformCaching:ciName undefined +0ms
prisma:client dirname /Users/clemens/Code/prisma-repro/generated/client +1ms
prisma:client relativePath ../.. +0ms
prisma:client cwd /Users/clemens/Code/prisma-repro +0ms
prisma:client clientVersion 6.15.0 +0ms
prisma:client:libraryEngine internalSetup +0ms
prisma:client Prisma Client call: +2ms
prisma:client prisma.person.create({
  data: {
    dateOfBirth: new Date("0000-01-01T00:00:00.000Z")
  }
}) +0ms
prisma:client Generated request: +0ms
prisma:client {
  "modelName": "Person",
  "action": "createOne",
  "query": {
    "arguments": {
      "data": {
        "dateOfBirth": {
          "$type": "DateTime",
          "value": "0000-01-01T00:00:00.000Z"
        }
      }
    },
    "selection": {
      "$composites": true,
      "$scalars": true
    }
  }
}
 +0ms
prisma:client:libraryEngine sending request, this.libraryStarted: false +0ms
prisma:client:engines:resolveEnginePath enginePath /Users/clemens/Code/prisma-repro/generated/client/libquery_engine-darwin-arm64.dylib.node +1ms
prisma:client:libraryEngine Using driver adapter: '{\n' +
  '  "adapterName": "@prisma/adapter-pg",\n' +
  '  "errorRegistry": {\n' +
  '    "registeredErrors": []\n' +
  '  },\n' +
  '  "provider": "postgres"\n' +
  '}' +8ms
prisma:client:libraryEngine library starting +9ms
prisma:client:libraryEngine library started +3ms
prisma:driver-adapter:pg [js::query_raw] '{\n' +
  '  "sql": "INSERT INTO \\"public\\".\\"Person\\" (\\"dateOfBirth\\") VALUES ($1) RETURNING \\"public\\".\\"Person\\".\\"id\\", \\"public\\".\\"Person\\".\\"dateOfBirth\\"",\n' +
  '  "args": [\n' +
  '    "0000-01-01T00:00:00+00:00"\n' +
  '  ],\n' +
  '  "argTypes": [\n' +
  '    {\n' +
  '      "scalarType": "datetime",\n' +
  '      "dbType": "DATE",\n' +
  '      "arity": "scalar"\n' +
  '    }\n' +
  '  ]\n' +
  '}' +58ms
prisma:driver-adapter:pg Error in performIO: '{\n' +
  '  "length": 199,\n' +
  '  "name": "error",\n' +
  '  "severity": "ERROR",\n' +
  '  "code": "22008",\n' +
  '  "hint": "Perhaps you need a different \\"datestyle\\" setting.",\n' +
  `  "where": "unnamed portal parameter $1 = '...'",\n` +
  '  "file": "datetime.c",\n' +
  '  "line": "3992",\n' +
  '  "routine": "DateTimeParseError"\n' +
  '}' +20ms
driver-adapter-utils [error@wrapAsync] {
  "name": "DriverAdapterError",
  "cause": {
    "originalCode": "22008",
    "originalMessage": "date/time field value out of range: \"0-01-01\"",
    "kind": "postgres",
    "code": "22008",
    "severity": "ERROR",
    "message": "date/time field value out of range: \"0-01-01\"",
    "hint": "Perhaps you need a different \"datestyle\" setting."
  }
} +27ms
prisma:client:request_handler {
  "clientVersion": "6.15.0",
  "name": "PrismaClientUnknownRequestError"
} +1ms
```


### Environment & Setup

- **OS:** macOS Sequoia 15.6.1
- **Database:** Postgres 16/10
- **Node.js version:** v20.17.0, v22.18.0


### Prisma Version

From the actual app where I'm experiencing the issue:

```
[dotenv@17.2.1] injecting env (2) from prisma/.env -- tip: ⚙️  enable debug logging with { debug: true }
Loaded Prisma config from prisma.config.ts.

Prisma config detected, skipping environment variable loading.
Prisma schema loaded from prisma
prisma                : 6.15.0
@prisma/client        : 6.15.0
Computed binaryTarget : darwin-arm64
Operating System      : darwin
Architecture          : arm64
Node.js               : v22.18.0
TypeScript            : 5.9.2
Query Compiler        : enabled
PSL                   : @prisma/prisma-schema-wasm 6.15.0-5.85179d7826409ee107a6ba334b5e305ae3fba9fb
Schema Engine         : schema-engine-cli 85179d7826409ee107a6ba334b5e305ae3fba9fb (at ../../node_modules/@prisma/engines/schema-engine-darwin-arm64)
Default Engines Hash  : 85179d7826409ee107a6ba334b5e305ae3fba9fb
Studio                : 0.511.0
Preview Features      : driverAdapters, fullTextSearchPostgres, nativeDistinct, queryCompiler, relationJoins, views
```

From the [test app](https://github.com/clemens/prisma-bug-repro):

```
prisma                  : 6.15.0
@prisma/client          : 6.15.0
Computed binaryTarget   : darwin-arm64
Operating System        : darwin
Architecture            : arm64
Node.js                 : v20.17.0
TypeScript              : unknown
Query Engine (Node-API) : libquery-engine 85179d7826409ee107a6ba334b5e305ae3fba9fb (at node_modules/@prisma/engines/libquery_engine-darwin-arm64.dylib.node)
PSL                     : @prisma/prisma-schema-wasm 6.15.0-5.85179d7826409ee107a6ba334b5e305ae3fba9fb
Schema Engine           : schema-engine-cli 85179d7826409ee107a6ba334b5e305ae3fba9fb (at node_modules/@prisma/engines/schema-engine-darwin-arm64)
Default Engines Hash    : 85179d7826409ee107a6ba334b5e305ae3fba9fb
Studio                  : 0.511.0
Preview Features        : driverAdapters
```


Discussion:

Patch:
diff --git a/packages/adapter-pg/src/conversion.ts b/packages/adapter-pg/src/conversion.ts
index 3bee903f90ef..7c9a69d704e6 100644
--- a/packages/adapter-pg/src/conversion.ts
+++ b/packages/adapter-pg/src/conversion.ts
@@ -466,7 +466,7 @@ function formatDateTime(date: Date): string {
   const pad = (n: number, z = 2) => String(n).padStart(z, '0')
   const ms = date.getUTCMilliseconds()
   return (
-    date.getUTCFullYear() +
+    pad(date.getUTCFullYear(), 4) +
     '-' +
     pad(date.getUTCMonth() + 1) +
     '-' +
@@ -483,7 +483,7 @@ function formatDateTime(date: Date): string {
 
 function formatDate(date: Date): string {
   const pad = (n: number, z = 2) => String(n).padStart(z, '0')
-  return date.getUTCFullYear() + '-' + pad(date.getUTCMonth() + 1) + '-' + pad(date.getUTCDate())
+  return pad(date.getUTCFullYear(), 4) + '-' + pad(date.getUTCMonth() + 1) + '-' + pad(date.getUTCDate())
 }
 
 function formatTime(date: Date): string {


Test patch:
diff --git a/packages/adapter-pg/src/__tests__/conversion.test.ts b/packages/adapter-pg/src/__tests__/conversion.test.ts
new file mode 100644
index 000000000000..e438c2e405df
--- /dev/null
+++ b/packages/adapter-pg/src/__tests__/conversion.test.ts
@@ -0,0 +1,41 @@
+import { describe, expect, it } from 'vitest'
+
+import { mapArg } from '../conversion'
+
+describe('mapArg', () => {
+  it('converts a date with a 4-digit year (value >= 1000-01-01) to the correct date', () => {
+    const date = new Date('1999-12-31T23:59:59.999Z')
+    const result = mapArg(date, { dbType: 'DATE', scalarType: 'datetime', arity: 'scalar' })
+    expect(result).toBe('1999-12-31')
+  })
+
+  it('converts a date with a 3-digit year (0100-01-01 <= value < 1000-01-01) to the correct date', () => {
+    const date = new Date('0999-12-31T23:59:59.999Z')
+    const result = mapArg(date, { dbType: 'DATE', scalarType: 'datetime', arity: 'scalar' })
+    expect(result).toBe('0999-12-31')
+  })
+
+  it('converts a date with a 2-digit year (0000-01-01 <= value < 0100-01-01) to the correct date', () => {
+    const date = new Date('0099-12-31T23:59:59.999Z')
+    const result = mapArg(date, { dbType: 'DATE', scalarType: 'datetime', arity: 'scalar' })
+    expect(result).toBe('0099-12-31')
+  })
+
+  it('converts a date with a 4-digit year (value >= 1000-01-01) to the correct datetime', () => {
+    const date = new Date('1999-12-31T23:59:59.999Z')
+    const result = mapArg(date, { dbType: 'DATETIME', scalarType: 'datetime', arity: 'scalar' })
+    expect(result).toBe('1999-12-31 23:59:59.999')
+  })
+
+  it('converts a date with a 3-digit year (0100-01-01 <= value < 1000-01-01) to the correct datetime', () => {
+    const date = new Date('0999-12-31T23:59:59.999Z')
+    const result = mapArg(date, { dbType: 'DATETIME', scalarType: 'datetime', arity: 'scalar' })
+    expect(result).toBe('0999-12-31 23:59:59.999')
+  })
+
+  it('converts a date with a 2-digit year (0000-01-01 <= value < 0100-01-01) to the correct datetime', () => {
+    const date = new Date('0099-12-31T23:59:59.999Z')
+    const result = mapArg(date, { dbType: 'DATETIME', scalarType: 'datetime', arity: 'scalar' })
+    expect(result).toBe('0099-12-31 23:59:59.999')
+  })
+})