From 917c66fe440c96f37c46061e57e4e5ec279cb762 Mon Sep 17 00:00:00 2001 From: reiv Date: Tue, 2 Sep 2025 12:35:02 +0200 Subject: [PATCH] fix(db-sqlite): convert Date to ISO 8601 string in queries (#11694) ### What? Restores the ability to query by `Date(...)` when using the `sqlite` adapter. ### Why? Queries involving JavaScript `Date`s return incorrect results because they are not converted to ISO 8601 strings by the `sqlite` adapter. ### How? Wraps comparison operators to convert `Date` parameters to ISO 8601 strings. Fixes #11692 --------- Co-authored-by: German Jablonski <43938777+GermanJablo@users.noreply.github.com> --- .../drizzle/src/queries/sanitizeQueryValue.ts | 37 +++++++--- test/fields/int.spec.ts | 73 ++++++++++++++++++- 2 files changed, 97 insertions(+), 13 deletions(-) diff --git a/packages/drizzle/src/queries/sanitizeQueryValue.ts b/packages/drizzle/src/queries/sanitizeQueryValue.ts index 72b1d5cbac..e74da29c52 100644 --- a/packages/drizzle/src/queries/sanitizeQueryValue.ts +++ b/packages/drizzle/src/queries/sanitizeQueryValue.ts @@ -110,19 +110,32 @@ export const sanitizeQueryValue = ({ } } - if (field.type === 'date' && operator !== 'exists') { - if (typeof val === 'string') { - if (val === 'null' || val === '') { - formattedValue = null - } else { - const date = new Date(val) - if (Number.isNaN(date.getTime())) { - return { operator, value: undefined } - } - formattedValue = date.toISOString() + // Helper function to convert a single date value to ISO string + const convertDateToISO = (item: unknown): unknown => { + if (typeof item === 'string') { + if (item === 'null' || item === '') { + return null } - } else if (typeof val === 'number') { - formattedValue = new Date(val).toISOString() + const date = new Date(item) + return Number.isNaN(date.getTime()) ? undefined : date.toISOString() + } else if (typeof item === 'number') { + return new Date(item).toISOString() + } else if (item instanceof Date) { + return item.toISOString() + } + return item + } + + if (field.type === 'date' && operator !== 'exists') { + if (Array.isArray(formattedValue)) { + // Handle arrays of dates for 'in' and 'not_in' operators + formattedValue = formattedValue.map(convertDateToISO).filter((item) => item !== undefined) + } else { + const converted = convertDateToISO(val) + if (converted === undefined) { + return { operator, value: undefined } + } + formattedValue = converted } } diff --git a/test/fields/int.spec.ts b/test/fields/int.spec.ts index 1822c524a7..738a6950de 100644 --- a/test/fields/int.spec.ts +++ b/test/fields/int.spec.ts @@ -590,6 +590,7 @@ describe('Fields', () => { describe('timestamps', () => { const tenMinutesAgo = new Date(Date.now() - 1000 * 60 * 10) + const tenMinutesLater = new Date(Date.now() + 1000 * 60 * 10) let doc beforeEach(async () => { doc = await payload.create({ @@ -612,7 +613,7 @@ describe('Fields', () => { expect(docs.map(({ id }) => id)).toContain(doc.id) }) - it('should query createdAt', async () => { + it('should query createdAt (greater_than_equal with results)', async () => { const result = await payload.find({ collection: 'date-fields', depth: 0, @@ -626,6 +627,76 @@ describe('Fields', () => { expect(result.docs[0].id).toEqual(doc.id) }) + it('should query createdAt (greater_than_equal with no results)', async () => { + const result = await payload.find({ + collection: 'date-fields', + depth: 0, + where: { + createdAt: { + greater_than_equal: tenMinutesLater, + }, + }, + }) + + expect(result.totalDocs).toBe(0) + }) + + it('should query createdAt (less_than with results)', async () => { + const result = await payload.find({ + collection: 'date-fields', + depth: 0, + where: { + createdAt: { + less_than: tenMinutesLater, + }, + }, + }) + + expect(result.docs[0].id).toEqual(doc.id) + }) + + it('should query createdAt (less_than with no results)', async () => { + const result = await payload.find({ + collection: 'date-fields', + depth: 0, + where: { + createdAt: { + less_than: tenMinutesAgo, + }, + }, + }) + + expect(result.totalDocs).toBe(0) + }) + + it('should query createdAt (in with results)', async () => { + const result = await payload.find({ + collection: 'date-fields', + depth: 0, + where: { + createdAt: { + in: [new Date(doc.createdAt)], + }, + }, + }) + + expect(result.docs[0].id).toBe(doc.id) + }) + + it('should query createdAt (in without results)', async () => { + const result = await payload.find({ + collection: 'date-fields', + depth: 0, + where: { + createdAt: { + in: [tenMinutesAgo], + }, + }, + }) + + expect(result.totalDocs).toBe(0) + }) + // Function to generate random date between start and end dates function getRandomDate(start: Date, end: Date): string { const date = new Date(start.getTime() + Math.random() * (end.getTime() - start.getTime()))