Repository navigation
[scheduler] Fix the navigation of the Timeline presets across DST transitions - #23966
flaviendelangle merged 13 commits into
Conversation
The presets of the Timeline read the first and last days of their range and move the visible date with the PlainDate API, instead of the adapter's calendar math. Navigating keeps the wall-clock time of the visible date, whatever the timezone of the system. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Deploy previewBundle size
Check out the code infra dashboard for more information about this PR. |
…transitions Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The `dayAndHour`, `dayAndMonth`, `monthAndYear` and `year` presets build their range and navigation with `createPlainDateRange()`, from the start of their period and its duration. The end of a range is the last instant before the period that follows it, so it no longer subtracts a day it then adds back. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- `createPlainDateRange()` builds the start, the end and the navigation of the day, week, month and year presets from one unit, and `getEndDate()` adds the units to the plain date of the start. - `addToAdapterDate()` moves an adapter date by a plain-date duration on its wall clock. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…and repeated times Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…m skips Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
This pull request has conflicts, please resolve those before we can evaluate the pull request. |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
A few things from my review, all checked by running the presets on this branch and on master. No bugs in the normal date range, and the new tests all fail on master: 🟡 🟡 The weeks, months and years units aren't pinned. Every new navigate test uses
The Havana repeated midnight of change 3 isn't tested either. ℹ️ Ranges past year 9999 now throw. The ℹ️ An invalid ℹ️ |
…pterDate() addDaysOnWallClock() had the same body as addToAdapterDate() with a duration in days. It now calls it and keeps returning the date unchanged for 0 days. The JSDoc of createDateFromPlainDate() also names what an `offsetMs` of -1 returns. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…et unit The navigation tests run on the days, weeks, months and years of the presets, and new tests cover the start of a month at a midnight that Havana repeats on a Los Angeles system, the end of a month in Nuuk on a Paris system, and the end of a year that Kiritimati cut short. They all fail with master's presets. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
This pull request has conflicts, please resolve those before we can evaluate the pull request. |
# Conflicts: # packages/x-scheduler-internals/src/internals/utils/plain-date-utils/adapter.ts
# Conflicts: # packages/x-scheduler-internals-premium/src/internals/utils/preset-utils.tsx
|
This pull request has conflicts, please resolve those before we can evaluate the pull request. |
This PR moves the ranges and the navigation of the Timeline presets to the plain dates of BaseTemporal, after the recurring events in #23943. The presets compute the first and last instants of the visible range, the number of grid cells of some of them, and the visible date of the next and previous buttons. It's independent of #23961, #23962 and #23963. The public API doesn't change.
Changes
getStartDate(),getEndDate()andnavigate()of the 5 presets come from one helper,createPlainDateRange(), given the unit of the preset and the first day of its period:PlainDate.with().dayAndWeekstill starts ongetStartOfWeek().PlainDate.add().dayAndWeekno longer callsgetEndOfWeek().navigate()moves the visible date by days, weeks, months or years on the wall clock withaddToAdapterDate(), keeping its time of day. A time that a DST transition skips moves forward by the length of the gap, and a repeated one resolves to its first instant, like the wall times of [scheduler] Fix recurring events across timezones and DST transitions #23943.getCssUnitCount()ofdayAndWeekandmonthAndYearsubtracts day numbers instead of callingdifferenceInDays().plain-date-utilsgetsaddToAdapterDate(), andaddDaysOnWallClock()of the drag and drop, from [scheduler] Fix the drag and drop of the day grid across DST transitions and speed up its preview #23964, calls it, so that one function moves dates on the wall clock. The internals export thePlainDateDurationtype.createStartOfPlainDate()andcreateEndOfPlainDate()instead of theoffsetMsofcreateDateFromPlainDate().Behavior changes
The old code depended on how
TZDateresolves the times that DST transitions skip or repeat, which changes with the timezone of the system. An exhaustive scan of the navigation, below, found no change of the visible range in common timezones. The changes are in the visible date the buttons set, whichonVisibleDateChangereceives, and in the bounds of some ranges in rarer timezones.dayAndHourfrom October 24th, 2026 at 02:00 went to October 25th at 01:00. It goes to 02:00 now, and so dodayAndWeekfrom October 18th,monthAndYearfrom September 25th andyearfrom October 25th, 2025. On a London system with the Timeline in Europe/Paris, the next day of March 27th, 2027 at 02:30 was March 28th at 01:30, before the skipped hour. It's 03:30 now, after it. On a Lord Howe system, the 30-minute transitions moved some times by 30 minutes.dayAndHourordayAndMonthrange starting on April 5th, 2026 in America/Santiago started on April 4th at 23:30.monthAndYearrange starting on April 1st, 2012 in New York started at 01:00.monthAndYearrange of October 2021 ended on October 30th at 22:59:59.999, 25 hours before the end of the month, and adayAndWeekweek ending on Saturday, October 30th ended an hour early.dayAndWeekweek from Saturday, December 24th in New York ended on Saturday the 31st instead of Friday the 30th.monthAndYearoryearrange ending with 1994 ended on January 1st, 1995 at 23:59:59.999, andmonthAndYearcounted 32 days in December 1994. The range ends just before 1995 starts now, and December 1994 has 30 days.visibleDatenow throws in all the presets, where master rendered an empty Timeline. The Event Calendar already throws on master.yearpreset throws for avisibleDatefrom 9970, because the end of its 30 years is computed from January 1st, 10000, after the last year ofPlainDate. The next button gets there from 9940. Master rendered these ranges, up to one ending in 10028. The Scheduler doesn't support dates after 9999.getStartOfWeek()also depends on the system on these days, which #23961 fixes. Until then, on a Lord Howe system,dayAndWeekin Santiago can still start on April 4th, 2026 at 23:30. Its 16 weeks then end on July 24th, where they ended on July 18th before. On a Paris system, a week of America/Nuuk from Sunday, October 31st, 2021 still starts on October 30th at 23:00.Bundle size
Measured from source with esbuild, minified, with third-party packages external and without the error minifier, gzip level 9.
event-calendarevent-calendar-premiumevent-timeline-premiumThe Timeline already bundles
PlainDate. The difference is the helpers ofpreset-utils.tsxandaddToAdapterDate(), which the Event Calendar now calls fromaddDaysOnWallClock().Adapter usage
Counted like in #23943. The references go from 493 to 474, and
endOfYearis gone, so the Scheduler uses 50 methods and properties instead of 51.addDaysaddMonths,addWeeks,addYearsdifferenceInDaysstartOfDayendOfDaystartOfMonth,startOfYearendOfMonth,endOfYeargetTimezoneValidation
pnpm test:unit --project "x-scheduler*" --run: 3,856 passed, andpnpm test:browseronx-scheduler-internals,x-scheduler-internals-premiumandx-scheduler-premium: 3,146 passed. With master'spreset-utils.tsx, the 18 tests ofgetStartDate(),getEndDate()andnavigate()fail. They cover the navigation of the 4 units, the starts ofdayAndHourandmonthAndYear, and the ends ofdayAndHour,dayAndWeek,monthAndYearandyear.A randomized harness, not committed, compared the presets with master's on 5,000 random cases under each of 8 system timezones (UTC, New York, Paris, Lord Howe, Havana, Kolkata, Apia and Santiago). It covered 10 display timezones around their DST transitions: the start, end, cell count and navigation of the 5 presets, with random unit counts and week starts. Every difference falls into one of the behavior changes above. After the last
/simplifypass, it compared the presets with their previous version on 3,000 random cases under 6 system timezones. The only differences were the ends ofdayAndWeekon the Apia and Lord Howe systems, described above.After Rita's review, a search compared the start, end and cell count of
monthAndYearandyearwith master's for every month from 1990 to 2030, under 12 system timezones and in 16 display timezones, then foryearunder 19 system timezones with unusual transitions. Every difference falls into the behavior changes above, which now list the Havana system, Nuuk and Kiritimati ones, and the start of a year never changed. With the bounds the presets return,differenceInDays(end, start) + 1gives the same count as the day numbers under 14 system timezones and in 14 display timezones. They only differ whengetStartOfWeek()returns a start that isn't a midnight, like the Nuuk week above.An exhaustive scan ran every quarter of an hour of 3 days around 8 DST transitions of 2026, in 9 display timezones (UTC, New York, Los Angeles, São Paulo, London, Paris, Tokyo, Sydney and the system one). It went through the navigation of the 5 presets by one step and by their unit count, under 6 system timezones (UTC, New York, Los Angeles, London, Paris and Sydney). The new visible dates never changed the visible range.
Six review rounds with Codex and the base-ui-review skill, in three passes, approved without findings. Two
/simplifypasses shared one plain-date range between the presets, then built their start, end and navigation from one helper.TypeScript, ESLint and Prettier pass.
I have followed (at least) the PR section of the contributing guide.
🤖 Generated with Claude Code