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:
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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()))
|
||||||
|
|||||||
Reference in New Issue
Block a user