-
-
Notifications
You must be signed in to change notification settings - Fork 100
Description
Bug Description
When the device's local timezone differs from the timeZone prop on CalendarContainer, all events render on the wrong day (typically diffDays: -1, placing them one column to the left and off-screen).
Affects: v2.5.6 (likely all v2.x)
Environment: React Native 0.83, Expo SDK 55, Hermes, Reanimated v4
Root Cause
In src/components/EventItem.tsx (lines 84-110), the diffDays calculation iterates from startUnix to eventStartUnix and checks each day against visibleDates:
const dayStartUnix = parseDateTime(dayUnix).startOf('day').toMillis();
if (!visibleDates[dayStartUnix]) {
diffDays--;
}The problem: visibleDates keys are midnight timestamps computed in one timezone context (from the page layout system), but parseDateTime(dayUnix).startOf('day').toMillis() computes midnight in the device's local timezone. When these differ (e.g., device in America/Toronto UTC-5, container timeZone="Africa/Tripoli"), the midnight timestamps never match — so diffDays-- fires for every day, collapsing all events to column -1.
Additionally, forceUpdateZone() in src/utils/dateUtils.ts hardcodes zone: 'local' instead of using the container's timeZone, which contributes to the mismatch.
Steps to Reproduce
- Set device timezone to anything other than UTC (e.g.,
America/Toronto, UTC-5) - Create a
CalendarContainerwithtimeZone="UTC"(or any timezone ≠ device) - Pass events with
start: { dateTime: "2026-03-23T08:00:00+00:00", timeZone: "UTC" } - Observe: all events have
diffDays: -1regardless of their actual date
Expected Behavior
Events should render on the correct day column based on the container's timeZone, independent of the device's local timezone.
Workaround
Replace the midnight-comparison loop in EventItem.tsx with a range-based lookup against visibleDates:
const sortedVisible = Object.keys(visibleDates)
.map(Number)
.sort((a, b) => a - b);
let diffDays = -1;
for (let i = 0; i < sortedVisible.length; i++) {
const visEnd =
i < sortedVisible.length - 1
? sortedVisible[i + 1]
: sortedVisible[i] + MILLISECONDS_IN_DAY;
if (eventStartUnix >= sortedVisible[i] && eventStartUnix < visEnd) {
diffDays = i;
break;
}
}This is timezone-agnostic — it checks which visibleDates interval the event's unix timestamp falls into, rather than comparing midnight timestamps across timezone contexts.
We're using this via pnpm patch in production. Happy to open a PR if helpful.