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>
This commit is contained in:
reiv
2025-09-02 12:35:02 +02:00
committed by GitHub
parent ac691b675b
commit 917c66fe44
2 changed files with 97 additions and 13 deletions

View File

@@ -110,19 +110,32 @@ export const sanitizeQueryValue = ({
} }
} }
if (field.type === 'date' && operator !== 'exists') { // Helper function to convert a single date value to ISO string
if (typeof val === 'string') { const convertDateToISO = (item: unknown): unknown => {
if (val === 'null' || val === '') { if (typeof item === 'string') {
formattedValue = null if (item === 'null' || item === '') {
} else { return null
const date = new Date(val)
if (Number.isNaN(date.getTime())) {
return { operator, value: undefined }
}
formattedValue = date.toISOString()
} }
} else if (typeof val === 'number') { const date = new Date(item)
formattedValue = new Date(val).toISOString() 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
} }
} }

View File

@@ -590,6 +590,7 @@ describe('Fields', () => {
describe('timestamps', () => { describe('timestamps', () => {
const tenMinutesAgo = new Date(Date.now() - 1000 * 60 * 10) const tenMinutesAgo = new Date(Date.now() - 1000 * 60 * 10)
const tenMinutesLater = new Date(Date.now() + 1000 * 60 * 10)
let doc let doc
beforeEach(async () => { beforeEach(async () => {
doc = await payload.create({ doc = await payload.create({
@@ -612,7 +613,7 @@ describe('Fields', () => {
expect(docs.map(({ id }) => id)).toContain(doc.id) 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({ const result = await payload.find({
collection: 'date-fields', collection: 'date-fields',
depth: 0, depth: 0,
@@ -626,6 +627,76 @@ describe('Fields', () => {
expect(result.docs[0].id).toEqual(doc.id) 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 to generate random date between start and end dates
function getRandomDate(start: Date, end: Date): string { function getRandomDate(start: Date, end: Date): string {
const date = new Date(start.getTime() + Math.random() * (end.getTime() - start.getTime())) const date = new Date(start.getTime() + Math.random() * (end.getTime() - start.getTime()))