Skip to content

Migrate to date-fns v4 and official @date-fns/tz, matching sanity core#1569

Open
stipsan wants to merge 5 commits into
mainfrom
cursor/date-fns-v4-official-tz-7118
Open

Migrate to date-fns v4 and official @date-fns/tz, matching sanity core#1569
stipsan wants to merge 5 commits into
mainfrom
cursor/date-fns-v4-official-tz-7118

Conversation

@stipsan

@stipsan stipsan commented Jul 13, 2026

Copy link
Copy Markdown
Member

Description

The repo had a wide range of date-fns majors in play: @sanity/rich-date-input pinned date-fns@^2.30.0 + the community date-fns-tz@^2.0.1, while other plugins were already on the catalog's v4. Since sanity core migrated to date-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 duplicate date-fns instance.

Changes

  • @sanity/rich-date-input
    • date-fns ^2.30.0catalog: (^4.4.0), date-fns-tz → official @date-fns/tz (catalog:, ^1.5.0) — the exact versions sanity itself ships, so pnpm/npm dedupe to a single instance
    • Conversions rewritten the way sanity core does them: formatInTimeZone(date, tz, fmt)format(date, fmt, {in: tz(...)}), getTimezoneOffset(tz, d) / 60 / 1000tzOffset(tz, d), and zonedTimeToUtc(...) → a shiftWallClockToTimeZone helper built on TZDate components (mirrors sanity's createTZDateFromComponents)
    • The static @vvo/tzdb database is replaced with the Intl.supportedValuesOf('timeZone') + Intl.DateTimeFormat approach ported from sanity core's useTimeZone (lazily computed and cached; timezone-list cost is now only paid when the selector dialog opens)
    • Review follow-up: city labels use the last segment of the IANA id (America/Indiana/Indianapolis → "Indianapolis"), the selector matches stored alias spellings via Intl canonicalization (Europe/KyivEurope/Kiev, Asia/KolkataAsia/Calcutta — names older plugin versions stored via @vvo/tzdb), and the timezone button falls back to the raw zone name instead of rendering "undefined"
    • Added unit tests for the new utils (DST both directions, cross-zone shifts, offset formatting, alias handling, multi-segment city labels)
  • @sanity/assist, sanity-plugin-media: date-fns barrel imports → subpath imports (date-fns/format etc.), as sanity core does (refactor: import date-fns functions from subpaths, not barrel sanity#12247)
  • Lint: no-restricted-imports now bans the date-fns barrel and date-fns-tz to prevent regressions (messages aligned with sanity core's .oxlintrc.json); documented the conventions in AGENTS.md

Equivalence verification

A scratch harness compared the old date-fns-tz@2 pipeline against the new @date-fns/tz implementation over 30,800 conversions (10 zones × ~310 instants, including DST transitions): all UTC/local values agree except instants inside DST transitions, where the old getTimezoneOffset reported the offset of the moment before the shift while tzOffset reports the offset consistent with the formatted local time (the new stored offset now always matches the stored local string, e.g. -420 for 01: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 (richDateTest document): switching the timezone from UTC to Tokyo via the new Intl-derived selector preserves the 14:30 wall clock and rewrites utc/local/timezone/offset correctly.

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-input minor, @sanity/assist + sanity-plugin-media patches).

To show artifacts inline, enable in settings.

Open in Web Open in Cursor 

@vercel

vercel Bot commented Jul 13, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
plugins-studio Ready Ready Preview, Comment Jul 15, 2026 9:02pm

Request Review

@changeset-bot

changeset-bot Bot commented Jul 13, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 8c00347

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 6 packages
Name Type
@sanity/assist Patch
sanity-plugin-media Patch
@sanity/rich-date-input Minor
sanity-plugin-internationalized-array Patch
@sanity/document-internationalization Patch
@sanity/sfcc Patch

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

@stipsan stipsan added the 🤖 bot label Jul 13, 2026 — with Cursor
@stipsan
stipsan marked this pull request as ready for review July 14, 2026 00:59
@stipsan
stipsan requested a review from a team as a code owner July 14, 2026 00:59
@stipsan
stipsan requested review from Copilot and pedrobonamin and removed request for a team July 14, 2026 00:59
@stipsan
stipsan enabled auto-merge (squash) July 14, 2026 00:59
Comment thread plugins/@sanity/rich-date-input/src/utils/index.ts
Comment thread plugins/@sanity/rich-date-input/src/components/TimezoneSelector.tsx

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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/tz to the workspace catalog and migrate @sanity/rich-date-input from date-fns-tz + @vvo/tzdb to @date-fns/tz + Intl-derived timezone listing.
  • Switch @sanity/assist and sanity-plugin-media to date-fns/* subpath imports and enforce via no-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.

Comment thread plugins/@sanity/rich-date-input/src/utils/index.ts Outdated
Comment thread plugins/@sanity/rich-date-input/src/components/TimezoneSelector.tsx
Copilot AI review requested due to automatic review settings July 14, 2026 01:03

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ 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.

Comment thread plugins/@sanity/rich-date-input/src/utils/index.ts

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 19 out of 20 changed files in this pull request and generated 3 comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

Comment thread plugins/@sanity/rich-date-input/src/utils/index.ts
Comment thread plugins/@sanity/rich-date-input/src/components/TimezoneSelector.tsx
Comment thread plugins/@sanity/rich-date-input/src/components/TimezoneButton.tsx
Copilot AI review requested due to automatic review settings July 14, 2026 13:54
@stipsan
stipsan removed the request for review from Copilot July 14, 2026 13:54
Copilot AI review requested due to automatic review settings July 15, 2026 21:02
@stipsan
stipsan removed the request for review from Copilot July 15, 2026 21:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants