Skip to content

fix(arc): render calendar dates in UTC so weekday labels stop shifting - #99

Merged
luidgi-dev merged 4 commits into
devfrom
fix/lui-149-day-of-the-week-offset
Sep 19, 2026
Merged

luidgi-dev merged 4 commits into
devfrom
fix/lui-149-day-of-the-week-offset

Conversation

@luidgi-dev

Copy link
Copy Markdown
Owner

Problem

On the ritual detail page, The Arc's week strip read SUN MON TUE ... SAT with the
header WEEK OF SEP 13, and logs made on Tuesday, Thursday and Saturday appeared
under Monday, Wednesday and Friday.

The week was never Sunday-first and the data was never wrong: every label was
rendered one day early, which makes a Monday-first row look Sunday-first.

Cause

i18n.ts does not set timeZone, so next-intl falls back to the server's zone
(getConfig.js: timeZone: o.timeZone || Intl.DateTimeFormat().resolvedOptions().timeZone)
and forwards it to NextIntlClientProvider - UTC in production.

the-arc.tsx parsed calendar dates without a Z:

new Date(`${value}T00:00:00`)

Per ECMAScript that is parsed in the runtime's zone. TheArc is a Client
Component, so that runtime is the browser: in Europe/Paris 2026-09-14T00:00:00
is 2026-09-13T22:00:00Z, which the UTC formatter renders as Sep 13.

Reproduced across zones (before -> after), formatter pinned to UTC:

Europe/Paris          Sun, Sep 13  ->  Mon, Sep 14
Pacific/Auckland      Sun, Sep 13  ->  Mon, Sep 14
America/Los_Angeles   Mon, Sep 14  ->  Mon, Sep 14
UTC                   Mon, Sep 14  ->  Mon, Sep 14

Only browsers east of UTC were affected, which is why it looked intermittent.

Fix

Add parseIsoDateUtc to lib/date.ts and pair it with timeZone: "UTC" at every
calendar-date display. Both halves are required: parsing in UTC without pinning the
formatter just moves the bug. This generalizes the convention already applied
locally in circles/[id]/page.tsx.

One change fixes all four shifted strings in The Arc: the day letters, the
WEEK OF header, the chart axis endpoints and the week-select aria-label.

rhythm-day-bar.tsx and one-time-status.tsx use the same pattern and are correct
today only because they render on the server, where both zones resolve to the same
value. Aligned for consistency; no behavior change. relDays is untouched:
relativeTime ignores timeZone and its T12:00:00 parse is a deliberate DST guard.

Tests

lib/rituals/arc.test.ts is new - arc.ts was pure but untested. It locks the data
contract this bug hid: weeks run Monday to Sunday, isToday is marked exactly once
at its Monday-based index, and Tue/Thu/Sat logs land at indices 1/3/5.

83 tests pass (76 + 7). lint, tsc --noEmit and build clean.
No i18n keys, no color tokens, no copy change.

Also in this PR

data/README.md documented the views as v_ritual_progress, v_daily_summary and
v_ritual_log_history. That prefix never existed - the real views are
ritual_progress, daily_summary and ritual_log_history, as migrate.py and
lib/data/rituals.ts have always used them.

Follow-up (not in this PR)

getUserToday (lib/profile.ts) reads profiles.timezone without .eq("id", user.id).
Since profiles is world-readable (for select using (true)), the query matches every
row, and maybeSingle() returns data = null plus a discarded PGRST116 error - so every
user silently falls back to 'UTC'. Between 00:00 and 02:00 Paris time that makes
"today" yesterday, including the logged_at written by logRitual. It is the only one
of the six timezone reads in the repo missing that filter.

- Introduced a new function `parseIsoDateUtc` to parse calendar dates in the format `YYYY-MM-DD` as UTC instants for consistent display formatting.
- This function ensures that dates are interpreted in UTC, preventing local time zone discrepancies during rendering.
- Added unit tests for the new `parseIsoDateUtc` function to verify correct UTC parsing and weekday retention.
- Created a new test file for the `arc` module, implementing tests for the `buildArcModel` function, ensuring proper week structure, today marker, and log placement.
- Enhanced test coverage for date handling and ritual logging functionality.
- Renamed views in the documentation for clarity: `v_ritual_progress` to `ritual_progress`, `v_daily_summary` to `daily_summary`, and `v_ritual_log_history` to `ritual_log_history`.
- Improved formatting for better readability.
…ponents to use parseIsoDateUtc for consistent UTC formatting

- Updated date parsing in RhythmDayBar and OneTimeStatus components to utilize the new parseIsoDateUtc function, ensuring dates are correctly interpreted as UTC.
- Removed the local date conversion function from TheArc component and replaced it with parseIsoDateUtc for better consistency in date formatting across the application.
- Enhanced date display logic to include time zone specification, preventing discrepancies in date representation.
@vercel

vercel Bot commented Sep 19, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
strive Ready Ready Preview Sep 19, 2026 7:40pm UTC

@github-actions

Copy link
Copy Markdown

Strive PR Agent Review

The review could not be generated for this run (model unavailable or rate-limited). Push again to retry.

@luidgi-dev
luidgi-dev merged commit db59d86 into dev Sep 19, 2026
11 checks passed
@luidgi-dev
luidgi-dev deleted the fix/lui-149-day-of-the-week-offset branch September 19, 2026 19:49
@luidgi-dev luidgi-dev mentioned this pull request Sep 20, 2026
5 tasks done

This branch was successfully deployed

1 active deployment
Preview — f9e545c5 Deployed Sep 19, 2026 by vercel[bot]
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.

1 participant