Skip to content

Standardize date/time handling on ISO8601 - #230

Open
gdevenyi wants to merge 7 commits into
mainfrom
iso8601-date-compliance
Open

Standardize date/time handling on ISO8601#230
gdevenyi wants to merge 7 commits into
mainfrom
iso8601-date-compliance

Conversation

@gdevenyi

Copy link
Copy Markdown

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)

  • API emitted non-ISO stringstoDateString() ("Wed Jul 14 2026") in dataset row payloads and CSV metadata exports.
  • 14× toLocaleDateString() with no locale arg in web/ — locale-indeterminate display.
  • Prisma @db.Date on createdAt/updatedAt truncated the time portion.
  • Loose client parsingz.coerce.date() accepted non-ISO strings.
  • Divergent User type declared dates as number (unix ms).
  • Magic constants (24 * 3600 * 1000) in polars day-since-epoch conversion.

Changes by commit

  1. add luxon and ISO8601 date helpers in core — new core/src/dates.ts exporting formatISODate, formatISODateTime, parseISODate (all UTC-forced).
  2. emit ISO8601 datetimes in dataset rows and CSV metadatatoDateString()formatISODateTime() in tabular-data.service.ts and datasets.service.ts.
  3. render ISO8601 dates in web UI via core helper — 14 toLocaleDateString() sites → formatISODate() (ISO date-only display).
  4. migrate Prisma createdAt/updatedAt to full DateTime — drop @db.Date from User, Dataset, Project, SetupConfig.
  5. align User type and project query parsing with ISO8601User date fields numberDate | null | undefined; z.coerce.date()z.union([z.iso.datetime(), z.iso.date()]).transform(parseISODate).
  6. replace date magic constants and simplify new Date callsMS_PER_DAY named constant with explanatory comment; new Date(Date.now())new Date().

Notes

  • i18n: UI now renders ISO date-only (2026-07-14) uniformly across en/fr locales. This is an intentional decision from the audit — flag if localized display is preferred instead.
  • Deploy: requires prisma db push (or app sync) so MongoDB reflects the dropped @db.Date. Existing docs keep their midnight-UTC instants, so reads stay consistent.
  • Out of scope: polars tryParseDates: true upload-date format validation (file-upload.processor.ts) — deferred to a separate task.

Verification

  • pnpm lint — clean across core, api, web
  • pnpm test — 17/17 passing

@gdevenyi
gdevenyi requested a review from joshunrau as a code owner July 14, 2026 17:09
Copilot AI review requested due to automatic review settings July 14, 2026 17:09

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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, and parseISODate helpers in @databank/core and exported them for cross-package use.
  • Updated web/ routes and useProjectQuery to render/parse dates using the shared ISO helpers instead of toLocaleDateString() and z.coerce.date().
  • Updated API services to emit ISO8601 datetimes and adjusted Prisma schemas to store full DateTime (removing @db.Date truncation), plus a small Date construction cleanup and a named MS_PER_DAY constant.

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.

Comment thread core/src/dates.ts
Comment on lines +10 to +12
export const formatISODate = (date: Date): string => {
return DateTime.fromJSDate(date, { zone: 'utc' }).toISODate()!;
};

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread core/src/dates.ts
Comment on lines +20 to +22
export const formatISODateTime = (date: Date): string => {
return DateTime.fromJSDate(date, { zone: 'utc' }).toISO()!;
};

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants