Standardize date/time handling on ISO8601 - #230
Conversation
There was a problem hiding this comment.
Pull request overview
This PR standardizes date/time handling across the monorepo by introducing shared ISO8601 helpers in @databank/core, updating API emissions/exports to use ISO8601 datetimes, and replacing locale-dependent UI rendering with deterministic ISO date-only display.
Changes:
- Added Luxon-backed
formatISODate,formatISODateTime, andparseISODatehelpers in@databank/coreand exported them for cross-package use. - Updated
web/routes anduseProjectQueryto render/parse dates using the shared ISO helpers instead oftoLocaleDateString()andz.coerce.date(). - Updated API services to emit ISO8601 datetimes and adjusted Prisma schemas to store full
DateTime(removing@db.Datetruncation), plus a small Date construction cleanup and a namedMS_PER_DAYconstant.
Reviewed changes
Copilot reviewed 21 out of 22 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| web/src/routes/portal/projects/index.tsx | Replace locale-dependent expiry display with formatISODate. |
| web/src/routes/portal/projects/$projectId/index.tsx | Standardize project/dataset created/updated/expiry display to ISO date-only. |
| web/src/routes/portal/projects/$projectId/datasets.$datasetId/index.tsx | Standardize dataset created/updated display to ISO date-only. |
| web/src/routes/portal/projects/$projectId/add-dataset.tsx | Standardize dataset created display to ISO date-only. |
| web/src/routes/portal/datasets/index.tsx | Standardize dataset created display to ISO date-only. |
| web/src/routes/portal/datasets/$datasetId/index.tsx | Standardize dataset created/updated display to ISO date-only. |
| web/src/routes/_public/datasets/index.tsx | Standardize public dataset created display to ISO date-only. |
| web/src/routes/_public/datasets/$datasetId.tsx | Standardize public dataset created/updated display to ISO date-only. |
| web/src/hooks/queries/useProjectQuery.ts | Tighten project date parsing to ISO-only inputs via parseISODate. |
| pnpm-workspace.yaml | Add Luxon and Luxon types to the workspace catalog. |
| pnpm-lock.yaml | Lockfile updates for Luxon and @types/luxon. |
| core/src/users.ts | Align User date fields’ TypeScript types with Date semantics. |
| core/src/index.ts | Re-export new dates helpers from the core package entrypoint. |
| core/src/dates.ts | New shared ISO8601 formatting/parsing helpers backed by Luxon. |
| core/package.json | Add Luxon dependency and @types/luxon devDependency. |
| api/src/tabular-data/tabular-data.service.ts | Emit ISO8601 datetimes for dataset-row DATETIME values. |
| api/src/datasets/datasets.service.ts | Emit ISO8601 datetimes in exported CSV metadata (datetime min/max). |
| api/src/columns/columns.service.ts | Replace magic milliseconds-per-day constant with MS_PER_DAY. |
| api/src/auth/auth.service.ts | Simplify new Date(Date.now()) to new Date(). |
| api/prisma/schema/setup.prisma | Store full DateTime (remove @db.Date) for setup timestamps. |
| api/prisma/schema/projects.prisma | Store full DateTime (remove @db.Date) for project timestamps. |
| api/prisma/schema/_main.prisma | Store full DateTime (remove @db.Date) for user/dataset timestamps. |
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| export const formatISODate = (date: Date): string => { | ||
| return DateTime.fromJSDate(date, { zone: 'utc' }).toISODate()!; | ||
| }; |
There was a problem hiding this comment.
Valid point. Fixed in 8047fde: now validates DateTime.isValid and throws TypeError before formatting, mirroring the existing parseISODate pattern. The non-null assertion is now unreachable on the invalid path.
| export const formatISODateTime = (date: Date): string => { | ||
| return DateTime.fromJSDate(date, { zone: 'utc' }).toISO()!; | ||
| }; |
There was a problem hiding this comment.
Valid point. Fixed in 8047fde: now validates DateTime.isValid and throws TypeError before formatting, mirroring the existing parseISODate pattern. The non-null assertion is now unreachable on the invalid path.
Summary
Standardize all date/time handling on ISO8601 across the codebase. Adds Luxon as the date library, introduces shared helpers in
@databank/core, and fixes every non-compliant emission/display site found in a full audit.Audit findings (now fixed)
toDateString()("Wed Jul 14 2026") in dataset row payloads and CSV metadata exports.toLocaleDateString()with no locale arg inweb/— locale-indeterminate display.@db.DateoncreatedAt/updatedAttruncated the time portion.z.coerce.date()accepted non-ISO strings.Usertype declared dates asnumber(unix ms).24 * 3600 * 1000) in polars day-since-epoch conversion.Changes by commit
add luxon and ISO8601 date helpers in core— newcore/src/dates.tsexportingformatISODate,formatISODateTime,parseISODate(all UTC-forced).emit ISO8601 datetimes in dataset rows and CSV metadata—toDateString()→formatISODateTime()intabular-data.service.tsanddatasets.service.ts.render ISO8601 dates in web UI via core helper— 14toLocaleDateString()sites →formatISODate()(ISO date-only display).migrate Prisma createdAt/updatedAt to full DateTime— drop@db.DatefromUser,Dataset,Project,SetupConfig.align User type and project query parsing with ISO8601—Userdate fieldsnumber→Date | null | undefined;z.coerce.date()→z.union([z.iso.datetime(), z.iso.date()]).transform(parseISODate).replace date magic constants and simplify new Date calls—MS_PER_DAYnamed constant with explanatory comment;new Date(Date.now())→new Date().Notes
2026-07-14) uniformly acrossen/frlocales. This is an intentional decision from the audit — flag if localized display is preferred instead.prisma db push(or app sync) so MongoDB reflects the dropped@db.Date. Existing docs keep their midnight-UTC instants, so reads stay consistent.tryParseDates: trueupload-date format validation (file-upload.processor.ts) — deferred to a separate task.Verification
pnpm lint— clean acrosscore,api,webpnpm test— 17/17 passing