Skip to content

[scheduler] Create plain Date objects and read them in explicit timezones - #24029

Draft
flaviendelangle wants to merge 3 commits into
mui:masterfrom
flaviendelangle:scheduler-plain-js-dates
Draft

flaviendelangle wants to merge 3 commits into
mui:masterfrom
flaviendelangle:scheduler-plain-js-dates

Conversation

@flaviendelangle

@flaviendelangle flaviendelangle commented Oct 28, 2026 •

Copy link
Copy Markdown
Member

The Scheduler stops tagging its dates with a timezone: it creates plain Date objects and reads them in the timezone it already knows, the display one or the one of the event.

Changes

  • The dates are plain Date objects. createAdapterDate() returns a Date of the instant, instead of a TZDate that adapter.setTimezone() tagged with the display or data timezone. The processed dates, the occurrences, the navigation and the values passed to the store all use it. TZDate only comes back if a user passes one in, and the Scheduler only reads its instant.
  • The timezone is explicit wherever a date is read. buildTemporalPlainDate(), processDate(), normalizeAllDayBounds(), moveToPlainDate(), addToAdapterDate(), getRecurringOccurrenceKey(), getWeekDayCode(), realignWeeklyByDay() and adjustRRuleForAllMove() take the timezone they read in, instead of adapter.getTimezone() of their value. The store passes the display timezone or the data timezone of the event, the recurring events read the data timezone, and the navigation the display one.
  • The adapter calls that only existed for TZDate go. toJsDate() becomes new Date(adapter.getTime(value)) for the Z strings, isSameDay() compares day numbers in the data timezone, and setTimezone() / getTimezone() are no longer called.
  • No more re-tagging of the changes. applyDataTimezoneToEventUpdate() only re-tagged the dates of a change with the data timezone, so it goes from the recurring events plugin, and the store passes one set of changes to the plugin and to the editing surface.
  • The plain-date helpers read the real offsets. They copied the way TZDate reads the offsets between -01:00 and 00:00 (as positive, like the -00:44:30 of Africa/Monrovia until 1972) to match the adapter values. With no TZDate left, they read the offset of the timezone.
  • Simplifications.
    • The adapter parameter goes from the helpers that only used it to create dates: createStartOfPlainDate(), createEndOfPlainDate(), createProcessedDate(), createAdapterDateFromWallTime(), getDayListFromPlainDates(), createStartOfPreviousDay() and getRecurrenceUntil().
    • schedulerOtherSelectors.visibleDate goes: it re-tagged the visible date with the display timezone, and its consumers only read its instant.
    • getDateKey() is inlined into getRecurringOccurrenceKey(), its only caller.
    • The regression tests no longer load @date-fns/tz before the fake clock, as nothing builds a TZDate there anymore.

No file of the BaseTemporal copy changes.

Changelog

Breaking changes

  • The value of the processed dates of the occurrences (onEventEditingStart, getEventAriaLabel, the timelineEventContent slot and useEventDialogOccurrence()) and the date passed to onVisibleDateChange no longer carry the display timezone. Treat them as instants, not as a type to rely on: read the date and the time of a processed date in the display timezone through its key, minutesInDay and timestamp. The instants don't change.

    -const hour = occurrence.displayTimezone.start.value.getHours();
    +const hour = Math.floor(occurrence.displayTimezone.start.minutesInDay / 60);
  • The offsets between -01:00 and 00:00, used by a few timezones before the 1970s (Africa/Monrovia until 1972), are read as negative. They were read as positive, like TZDate does, which put the events of those years about an hour off.

Adapter usage

Counted like in #23943. The references go from 43 on master, since #24021, to 25. setTimezone, getTimezone, toJsDate and isSameDay are gone, so the Scheduler uses 4 methods instead of 8: getTime, addMilliseconds, date and isValid.

Method or property master This PR Difference
setTimezone 10 0 −10
getTimezone 5 0 −5
addMilliseconds 4 2 −2
date 4 2 −2
toJsDate 2 0 −2
isSameDay 1 0 −1
getTime 14 18 +4

addMilliseconds remains for the paste and the end of an occurrence, date and isValid for parsing the event strings, and getTime reads the instants of the values that come in.

Bundle size

Measured from source with esbuild, minified, with third-party packages external and without the error minifier, gzip level 9.

Entry master This PR Difference
event-calendar 76.22 kB 75.88 kB −334 B
event-calendar-premium 91.62 kB 91.20 kB −420 B
event-timeline-premium 68.77 kB 68.34 kB −427 B

Validation

  • pnpm test:unit --project "x-scheduler*" --run and pnpm test:browser --project "x-scheduler*" --run pass. A few tests time out under the load of the full jsdom run and pass on their own.

  • The tests that checked the timezone a value was tagged with check its instant, its processed fields or that it is a plain Date. The tests that read the values with the adapter getters read their key / minutesInDay, or the value tagged by the test itself. Tests still build their inputs with adapter.date(..., timezone), which returns TZDates, as users can.

  • New tests: the processed dates and createAdapterDate() return plain Date objects, the epoch in Africa/Monrovia reads as 23:15:30 on December 31st, 1969, and getRecurringOccurrenceKey() keys an occurrence on its day in the timezone.

  • A /simplify-codebase pass on the change removed schedulerOtherSelectors.visibleDate, getDateKey() and the unused adapter parameter of getRecurrenceUntil(), and updated the comments that still described tagged values. It kept createAdapterDate(), the one place that decides what the adapter values are, and processDate() next to buildProcessedDate().

  • A review round with Codex and the base-ui-review skill approved without findings.

  • TypeScript, ESLint and Prettier pass; pnpm proptypes and pnpm docs:api were run (the features prop of the Event Timeline no longer lists applyDataTimezoneToEventUpdate).

  • I have followed (at least) the PR section of the contributing guide.

🤖 Generated with Claude Code

@flaviendelangle flaviendelangle added breaking change Introduces changes that are not backward compatible. type: enhancement It’s an improvement, but we can’t make up our mind whether it's a bug fix or a new feature. scope: scheduler Changes related to the scheduler. labels Oct 28, 2026
@code-infra-dashboard

code-infra-dashboard Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

Deploy preview

https://deploy-preview-24029--material-ui-x.netlify.app/
QR code for https://deploy-preview-24029--material-ui-x.netlify.app/

Bundle size

Bundle Parsed size Gzip size
@mui/x-data-grid 0B(0.00%) 0B(0.00%)
@mui/x-data-grid-pro 0B(0.00%) 0B(0.00%)
@mui/x-data-grid-premium 0B(0.00%) 0B(0.00%)
@mui/x-charts 0B(0.00%) 0B(0.00%)
@mui/x-charts-pro 0B(0.00%) 0B(0.00%)
@mui/x-charts-premium 0B(0.00%) 0B(0.00%)
@mui/x-date-pickers 0B(0.00%) 0B(0.00%)
@mui/x-date-pickers-pro 0B(0.00%) 0B(0.00%)
@mui/x-tree-view 0B(0.00%) 0B(0.00%)
@mui/x-tree-view-pro 0B(0.00%) 0B(0.00%)
@mui/x-scheduler ▼-951B(-0.22%) ▼-308B(-0.25%)
@mui/x-scheduler-premium ▼-1.17KB(-0.20%) ▼-370B(-0.22%)
@mui/x-chat 0B(0.00%) 0B(0.00%)
@mui/x-license 0B(0.00%) 0B(0.00%)

Details of bundle changes


Check out the code infra dashboard for more information about this PR.

@github-actions github-actions Bot added the PR: out-of-date The pull request has merge conflicts and can't be merged. label Oct 28, 2026
@github-actions

Copy link
Copy Markdown
Contributor

This pull request has conflicts, please resolve those before we can evaluate the pull request.

@flaviendelangle
flaviendelangle force-pushed the scheduler-plain-js-dates branch from 8124816 to 56969f8 Compare October 28, 2026 14:17
@github-actions github-actions Bot added PR: out-of-date The pull request has merge conflicts and can't be merged. and removed PR: out-of-date The pull request has merge conflicts and can't be merged. labels Oct 28, 2026
@github-actions

Copy link
Copy Markdown
Contributor

This pull request has conflicts, please resolve those before we can evaluate the pull request.

@flaviendelangle
flaviendelangle force-pushed the scheduler-plain-js-dates branch from 56969f8 to 0baae09 Compare October 29, 2026 12:34
@github-actions github-actions Bot removed the PR: out-of-date The pull request has merge conflicts and can't be merged. label Oct 29, 2026
flaviendelangle and others added 3 commits October 30, 2026 10:58
…ones

- The Scheduler creates plain `Date` objects instead of `TZDate`s tagged with the display or data timezone, and reads them in the timezone it already knows: `buildTemporalPlainDate()`, `processDate()`, `normalizeAllDayBounds()`, `moveToPlainDate()`, `addToAdapterDate()` and `getRecurringOccurrenceKey()` take it as a parameter.
- `toJsDate()` and `isSameDay()` are replaced with `getTime()` and day numbers, and `setTimezone()` and `getTimezone()` are no longer called.
- `applyDataTimezoneToEventUpdate()` is removed from the recurring events plugin: the changes need no re-tagging.
- The plain-date helpers read the real offsets: they no longer copy the offsets `TZDate` reads between -01:00 and 00:00.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Remove `schedulerOtherSelectors.visibleDate`: it re-tagged the visible date with the display timezone, and its consumers only read its instant.
- Inline `getDateKey()` into `getRecurringOccurrenceKey()`, its only caller.
- Remove the unused `adapter` parameter of `getRecurrenceUntil()`.
- Update the comments that still described values tagged with a timezone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@flaviendelangle
flaviendelangle force-pushed the scheduler-plain-js-dates branch from 0baae09 to ebba318 Compare October 30, 2026 09:58
@github-actions github-actions Bot added the PR: out-of-date The pull request has merge conflicts and can't be merged. label Oct 30, 2026
@github-actions

Copy link
Copy Markdown
Contributor

This pull request has conflicts, please resolve those before we can evaluate the pull request.

This branch has not been deployed

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

Labels

breaking change Introduces changes that are not backward compatible. PR: out-of-date The pull request has merge conflicts and can't be merged. scope: scheduler Changes related to the scheduler. type: enhancement It’s an improvement, but we can’t make up our mind whether it's a bug fix or a new feature.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant