Migrate to date-fns v4 and official @date-fns/tz, matching sanity core#1569
Migrate to date-fns v4 and official @date-fns/tz, matching sanity core#1569stipsan wants to merge 5 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
🦋 Changeset detectedLatest commit: 8c00347 The changes in this PR will be included in the next version bump. This PR includes changesets to release 6 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
There was a problem hiding this comment.
Pull request overview
Migrates plugin date/time handling to match Sanity Studio core by standardizing on date-fns v4 (via the workspace catalog) and the official @date-fns/tz, reducing the risk of duplicate date-fns instances in consuming studios and aligning timezone conversions/formatting behavior across plugins.
Changes:
- Add
@date-fns/tzto the workspace catalog and migrate@sanity/rich-date-inputfromdate-fns-tz+@vvo/tzdbto@date-fns/tz+ Intl-derived timezone listing. - Switch
@sanity/assistandsanity-plugin-mediatodate-fns/*subpath imports and enforce viano-restricted-imports. - Add unit tests for the new rich-date-input timezone utilities and ship changesets for the affected packages.
Reviewed changes
Copilot reviewed 19 out of 20 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| pnpm-workspace.yaml | Adds @date-fns/tz to the default catalog for consistent dependency resolution. |
| pnpm-lock.yaml | Updates lockfile to reflect catalog + dependency changes (dedupe, removals). |
| plugins/sanity-plugin-media/src/components/TableRowAsset/index.tsx | Switches formatRelative to date-fns/formatRelative subpath import. |
| plugins/sanity-plugin-media/src/components/FormSubmitButton/index.tsx | Switches format to date-fns/format subpath import. |
| plugins/sanity-plugin-media/src/components/AssetMetadata/index.tsx | Switches format to date-fns/format subpath import. |
| plugins/@sanity/rich-date-input/src/utils/index.ts | Replaces date-fns-tz/@vvo/tzdb utilities with @date-fns/tz + Intl-based timezone list and new conversion helpers. |
| plugins/@sanity/rich-date-input/src/utils/index.test.ts | Adds tests for timezone shifting, formatting, listing, and abbreviation resolution. |
| plugins/@sanity/rich-date-input/src/types/index.ts | Updates NormalizedTimeZone shape to match the new Intl-derived model. |
| plugins/@sanity/rich-date-input/src/components/TimezoneSelector.tsx | Updates timezone selection logic to use new utils and tzOffset. |
| plugins/@sanity/rich-date-input/src/components/TimezoneButton.tsx | Uses getTimeZoneAbbreviation instead of searching a static timezone list. |
| plugins/@sanity/rich-date-input/src/components/RelativeDateTimePicker.tsx | Updates UTC/local calculations to use shiftWallClockToTimeZone + tzOffset. |
| plugins/@sanity/rich-date-input/package.json | Replaces pinned deps with catalog: date-fns and adds @date-fns/tz. |
| plugins/@sanity/assist/src/constants.ts | Switches minutesToMilliseconds to subpath import. |
| plugins/@sanity/assist/src/components/TimeAgo.tsx | Switches formatDistanceToNow to subpath import. |
| plugins/@sanity/assist/src/assistDocument/hooks/useInstructionToaster.tsx | Switches addSeconds/isAfter to subpath imports. |
| AGENTS.md | Documents the new date-fns/@date-fns/tz conventions for contributors/agents. |
| .oxlintrc.json | Enforces subpath-only date-fns imports and bans date-fns-tz. |
| .changeset/rich-date-input-date-fns-v4.md | Minor release notes for the rich-date-input timezone migration. |
| .changeset/media-date-fns-subpaths.md | Patch release notes for media plugin date-fns import change. |
| .changeset/assist-date-fns-subpaths.md | Patch release notes for assist plugin date-fns import change. |
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit ac09075. Configure here.
… button label fallback
…ficial-tz-7118 # Conflicts: # .oxlintrc.json

Description
The repo had a wide range of
date-fnsmajors in play:@sanity/rich-date-inputpinneddate-fns@^2.30.0+ the communitydate-fns-tz@^2.0.1, while other plugins were already on the catalog's v4. Sincesanitycore migrated todate-fns@^4.4.0+ the official@date-fns/tz@^1.5.0(sanity-io/sanity#11295, #13041, #12990), the old pins meant studios bundled a duplicatedate-fnsinstance.Changes
@sanity/rich-date-inputdate-fns^2.30.0→catalog:(^4.4.0),date-fns-tz→ official@date-fns/tz(catalog:,^1.5.0) — the exact versionssanityitself ships, so pnpm/npm dedupe to a single instanceformatInTimeZone(date, tz, fmt)→format(date, fmt, {in: tz(...)}),getTimezoneOffset(tz, d) / 60 / 1000→tzOffset(tz, d), andzonedTimeToUtc(...)→ ashiftWallClockToTimeZonehelper built onTZDatecomponents (mirrors sanity'screateTZDateFromComponents)@vvo/tzdbdatabase is replaced with theIntl.supportedValuesOf('timeZone')+Intl.DateTimeFormatapproach ported from sanity core'suseTimeZone(lazily computed and cached; timezone-list cost is now only paid when the selector dialog opens)America/Indiana/Indianapolis→ "Indianapolis"), the selector matches stored alias spellings viaIntlcanonicalization (Europe/Kyiv→Europe/Kiev,Asia/Kolkata→Asia/Calcutta— names older plugin versions stored via@vvo/tzdb), and the timezone button falls back to the raw zone name instead of rendering "undefined"@sanity/assist,sanity-plugin-media:date-fnsbarrel imports → subpath imports (date-fns/formatetc.), as sanity core does (refactor: importdate-fnsfunctions from subpaths, not barrel sanity#12247)no-restricted-importsnow bans thedate-fnsbarrel anddate-fns-tzto prevent regressions (messages aligned with sanity core's.oxlintrc.json); documented the conventions inAGENTS.mdEquivalence verification
A scratch harness compared the old
date-fns-tz@2pipeline against the new@date-fns/tzimplementation over 30,800 conversions (10 zones × ~310 instants, including DST transitions): all UTC/local values agree except instants inside DST transitions, where the oldgetTimezoneOffsetreported the offset of the moment before the shift whiletzOffsetreports the offset consistent with the formatted local time (the new storedoffsetnow always matches the storedlocalstring, e.g.-420for01:30-07:00). For nonexistent spring-forward wall clocks the two libraries pick the two standard interpretations (skip forward vs. back); both are valid readings of an ambiguous input.Walkthrough
rich_date_input_tokyo_timezone_switch.mp4
Manual test in the dev studio (
richDateTestdocument): switching the timezone from UTC to Tokyo via the new Intl-derived selector preserves the 14:30 wall clock and rewritesutc/local/timezone/offsetcorrectly.Timezone dropdown showing GMT+9 Japan Standard Time Tokyo
JSON inspector showing Asia/Tokyo, offset 540, shifted UTC
Notes for release
Covered by the changesets (
@sanity/rich-date-inputminor,@sanity/assist+sanity-plugin-mediapatches).To show artifacts inline, enable in settings.