• About
  • Blog
  • Tools
  • Case Studies
  • Contact

ContentWrap simplifies your Sanity CMS workflow

© Copyright 2025 ContentWrap. All Rights Reserved.

Work
  • About
  • Blog
  • Tools
  • Case Studies
  • Contact
Legal
  • Terms of Service
  • Privacy Policy
  • Cookie Policy
  1. Blog
  2. how to solve timezone issues in sanity CMS
Sanity Setup
Studio UX
Sanity
May 13, 2025

How to Solve Timezone Issues in Sanity CMS with rich-date-input: A Step-by-Step Guide

Tamba Monrose
Founder of ContentWrap
how to solve timezone issues in sanity cms with rich-date-input

In this article

  1. Introduction
  2. The Problem with Sanity's Default Datetime Handling
  3. Understanding Datetime Storage in CMS Systems
  4. Introducing the rich-date-input Plugin
  5. Step 1: Installing the Plugin
  6. Step 2: Configuring Sanity Studio
  7. Step 3: Creating a Schema with richDate Fields
  8. Step 4: Testing the Implementation
  9. Step 5: Frontend Implementation
  10. Common Issues and Troubleshooting
  11. Real-world Use Cases
  12. Advanced Techniques
  13. Conclusion

Share this article

Introduction

If you've ever worked with a global team or managed content that needs precise publishing times, you've likely encountered the frustration of datetime handling in content management systems. Sanity CMS, while powerful in many ways, has a particularly confusing default approach to handling dates and times that can lead to unexpected publishing issues and team confusion.

Consider this real scenario that recently happened to a content editor in New York:

One evening, a content editor, based in NYC, created a blog post and set the "publishDate" field to be that current day, March 10, 2025.

After publishing, she visited the live site only to find her post displaying a publish date of March 11, 2025. Confused, she double-checked the Sanity Studio, which still showed March 10.

What went wrong?

This discrepancy occurs because Sanity displays dates in your local timezone within the Studio interface but stores them internally as UTC. For the NYC editor, 9:30 PM EST converts to 1:30 AM or 2:30 AM UTC on May 11 (depending on daylight saving time) — hence the incorrect display date on the frontend.

Fortunately, there's a solution: the rich-date-input plugin for Sanity CMS. This powerful tool stores datetime information with complete timezone context, solving these issues once and for all.

By the end of this tutorial, you'll be able to:

  • Understand why Sanity's default datetime handling is problematic
  • Install and configure the rich-date-input plugin
  • Implement timezone-aware datetime fields in your schemas
  • Properly render datetime information on your frontend
  • Avoid common timezone pitfalls in content management

Prerequisites

  • A Sanity CMS project (version 3.0+)
  • Basic knowledge of Sanity schema configuration
  • Node.js and npm installed
  • Familiarity with JavaScript/TypeScript

The Problem with Sanity's Default Datetime Handling

Sanity's built-in datetime type seems straightforward but contains a subtle yet significant issue: timezone confusion.

Here's what happens:

  1. A content editor sets a date and time in Sanity Studio
  2. The Studio UI displays this datetime in the editor's local timezone
  3. Sanity stores the value internally as UTC
  4. When retrieved via API, only the UTC value is available
  5. The frontend displays this UTC value, potentially showing a different date than what the editor set
graphic showing timezone issues with Sanity CMS
With Sanity's standard date and datetime fields, in the Sanity Studio, users set and see the datetime displayed in their local timezone. However, the datetime that is actually saved to Sanity's data lake is the datetime only in UTC timezone. This discrepancy makes working in distributed teams difficult; How do users know what datetime will be rendered in the app?

For teams spanning multiple timezones, this creates constant confusion. An editor in Los Angeles setting a display datetime of 7:30 PM PST might be surprised when their content displays a datetime of 2:30 AM UTC.

Even worse, they might unintentionally schedule content for the wrong datetime.

Understanding Datetime Storage in CMS Systems

Storing datetimes in UTC is actually a standard best practice in software development. The problem isn't with UTC storage itself, but rather with losing timezone context in the process.

Most CMS systems follow one of these approaches:

  1. UTC Only: Store only UTC timestamps, forcing frontend developers to handle timezone conversion (Sanity's default approach)
  2. Local Time + UTC: Store both the user's intended local time and the UTC conversion
  3. Complete Context: Store the original input, UTC conversion, timezone, and offset (the approach rich-date-input takes)

The third approach is clearly superior for content management because it preserves the editor's exact intent while providing the technical flexibility needed for proper rendering.

Introducing the rich-date-input Plugin

The rich-date-input plugin for Sanity solves these issues by storing a rich data structure that includes:

  • The local datetime as input by the editor
  • The UTC conversion
  • The timezone identifier (e.g., "America/New_York")
  • The offset from UTC in minutes

This comprehensive approach ensures that no matter how you need to display or manipulate the datetime, you have all the necessary context.

The typical data output from the plugin looks like this:

{  "_type": 'richDate',  "local": '2023-02-21T10:15:00+01:00',  "utc": '2023-02-12T09:15:00Z',  "timezone": 'Europe/Oslo',  "offset": 60}

With this structure, you can:

  • Display the time as intended in the editor's timezone
  • Convert to any viewer's local timezone
  • Maintain consistent dates across timezone boundaries
  • Implement sophisticated publishing logic based on regional needs

Step 1: Installing the Plugin

Installing via npm

First, let's install the plugin in your Sanity project:

Terminal
npm install @sanity/rich-date-input

This package provides the richDate input component and the corresponding data type that will store our timezone-aware dates.

Step 2: Configuring Sanity Studio

Adding the Plugin to Your Sanity Configuration

Next, you need to add the plugin to your Sanity configuration. Open your sanity.config.ts (or .js) file and update it as follows:

Sanitysanity.config.ts
import { defineConfig } from 'sanity'import { richDate } from '@sanity/rich-date-input'export default defineConfig({  // ...  plugins: [richDate()],  // ...})

This registers the plugin with your Sanity Studio, making the richDate type available for use in your schemas.

Step 3: Creating a Schema with richDate Fields

Defining a Schema with Timezone-Aware Dates

Now, let's create or update a schema to use the richDate type. Here's an example for blog schema:

TypeScriptschemas/documents/blog.ts
import { defineField, defineType } from 'sanity'export default defineType({  name: 'blogPost',  title: 'Blog Post',  type: 'document',  fields: [    // Other fields...    defineField({      name: 'publishDate',      title: 'Publish Date',      type: 'richDate',          }),    // More fields...  ],})

Step 4: Testing the Implementation

Verifying in Sanity Studio

After implementing the richDate field, restart your Sanity Studio and create or edit a document with your new field.

You'll notice a few key differences from the standard datetime input:

  1. The date/time selector clearly shows the timezone
  2. Users can optionally choose a different timezone if needed
  3. The interface displays both the local time and UTC time
Datetimes set and rendered with rich-date-input appear in local timezones
The datetimes and timezones set in Sanity Studio can be directly rendered in the frontend app, making work across distributed timezones easier and more intuitive. The timezone the editor sets is trusted, and directly referenced in-app.

When a user selects a date, the timezone will be stored in the document. They can choose a different timezone if desired. The date displayed will be the time as it would be in that timezone, and UTC will be calculated from the timezone and local time.

Step 5: Frontend Implementation

Retrieving and Rendering Rich Date Data

When querying your Sanity dataset, the richDate fields will return the complete structure:

This will return data with the full richDate structure:

{  "title": "Annual Conference",  "publishDate": {    "_type": "richDate",    "local": "2025-05-10T21:30:00-04:00",    "utc": "2025-05-11T01:30:00Z",    "timezone": "America/New_York",    "offset": -240  }}

Now, in your frontend code, you can choose how to display this information:

JavaScriptutils.js
import { format } from 'date-fns';// Display in the original input timezone (as the editor intended)const displayLocalTime = (richDate) => {  if (!richDate) return '';  return format(new Date(richDate.local), 'MMMM d, yyyy h:mm a');};// Display in UTCconst displayUtcTime = (richDate) => {  if (!richDate) return '';  return format(new Date(richDate.utc), 'MMMM d, yyyy h:mm a') + ' UTC';};// Display in the viewer's local timezoneconst displayViewerLocalTime = (richDate) => {  if (!richDate) return '';  // Just use the UTC time and let the browser convert to the viewer's timezone  return format(new Date(richDate.utc), 'MMMM d, yyyy h:mm a (zzz)');};

For most content purposes, you'll want to display the date in the original input timezone to match the editor's intent. For example, if an article mentions "the event happening on May 10th," you want that date to remain consistent regardless of who is viewing it.

Common Issues and Troubleshooting

1. Migration from Existing Datetime Fields

Problem: You have existing content using Sanity's default datetime fields.

Solution: Create a migration script that converts these fields to richDate format:

// Migration script exampleimport client from './sanityClient';// Fetch all documents with datetime fieldsconst documents = await client.fetch(`*[_type == "yourType" && defined(publishDate)]`);// Update each documentfor (const doc of documents) {  const utcDate = doc.publishDate;    // Create richDate structure  const richDateValue = {    _type: 'richDate',    utc: utcDate,    local: utcDate, // Note: You'll lose the original local time intention    timezone: 'UTC', // Assuming UTC as default    offset: 0  };    // Update the document  await client    .patch(doc._id)    .set({publishDate: richDateValue})    .commit();    console.log(`Updated document ${doc._id}`);}

2. TypeScript Type Definitions

Problem: TypeScript doesn't recognize the richDate structure.

Solution: Define a type for richDate fields:

TypeScripttypes.ts
interface RichDate {  _type: 'richDate';  local: string;  utc: string;  timezone: string;  offset: number;}interface YourDocumentType {  _id: string;  title: string;  publishedAt: RichDate;  // Other fields...}

3. Frontend Date Formatting Issues

Problem: Dates display incorrectly or inconsistently on the frontend.

Solution: Use a reliable date library like date-fns or Luxon, and always specify which part of the richDate structure you're using:

import { parseISO, format } from 'date-fns';// Always be explicit about which datetime you're usingconst formatDate = (richDate) => format(parseISO(richDate.local), 'MMMM d, yyyy');

Real-world Use Cases

Publishing Schedules Across Timezones

For content teams working across multiple regions, richDate ensures that when an editor in Paris schedules content to go live at "9:00 AM local time," team members in New York understand that means 3:00 AM their time.

Event Calendars with Global Participants

For event platforms, storing the complete timezone context allows you to display event times appropriately for each viewer while maintaining the original intended time in the originating timezone.

Regional Content Releases

When coordinating product launches or marketing campaigns across different regions, richDate allows you to schedule region-specific timing while maintaining a clear global overview.

Advanced Techniques

Creating Custom UI Components

You can create custom UI components that leverage the rich date structure for specialized needs:

Reactcomponents/GlobalTimezoneDisplay.jsx
import React from 'react';const GlobalTimezoneDisplay = ({richDate}) => {  if (!richDate) return null;    const timezones = [    { name: 'New York', zone: 'America/New_York' },    { name: 'London', zone: 'Europe/London' },    { name: 'Tokyo', zone: 'Asia/Tokyo' },    // Add more as needed  ];    // Calculate display times for each timezone  const displayTimes = timezones.map(tz => {    // Complex timezone calculation logic here    // This is simplified    return {      name: tz.name,      formattedTime: '...' // Use a timezone library for proper conversion    };  });    return (    <div className="timezone-display">      <h4>Event Time Across Regions</h4>      <ul>        {displayTimes.map(dt => (          <li key={dt.name}>            <strong>{dt.name}:</strong> {dt.formattedTime}          </li>        ))}      </ul>    </div>  );};

Conclusion

The rich-date-input plugin for Sanity CMS solves one of the most persistent challenges in content management: correctly handling dates and times across timezones. By storing complete datetime context, it ensures that your content team's intentions are preserved regardless of where your content is viewed.

Implementing this plugin requires minimal effort but provides significant benefits:

  • Eliminated timezone confusion for content teams
  • Consistent date display across your platform
  • Flexible options for timezone handling in the frontend
  • Support for global content strategies

For teams working across multiple regions or publishing time-sensitive content, the rich-date-input plugin isn't just a nice-to-have; It's essential infrastructure that prevents mistakes and ensures smooth operations.

Next Steps

  • Consider migrating existing datetime fields to richDate
  • Create custom formatting utilities for your frontend
  • Implement timezone-aware scheduling logic
  • Review your content calendar for potential timezone issues

Additional Resources

  • Official rich-date-input GitHub Repository
  • Sanity Documentation on Date Types
  • Working with Timezones in JavaScript
  • date-fns Library for Date Manipulation

Related Articles

adding open in new tab option to links in sanity cms
Sanity Setup
Custom Features
Sanity
May 2025
How to Add 'Open in New Tab' Option for Links in Sanity CMS: A Step-by-Step Guide

Learn how to add the missing "Open in New Tab" option to Sanity CMS links. This step-by-step guide shows you how to customize link annotations and properly implement frontend rendering for better content management.

superscript and subscript decorator options showing in decorators section of Sanity block array field
Sanity Setup
Custom Features
Sanity
April 2025
How to Add Superscripts and Subscripts to Sanity CMS: A Step-by-Step Guide

In this tutorial, you will learn how to extend Sanity CMS with custom superscript and subscript decorators for block array fields.

how to implement text wrap around images in sanity cms
Sanity Setup
Rich Content & Embeds
Custom Features
Sanity
May 2025
How to Implement Text Wrap Around Images in Sanity CMS: A Step-by-Step Guide

Frustrated by Sanity's inability to wrap text around images? This tutorial shows you how to build a custom component that gives your content editors the power to float images left or right with adjustable width controls — just like Webflow.