Joseph_Ebuka
Joseph_Ebuka•13mo ago

How do i get a type of date

I need to get a date type with convex values and it seems it isn't available
13 Replies
erquhart
erquhart•13mo ago
There isn't a date type, you'll have to store dates as strings or numbers and parse them inside your functions.
ian
ian•13mo ago
number is how we store _creationTime, which works well with Date.now() and Date(number)
Joseph_Ebuka
Joseph_EbukaOP•13mo ago
Okay thanks But u guys should consider adding it it would be really helpful
ian
ian•13mo ago
Noted, thanks! For your application, what kind of Date would you want: - Always requires a timezone - Allows specifying a non-timezone date (that defaults to the timezone of the environment when comparing against Date.now()) - Allows for a date without a time - Is always in UTC One reason we've hesitated to introduce Date is due to the shortcomings of the JavaScript Date implementation that has inconsistent behavior in different browsers & situations. The most unambiguous way to represent dates ends up looking like UTC time, which encodes & decodes neatly as a number. The better we understand what features are missing about this approach the better we can design for it. For instance, having a string or number turn into a Date object once it hits the server could be a good usecase for a zod argument validator - I'm publishing a post on using zod validators in the next day or to make that easy.
ian
ian•13mo ago
Using Zod with TypeScript for Server-side Validation and End-to-End...
Use Zod with TypeScript for argument validation on your server functions allows you to both protect against invalid data, and define TypeScript types ...
GitHub
convex-helpers/convex/zodExample.ts at main · get-convex/convex-hel...
A collection of useful code to complement the official packages. - get-convex/convex-helpers
Joseph_Ebuka
Joseph_EbukaOP•13mo ago
Thanks @ian but I think the kind of date I would prefer would be one that has time zones
erquhart
erquhart•13mo ago
There's something to be said for storing all dates in a timezone - typically UTC - and then applying timezones on read or in the UI based on the request locale.
ian
ian•13mo ago
Also - I like to specify the timezone as the "america/los_angeles" type instead of an hour offset so date libraries can adjust for DST - e.g. storing a string like "2023-12-15T20:19:30+8" stores the offset of 8, but at another time of year here it's +7 so I typically store this in the user table separately
abdullahislam_
abdullahislam_•11mo ago
Following up on this - I have dates stored in the DB as strings e.g. "01/15/2024". I'm trying to use the q.gte("date_start", startdate) for example but seem to be missing the parsing. I can parse the date type of startdate passed in, but does it work with gte?
erquhart
erquhart•11mo ago
@abdullahislam_ you can only index against the data as it's stored, so to sort on a date you'll want to store it in a sort-friendly format - an ISO string is the typical approach (2024-01-15) for dates without times. ISO strings work great with sorting and can be compared with greater-than and less-than operators both in your JS and in convex indexes.
abdullahislam_
abdullahislam_•11mo ago
Thank you. I'll try this. I've already uploaded all my data that have dates stored with slashes e.g. "01/01/2024" - is there a function or something I can write to loop through and edit those dates or should I just re-upload?
erquhart
erquhart•11mo ago
I would write a mutation that gets the collection, rewrites the dates, and patches in the new values. If the collection is more than 1000 or so you'll want to paginate. This should be pretty close, uses date-fns for date parsing and formatting:
import { parse as parseDate, formatISO } from 'date-fns'

const formatIsoDates = internalMutation({
args: {
paginationOpts: v.optional(paginationOptsValidator),
},
handler: async (ctx, args) => {
const NUM_ITEMS = 500
const result = await ctx.db
.query('my_table')
.paginate(args.paginationOpts || { cursor: null, numItems: NUM_ITEMS })

await Promise.all(result.page.map(async (doc) => {
const date = parseDate(doc.date_start, 'MM/dd/yyyy', new Date())
const isoDate = formatISO(date, { representation: 'date' })
await ctx.db.patch(doc.id, { date_start: isoDate })
}))

if (result.isDone) {
return
}

ctx.scheduler.runAfter(0, internal.<fileName>.formatIsoDates, {
paginationOpts: {
cursor: result.continueCursor,
numItems: NUM_ITEMS,
},
})
}
})
import { parse as parseDate, formatISO } from 'date-fns'

const formatIsoDates = internalMutation({
args: {
paginationOpts: v.optional(paginationOptsValidator),
},
handler: async (ctx, args) => {
const NUM_ITEMS = 500
const result = await ctx.db
.query('my_table')
.paginate(args.paginationOpts || { cursor: null, numItems: NUM_ITEMS })

await Promise.all(result.page.map(async (doc) => {
const date = parseDate(doc.date_start, 'MM/dd/yyyy', new Date())
const isoDate = formatISO(date, { representation: 'date' })
await ctx.db.patch(doc.id, { date_start: isoDate })
}))

if (result.isDone) {
return
}

ctx.scheduler.runAfter(0, internal.<fileName>.formatIsoDates, {
paginationOpts: {
cursor: result.continueCursor,
numItems: NUM_ITEMS,
},
})
}
})
Update "my_table" and "<fileName>" with real values and call it from the dashboard with no args.
abdullahislam_
abdullahislam_•11mo ago
This is brilliant. Really appreciate this 👌

Did you find this page helpful?