SitecoreAI: Fix Event Timezone Issues with Next.js

While working on a calendar events feature for a healthcare website, I encountered a frustrating bug that many Sitecore developers face.
The problem looked like this:
- Author enters event time:
10:00 AM - 11:30 AM - Local development shows:
10:00 AM - 11:30 AM(correct) - Production shows:
5:00 AM - 6:30 AM(wrong)
The Sitecore Content Editor showed the correct time. The local environment displayed it correctly. But production? Completely wrong.
This guide explains why this happens and how to fix it properly in SitecoreAI (XM Cloud) with a Next.js rendering host.
š Understanding Why Times Display Differently Across Environments
The root cause involves two factors working against each other:
Factor 1: Sitecore's ServerTimeZone Setting
When Sitecore'sServerTimeZone setting is empty (the default), Sitecore uses the server's OS timezone to interpret datetime inputs.
| Environment | Server Timezone | Author Enters 10:00 AM | Stored as UTC |
|---|---|---|---|
| Local (India) | IST (UTC+5:30) | 10:00 AM IST | 04:30 UTC |
| Production (Cloud) | UTC | 10:00 AM UTC | 10:00 UTC |
10:00 AM, Sitecore interprets it as 10:00 UTC and stores T100000Z.
Factor 2: Frontend Timezone Conversion
The frontend typically converts UTC to a display timezone (like Eastern Time):
T100000Z(10:00 UTC) converts to5:00 AM ET
But for a 10:00 AM Eastern event, we need:
T150000Z(15:00 UTC) which converts to10:00 AM ET
The mismatch happens because the author intended Eastern Time, but Sitecore stored it as UTC.
š ļø The Solution: Fixing the Timezone Issue
āļø Part 1: Configure Sitecore ServerTimeZone
Add theServerTimeZone setting to ensure all datetime inputs are interpreted consistently:
- File:
App_config/Include/YourProject/SiteSettings.config
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/"> <sitecore> <settings> <setting name="ServerTimeZone" value="Eastern Standard Time"></setting> </settings> </sitecore> </configuration>
What this does:
- Author enters
10:00 AM - Sitecore interprets as 10:00 AM Eastern Time
- Sitecore converts and stores as
T150000Z(15:00 UTC) - Frontend converts back to
10:00 AM ET(correct)
Eastern Standard Time), not the IANA ID. Windows handles daylight saving (EDT/EST) automatically.
š» Part 2: Create a Shared Frontend Utility
Create a consistent date formatting utility for all event components:
// lib/utils/eventDateHelper.ts import { parseSitecoreDate } from './helpers'; // Default timezone - used when dictionary entry is missing export const DEFAULT_EVENT_TIMEZONE = 'America/New_York'; /** * Formats a date range for event details display. * Output: "Jan 27, 10:00 AM - Jan 27, 11:30 AM" or "Jan 27, 10:00 AM" if no end date */ export function formatEventDateRange( startDate?: string, endDate?: string, timeZone?: string ): string { if (!startDate) return ''; const start = parseSitecoreDate(startDate); const end = endDate ? parseSitecoreDate(endDate) : undefined; if (!start) return ''; const fmt = (d: Date) => new Intl.DateTimeFormat('en-US', { month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit', hour12: true, timeZone: timeZone || DEFAULT_EVENT_TIMEZONE, }).format(d); return end ? `${fmt(start)} - ${fmt(end)}` : fmt(start); } /** * Formats a single date for event listing display. * Output: "2027 - January 27 - 10:00 AM" */ export function formatEventDate(dateString: string, timeZone?: string): string { if (!dateString) { return ''; } try { const date = parseSitecoreDate(dateString); if (!date || isNaN(date.getTime())) { return ''; } const tz = timeZone || DEFAULT_EVENT_TIMEZONE; const year = date.toLocaleDateString('en-US', { year: 'numeric', timeZone: tz }); const month = date.toLocaleDateString('en-US', { month: 'long', timeZone: tz }); const day = date.toLocaleDateString('en-US', { day: 'numeric', timeZone: tz }); const time = date.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true, timeZone: tz, }); return `${year} - ${month} ${day} - ${time}`; } catch (error) { console.error('Error formatting date:', error); return ''; } }
Key implementation details:
- Uses
parseSitecoreDatehelper to handle Sitecore's date format - Two functions for different display contexts (details vs listing)
- Graceful handling of missing or invalid dates
Intl.DateTimeFormatfor timezone-aware formatting
šļø Making the Timezone Configurable
For flexibility, read the timezone from a Sitecore Dictionary entry:
// In your component const timeZone = sitecoreContext.dictionary?.['Event_TimeZone'] || DEFAULT_EVENT_TIMEZONE; const displayTime = formatEventDateRange(startDate, endDate, timeZone);
Configure this Dictionary entry in Sitecore:
| Key | Value |
|---|---|
Event_TimeZone
|
America/New_York
|
With this in place, content authors can change the display timezone without code changes.
ā Post-Deployment Checklist
After deploying the ServerTimeZone configuration:
1.Re-save affected events in Sitecore Content Editor (the stored UTC values need to be recalculated)
2.Verify GraphQL responses show correct UTC values:
- 10:00 AM ET should store as
T150000Z - 11:30 AM ET should store as
T163000Z
3.Verify frontend display shows the expected times
š Windows vs IANA Timezone IDs
A common source of confusion:
| System | Format | Example |
|---|---|---|
| Sitecore/Windows | Windows ID |
Eastern Standard Time
|
| JavaScript | IANA ID |
America/New_York
|
Both handle daylight saving automatically. Use the correct format for each system.
š« Why Not Just Display UTC?
An earlier approach considered displaying raw UTC values with an "ET" label. This was rejected because:
- It misleads users (showing UTC labeled as ET)
- It breaks when ServerTimeZone is eventually configured
- It doesn't solve the root cause
Always fix the data layer first, then handle display consistently.
šÆ Conclusion
Timezone issues in SitecoreAI (XM Cloud) usually stem from the mismatch between:
- Where the server runs (UTC in the cloud)
- What timezone authors intend (often Eastern or local)
- How the frontend displays dates
The fix requires both:
- Server-side: Configure
ServerTimeZoneto interpret inputs correctly - Client-side: Use
Intl.DateTimeFormatwith explicit timezone for display
This ensures consistency across all environments and eliminates the "works locally, broken in production" problem.
Happy coding!