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